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
}
}]);
Related
I get follow scenario which is working now:
MVC controller using System.Web.Mvc.AuthorizeAttribute to authenticate user is authenticated or not, it will be using cookie.
API controller using System.Web.Http.AuthorizeAttribute to authorise with bearer token.
I do also have angular http interceptor that verify and get bearer token for API purpose that can use among all angular $http request. But I am confusing how to achieve both after user has login?
This is current workflow
User click login, angular verify and store bears token in local storage.
After complete, manually trigger MVC controller so that it will get cookie for MVC authenticate.
This seem to me really double job, or I should focusing on using one AuthorizeAttribute?
You need you use Authorize key to give permission to those functions where authorization is needed. And those functions can only be accessed when authorization token is generated and passed with http request.
module.service('tokenservice', function ($http) {
this.get = function () {
var accesstoken = sessionStorage.getItem('accessToken');
var logged_in = localStorage.getItem('logged_in').toString().trim() === 'false' ? false : true;
var authHeaders = {};
if (accesstoken && logged_in) {
authHeaders.Authorization = 'Bearer ' + accesstoken;
}
return authHeaders;
};
});
module.controller('yourControllerName', function ( $http, tokenservice) {
$http({
method: "POST",
url: '/Controller/MyFucntion',
headers: tokenservice.get(),
});
});
This will help you to get generated token in user login. After that You need to work with your controller
[Authorize]
public JsonResult MyFucntion()
{
//Your logic and calculation
//return
}
Hope that will help
I can't find a way to fun a function once the app bootstraps it self. I am using bearer token authentication and if the user has logged in before the token is stored in local storage:
localStorage.setItem('access_token', response.body.access_token);
Then I am using interceptors to add a constant header to every request
authorizationInterceptor: {
enable: () => {
if(auth.loggedIn())
{
fetchIntercept.register({
request: function (url, config) {
config.headers.Authorization = 'Bearer ' + auth.getToken();
return [url, config];
}
});
}
}
}
The problem is I am running the authorizationInterceptor.enable() on login request, but if the user already has logged in and hits refresh I need to run the interceptor again. So is there a way to run a function once only after app bootstrapping is finished? I do not want to add a check in every fetch request, seems very redundant.
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 have been fumbling around with different implementations and ideas to get this to work, but I feel like I am not doing this as DRY or smart as I could be. I've been following this "tutorial" Angular Auth
So, I have a fully functional laravel (4.2) back end set up with some resource routes protected by the oauth filter. I am using the password grant and everything is working just fine there. I've got log in/out routes also set up and am able to sign in to my Ionic app and obtain and access_token and refresh_token from laravel just fine. Obtaining new access_tokens using the refesh_token works just fine as well. BUT, I am having some issues trying to figure out how to correctly handle the following things in Ionic:
Make sure the access_token hasn't expired before the user hits an Ionic state which will consume a resource from my back end.
Handle the case where the user's access_token & refresh token have both expired requiring them to log back in to the laravel back end in order to obtain a new pair of access & refresh tokens. I only have the user "log in" when they need to obtain a new access_token & refresh token (or they are first registering) as this route, oauth/access_token, requires the params {username, password}.
What I Tried
In the article I mentioned earlier, he sets up a rootScope watcher in the run module which watches for the statechangestart event like so.
$rootScope.$on('$stateChangeStart', function (event, next) {
var authorizedRoles = next.data.authorizedRoles;
if (!AuthService.isAuthorized(authorizedRoles)) {
event.preventDefault();
if (AuthService.isAuthenticated()) {
// user is not allowed
$rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
} else {
// user is not logged in
$rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
}
}
});
I am not using roles so when I implemented this I just had something like this
$rootScope.$on('$stateChangeStart', function(event, next) {
if (next.url != "/login") {
AuthService.isAuthenticated().then(function() {
console.log('you are already authed an logged in and trying to access: ' + next.url);
}, function() {
event.preventDefault();
console.log('YOU DO NOT HAVE A VALID ACCESS TOKEN');
$location.path('/app/login');
});
}
});
isAuthenticated() just hits a route inside my oauth filter so if it throws back an error (401 for example), I know that the access_token is bad. I then have a private method also inside my AuthService service that tries to get a new access_token using the users stored refresh_token
function useRefreshToken() {
console.log('Using refresh token to get new token:');
var deferred = $q.defer();
$http({
method: 'POST',
url: base_url.dev.url + 'oauth/access_token',
data: $.param({
grant_type: 'refresh_token',
client_id: API.client_id,
client_secret: API.client_secret,
refresh_token: $localStorage.session.refresh_token
}),
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).success(function(data) {
console.log('refresh token worked!');
$localStorage.session.access_token = data.access_token;
$localStorage.session.refresh_token = data.refresh_token;
deferred.resolve();
}).error(function(error) {
console.log('refresh token failed');
CurrentUserService.setLogged(false);
console.log(JSON.stringify(error));
deferred.reject(error);
});
return deferred.promise;
};
If the above method returns back a rejected promise I just assume (which may be a good idea or not??) that the refresh token has expired and thus the user needs to log back in and retrieve a new access & refresh token pair from my laravel oauth/access_token route.
So the above methods have been working fine on their own, in that I am able to check if the users access_token is valid and if not retrieve a new access_token just fine using the users refresh_token.
Here's my isAuthenticated method in case you wanted to see that as well. It's a public method inside of my AuthService service.
isAuthenticated: function() {
console.log('Checking if token is still valid.');
var deferred = $q.defer();
$http.get(base_url.dev.url + 'valid-token', {
params: {
access_token: $localStorage.session.access_token
}
}).success(function(data) {
console.log('Token is still valid.');
CurrentUserService.setLogged(true);
deferred.resolve();
}).error(function(error) {
console.log(JSON.stringify(error));
useRefreshToken().then(function() {
deferred.resolve();
}, function(error) {
deferred.reject(error);
});
});
return deferred.promise;
}
The big problem I was running into is that because the AuthService.isAuthenticated() method runs async, the state the app was changing to, say PHOTOS, would be hit before isAuthenticated returns and if we have Case: 1 mentioned at the beginning of my post, the PHOTOS state will try to use an invalid access_token to try and consume a resource on my back end BEFORE the isAuthenticated method is able to get a new access_token using the refresh_token.
Now I was able to avoid the above issue by using a resolve on EVERY state which handled using the isAuthenticated method to check the access_token and get a new one if need be BEFORE consuming a resource. BUT that felt horribly unDRY. I apologize for the length of this post but I wanted to make sure you guys knew everything that was going on and what I was trying to accomplish.
I appreciate any feedback, criticism and instruction! Thanks guys.
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