问题
As shown in the illustration below, I have a standalone API Project running on a server with a port say 3001
, and I have a Web App running on a server with port say 3002
.
API on port 3001, has all the API routes required for the Web App (& mobile apps) to fetch and put data, including Authentication API (Using passport-local
and passport-jwt
). In the API side of the project, I have also handled user role authorization, and every routes has list of roles who can access the APIs.
Example Route
todoRoutes.get('/',
requireAuth,
AuthController.roleAuth(['user','editor','admin']),
TodoController.getTodos);
Role Authorization API Method in port 3001
exports.roleAuth = function(roles){
return function(req, res, next){
var user = req.user;
User.findById(user._id, function(err, foundUser){
if(err){
res.status(422).json({error: 'No user found.'});
return next(err);
}
if(roles.indexOf(foundUser.role) > -1){
return next();
}
res.status(401).json({error: 'You are not authorized to view this content'});
return next('Unauthorized');
});
}
}
Response json after login successfully is like this
{
"token": "JWT eyJhbGci...",
"user": {
"_id": "5986b81d940bab06ddc79b34",
"email": "myemail@gmail.com",
"role": "admin"
}
}
Now in Web App, I want to use same role authorization and authentication (login), but you see, Web App is not connected to database, for me to make queries like check if the user in session is valid and has the role as in the response it got after login successfully.
Summary Here are bullet points of what I was looking for in this question:
- Login on Client-Side Web Application, via Remote API on port 3001 (achieved)
- Get User Token and other information (response shown above) (achieved)
- Ensure user is authenticated on Client-Side Web App and also remember role of the user loggedin, to use these information for authorization of every routes on Client-side app. In client-side app I have few pages with forms to send data to Server-Side API on port 3002, these pages are used by two different user with roles editor and admin.
TIA
回答1:
Your authenticating API should return a JWT with guaranteed information (the role) embedded. Further, the token should be made using a secret known to your view APIs.
For example, using npm module jsonwebtoken, sign it like so:
token = jwt.sign( {
exp: Math.floor( Date.now() / 1000 ) + ( 60 * 60 ), // 1 hour
i: user._id,
role: user.role
}, "my-secret" );
Then, on your view API, use passport-jwt, which both verifies the token and provides you a payload that matches the original object you signed. Use the payload as the user object:
passport.use( new JwtStrategy( {
secretOrKey: "my-secret"
}, ( payload, callback ) => callback( null, payload ) ) );
const authenticate = () =>
passport.authenticate( "jwt", { session: false, failWithError: true } )
At this stage, your user is at minimum authenticated. If you want to restrict a view to certain roles, you would add a second middleware:
const assertRole = ( ...roles ) => ( req, res, next ) =>
req.user && roles.includes( req.user.role ) ? next() : res.sendStatus( 403 ) );
todoRoutes.get("/admin/view1", authenticate, assertRole( "user", "editor", "admin" ), TodoController.getTodos );
If your view is going to be need more information about the user, the authentication API will need to provide that, either in the JWT (and thus guaranteed) or outside (not guaranteed, but results in smaller tokens).
回答2:
exports.roleAuth = function(roles){
return function(req, res, next){
var user = req.user;
User.findById(user._id, function(err, foundUser){
if(err){
res.render('index.html');
}
if(roles.indexOf(foundUser.role) > -1){
res.render('another.html');
}
res.render('another2.html');
});
}
}
回答3:
There are two ways that I see this can be done, though not perfectly.
One is to use the primary api endpoint of a particular view. Normally each view has a primary endpoint, and if that endpoint returns an Unauthorized
/ Forbidden
status, you shouldn't render the view. But this solution has problems and not always there's a primary endpoint matching a view.
Other option is to namespace the routes with roles, like admin/dashboard
and users/dashboard
, and the user should have a field describing their role, eg. user.role
. And before rendering the views, check for the respective role in url and in user object returned by the API.
The second option is preferred and this is what I generally use.
Hope it helps.
回答4:
Doesn't the beauty of JWT
s solve this issue already, or rather can solve your issue with the correct implementation?
All that is required is for you to
- initialise the
passport-jwt
module in your WebApp in the same way you initialise it in your API, namely thesecretOrKey
parameter. - inspect the
JWT
, specifically therole
property and allow/reject based on that
来源:https://stackoverflow.com/questions/45560910/how-to-authenticate-authorize-a-client-side-web-app-using-remote-nodejs-api-th