I'm using express.js, passport with jwt strategy and of course jsonwebtoken for node.js.
So, currently, I've managed to implement a server-side logic, which enables users to login and returns the jwt token.
After that, when I do a get request with the corresponding token in the header, it correctly verifies the jwt token and display the info. The code is as follows:
var jwt = require('jsonwebtoken');
function createToken(user) {
return jwt.sign(user, 'shhhhh', {
issuer: "accounts.examplesoft.com"
});
}
var opts = {};
opts.secretOrKey = 'shhhhh';
opts.issuer = "accounts.examplesoft.com";
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
console.log(jwt_payload);
User.findById(jwt_payload.id, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
app.post('/jwt_login', function(req, res) {
User._loginJwt({
email: req.body.email,
password: req.body.password
}, function(err, user) {
if (err) res.json(err);
else res.json(createToken(user));
});
});
app.get('/jwt_test', passport.authenticate('jwt', {
session: false
}), function(req, res) {
res.json(true);
});
Now I'm trying to do a client-side page. I'm using angularjs and there are a lot of jwt libraries for angularjs or rather, client side in general. Now I have a series of questions:
First and foremost, is the server-side implement correctly (from what you can tell by the code above)?
Is it safe if I store the jwt token in localStorage (on client-side)?
Why are there so many libraries available for jwt client side? Isn't it enough to get the token and then call the requests with that token? What else could I do with that token on the client side?
Can't somebody just copy the jwt token from the localStorage and make requests as if they're logged in? Isn't that a security issue?
Thanks for your responses!
The server-side implementation looks fine, though the claims in the token could be expanded. Just always authenticate the token and you're good.
Yes. That's part of why JWT is useful. If the user alters the token, it will not match its signature and will fail authentication.
From what I recall, the client-side stuff is for when you pass data in the payload that is used on the client. You want to be able to authenticate the token on that side as well then, so your front-end doesn't do anything it shouldn't.
a. If you just have a RESTful API that validates requests with the token, you don't have to do anything with the JWT on the front-end besides sending it with requests.
Yes. That's why your token should include an expiration in its claims. Keep in mind, the only way that gets into LocalStorage is if they logged in to begin with.
See here for claims that can be in your token:
http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#rfc.section.4
Related
recently i am working hard on my website with angularjs on the Front End and Symfony 3 on the backend. I put a security layer on my backend so every request from my FE must need a valid token (using grant_type=client_credentials). I have read a looooot about the best practices about call my API Backend with angular... I normally send the token on every request that i make to the Backend, but i read that i can use the $http interceptor to send always on the header my bearer token.
So, i am a little confused that how start... because for one part:
i want to do calls to my backend to load certain data to be used on my pages to show info (using the grant_type=client_credentials) and,
i will have an user management system too. So this users must to login with user and password (again another call to my backend) but with grant_type=password...
The really big question is:
can i do the same things with one interceptor? (one for show page elements data with grant_type=client_credentials and other for the normal users?)
Tha another question is... can i make a token with this interceptor if the token has not been created yet (only for the pages info, for the users i want to refresh the token if is going to expire)?
Sorry if is a little confused... i am confused, i really read many posts, documentation and help... but i don't know where to start... I hope that you can help me...
Thanks for all.
The beauty of JWT is that they are essentially just javascript objects. You could for instance provide the user a token containing their role in the system (user, admin, support etc...) and show/hide elements accordingly.
So basically not only you grant the user access to the API, you also provide them with their type of access. Of course you should NEVER rely on client side authentication to allow restricted API's directly (verify the token on each request, check for the provided role on the server).
Here's an example in NodeJS and Angular:
//In NodeJS...
app.get('/path/to/secured/api', verifyTokenOr401, function(req, res) {
//Do stuff...
res.json({msg: 'Success');
});
function verifyTokenOr401(req, res, next) {
var authHeader = req.headers.authorization;
try {
var token = authHeader.split(' ')[1];
if(jwt.verify(token, 'myAppSecret'))
next();
} catch(e) {
res.status(401).send('Not authorized');
}
}
//Assuming you use node-jsonwebtoken package
app.post('/path/to/authentication', function (req, res) {
//Verify authentication...
User.findOne({username: req.body.username}).then(function(user) {
//VerifyPassword
if(!user)
return res.status(401).send('No such user ' + req.body.username);
if(!user.verifyPassword(req.body.password))
return res.status(401).send('Wrong password for user ' + user.username);
//Provide the user with the access token
var token = jwt.sign({ subject: user.id, role: user.role }, 'myAppSecret');
res.setHeader('Authorization', 'Bearer ' + token.toString());
res.json(user);
})
.catch(function (e) { res.status(500).json(e); });
});
//In angular...
.factory('jwtInterceptor', function() {
return {
request: function(config){
var authHeader = config.headers('authorization');
//Attach header if not present
if(!authHeader)
config.headers.authorization = 'Bearer ' + localStorage.get('myAppToken');
return config;
},
response: function(response){
//Look for token in the header if you get a response and save it
var authHeader = response.headers('authorization');
if(authHeader){
try { localStorage.myAppToken = authHeader.split(' ')[1]; } catch(e) {}
}
return response;
}
}
});
Notable mention: check out auth0's repos for NodeJS and Angular. Both are awesome.
You can create a service which when loaded by angular make a get call for authorization token and set in header. Through this you do not need to set token at every Ajax call. You can do it like this:
app.service("MyService", ["$http", function($http) {
initialize();
function initialize() {
getAuthorizationToken().then(function(response) {
$http.defaults.headers.common.Authorization = 'Bearer some_auth_code_here';
});
}
function getAuthorizationToken() {
// Get call for token
}
}]);
I need to know that if my authentication and session management method is right.
I am using session management as when I receive successful auth. from node server. I store user data(without any trace of pass.) in $window.sessionStorage and if user marked rememberMe(checkbox), store data in $window.localStorage too.
Through this I am able to get data in different controllers. Though I read somewhere about session implementation at server(nodeJs) side is also possible. But I am not sure about how to use session along with JSONToken Authentication.
I was using
https://jasonwatmore.com/post/2015/12/09/MEAN-Stack-User-Registration-and-Login-Example.aspx
as a learning example but I could not understand it.
/app/app.js
Why is it in the run() method ?
// add JWT token as default auth header
$http.defaults.headers.common['Authorization'] = 'Bearer ' + $window.jwtToken;
and what is this:
// manually bootstrap angular after the JWT token is retrieved from the server
$(function () {
// get JWT token from server
$.get('/app/token', function (token) {
window.jwtToken = token;
angular.bootstrap(document, ['app']);
});
});
/controllers/app.controller.js
// use session auth to secure the angular app files
router.use('/', function (req, res, next) {
if (req.path !== '/login' && !req.session.token) {
return res.redirect('/login?returnUrl=' + encodeURIComponent('/app' + req.path));
}
next();
});
// make JWT token available to angular app
router.get('/token', function (req, res) {
res.send(req.session.token);
});
// serve angular app files from the '/app' route
router.use('/', express.static('app'));
So using a session server-side with JWT kind of defeats the purpose of using JWT. JWT's are awesome in a number of ways, but one of the ways they are great, is regardless which server intercepts a request, they can verify the user.
If you put it in a session, you have to make sure the client keeps going to the same server as the session is saved in memory on that machine. There are plenty of ways around that, but again it kind of defeats the purpose of a JSON web token.
What I did for my authentication with angular/node/JWT was just passed the JWT back in the header every time, and with my middleware intercepted it with:
req.header.whatever_my_tokens_name_is
The code below set the $http to send on every request the JWT Token to the server.
// add JWT token as default auth header
$http.defaults.headers.common['Authorization'] = 'Bearer ' + $window.jwtToken;
The code below get the token from '/app/token' and store it in LocalStorage. After that, it starts the angular.
// manually bootstrap angular after the JWT token is retrieved from the server
$(function () {
// get JWT token from server
$.get('/app/token', function (token) {
window.jwtToken = token;
angular.bootstrap(document, ['app']);
});
});
Here this is a middleware that check if there are no token stored in req.session.token and requested url is not '/login'. If so, send a redirect to '/login'.
// use session auth to secure the angular app files
router.use('/', function (req, res, next) {
if (req.path !== '/login' && !req.session.token) {
return res.redirect('/login?returnUrl=' + encodeURIComponent('/app' + req.path));
}
next();
});
Finally here, this is a endpoint to the client request the '/token' again from the server.
// make JWT token available to angular app
router.get('/token', function (req, res) {
res.send(req.session.token);
});
Anyway, check the #morgan-g response regarless session-side and JWT.
I hope this helps.
I'm working on a MEAN application with authentication using JSON web tokens. basically on every request, I am checking to see if user has a valid token. if so they can go through to the route, otherwise they are returned to login page.
I want to make certain routes /admin/etc... only accessible to logged in users who are also admin. I have set up an isAdmin flag in mongo. I am new to nodejs and wondering what is the best way to check this. Do I do it on the angular side in routes? Or can I somehow create permission-based tokens on authentication? For reference, I am following the code from the MEAN Machine book, in particular here -
https://github.com/scotch-io/mean-machine-code/tree/master/17-user-crm
First, authorization decisions must be done on the server side. Doing it on the client side in Angular.js as you suggested is also a good idea, but this is only for the purpose of improving the user's experience, for example not showing the user a link to something they don't have access to.
With JWTs, you can embed claims about the user inside the token, like this:
var jwt = require('jsonwebtoken');
var token = jwt.sign({ role: 'admin' }, 'your_secret');
To map permissions to express routes, you can use connect-roles to build clean and readable authorization middleware functions. Suppose for example your JWT is sent in the HTTP header and you have the following (naive) authorization middleware:
// Naive authentication middleware, just for demonstration
// Assumes you're issuing JWTs somehow and the client is including them in headers
// Like this: Authorization: JWT {token}
app.use(function(req, res, next) {
var token = req.headers.authorization.replace(/^JWT /, '');
jwt.verify(token, 'your_secret', function(err, decoded) {
if(err) {
next(err);
} else {
req.user = decoded;
next();
}
});
})
With that, you can enforce your authorization policy on routes, like this:
var ConnectRoles = require('connect-roles');
var user = new ConnectRoles();
user.use('admin', function(req) {
return req.user && req.user.role === 'admin';
})
app.get('/admin', user.is('admin'), function(req, res, next) {
res.end();
})
Note that there are much better options for issuing & validating JWTs, like express-jwt, or using passport in conjunction with passort-jwt
Currently, I am working on an Angularjs website that uses JWT (json web tokens) to verify if someone is logged in or not. Now I am wanting to add some "one-click" login methods using passport / oauth but finding it difficult on how I can pass the login details back to angularjs.
When I login with email / password, I generate a token, and pass it back to angular:
var token = jwt.sign({ _id: user._id.toString() }, JWT_SECRET);
return res.json({
userID: user._id.toString(),
username: user.username,
isAdmin: user.isAdmin,
token: token
});
And the token is used to verify login. When going through oauth, you must leave the site, and then come back, so I can't just return some token from ajax the same way.
I thought it might work to add it to my verify call (when someone refreshes the website, I run a check to see if they were logged in already). This checks for the token and returns the proper user data. Is there a way to check if passport is logged in already here as well? My verify code looks like this (JWT automagically sets the token info to req.user):
Schemas.User.findOne({ _id: req.user._id }, function (err, user) {
if (err || !user) { return res.send(401); }
return res.json({
userID: user._id.toString(),
username: user.username,
isAdmin: user.isAdmin
});
});
So I had a couple of issues. I wasn't using passport properly (I had forgotten to add the serialize / deserialize). Then on my auth callback I set a cookie with a JWT in it (I also didn't realize that passport also set req.user automagically), and checked for the cookie in angular. If the cookie exists, I set it to local storage, and delete the cookie. Works like a charm!
We are transitioning from an API using cookies for state (ExpressJS sessions) to a stateless (token) API.
We use a single PassportJS authentication strategy (GoogleStrategy). When the OAuth flow completes, Google calls back to a backend route with an access token.
Previously, we would set a cookie at this point using req.session and redirect the user to our dashboard.
With a token API, we generate a token based on the email (acting as a username) and access token (acting as password) when Google calls back to the backend route.
How do we pass this token to the front-end (Angularjs) so that it can make authenticated requests?
Do we need to switch to Google's front-end OAuth APIs?
One way to pass the token to a client-side web application is to put the signed JSON web token into the cookie , which your client-side app can access and use (either appending the token to every GET request or using it in your web socket authentication). Just to be clear, you're no longer using cookies as a reference for server-recorded state, instead you're are simply using them as a storage mechanism that both client and server can access, where you can store your encoded token.
Here's a great example from the generator-angular-fullstack:
In the router, receiving the callback from the google authentication:
.get('/callback', passport.authenticate('google', {
failureRedirect: '/signup',
session: false
}), auth.setTokenCookie);
And the setTokenCookie function:
function setTokenCookie(req, res) {
if (!req.user) return res.json(404, { message: 'Something went wrong, please try again.'});
var token = signToken(req.user._id, req.user.role);
res.cookie('token', JSON.stringify(token));
res.redirect('/');
}
And the signToken function:
function signToken(id) {
return jwt.sign({ _id: id }, config.secrets.session, { expiresInMinutes: 60*5 });
}