React.js run function on bootstraping - reactjs

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.

Related

Custom AngularJS $http interceptor case

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
}
}]);

AngularJS, $http and interceptor - every request received twice in nodejs

I'm trying to use AngularJS' $http interceptor for adding a token on every API call.
This is my code (http://pastebin.com/x1EMGcVE):
.factory('authInterceptor', function(authorization, API) {
return {
request: function(config) {
var token = authorization.getToken();
if (config.url.indexOf(API) === 0 && token) {
config.headers.Authorization = token;
}
return config;
}
}
})
On my nodes backend I'm receiving the call with express:
app.route('/API-CALL')
.get(function(req, res) {
...
My problem is that every API request is received twice in nodejs, one request without the token added in Authorization and one with the token added.
Is this an issue with the interceptor? Outputting the config from the interceptor seems to show no issues and only the correct number of requests with the token added.
If I remove the:
$httpProvider.interceptors.push('authInterceptor');
calls are only made once, but of course without the token added.

Session and Login User data with Node and AngularJS

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.

Ionic/Laravel App Client Side Auth Management

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.

implementing refresh-tokens with angular and express-jwt

I want to implement the Sliding expiration concept with json web tokens using angular, nodejs and express-jwt. I'm a little confused on how to do this, and am struggling to find any example of refresh tokens or and other material relating to sessions with these technologies/frameworks.
A few options I was thinking of were
Generating a new token with each request after the initial login
Keeping track of issued token on the server side along
But I'm honestly not sure, please help
I managed to implement this scenario.
What I've done...
On the server:
-Enable an API endpoint for signin. This endpoint will respond with the Json Web Token in the header. The client side has to catch it (with $http interceptors) and save it (I use local storage). The client will also manage the refreshed tokens sent by the server.
-On every request to the server configure a middleware in express to validate the token. At first I tried express-jwt module but jsonwebtoken was the right one for me.
For specific routes you may want to disable the middleware. In this case signin and signout.
var jwtCheck = auth.verifyJWT;
jwtCheck.unless = unless;
app.use('/api', jwtCheck.unless({path: [
'/api/auth/signin',
'/api/auth/signout'
]}));
-The middleware verifyJWT always responds with a token in the header. If the token needs to be refreshed a refreshed function is called.
jwtLib is my own library where the code lives to create, refresh and fetch jwt tokens.
function(req, res, next) {
var newToken,
token = jwtLib.fetch(req.headers);
if(token) {
jwt.verify(token, config.jwt.secret, {
secret: config.jwt.secret
}, function(err, decoded) {
if(err) {
return res.status(401).send({
message: 'User token is not valid'
});
}
//Refresh: If the token needs to be refreshed gets the new refreshed token
newToken = jwtLib.refreshToken(decoded);
if(newToken) {
// Set the JWT refreshed token in http header
res.set('Authorization', 'Bearer ' + newToken);
next();
} else {
res.set('Authorization', 'Bearer ' + token);
next();
}
});
} else {
return res.status(401).send({
message: 'User token is not present'
});
}
};
-The refresh function (jwtLib). As argument needs a decoded token, see above that jsonwebtoken resolve a decoded when call to jwt.verify().
If you create during signin a token with an expiration of 4 hours and have a refresh expiration of 1 h (1 * 60 * 60 = 3600 secs) that means that the token will be refreshed if the user has been inactive for 3 hours or more, but not for more than 4 hours, because the verify process would fail in this case (1 hour window of refreshing). This avoids generating a new token on each request, only if the token will expire in this time window.
module.exports.refreshToken = function(decoded) {
var token_exp,
now,
newToken;
token_exp = decoded.exp;
now = moment().unix().valueOf();
if((token_exp - now) < config.jwt.TOKEN_REFRESH_EXPIRATION) {
newToken = this.createToken(decoded.user);
if(newToken) {
return newToken;
}
} else {
return null;
}
};
On the client (Angularjs):
-Enable a client side for login. This calls the server endpoint. I use Http Basic Authentication encoded with base64.
You can use base64 angular module to encode the email:password
Note that on success I do not store the token on the localStorage or Cookie. This will be managed by the http Interceptor.
//Base64 encode Basic Authorization (email:password)
$http.defaults.headers.common.Authorization = 'Basic ' + base64.encode(credentials.email + ':' + credentials.password);
return $http.post('/api/auth/signin', {skipAuthorization: true});
-Configure the http interceptors to send the token to the server on every request and store the token on the response. If a refreshed token is received this one must be stored.
// Config HTTP Interceptors
angular.module('auth').config(['$httpProvider',
function($httpProvider) {
// Set the httpProvider interceptor
$httpProvider.interceptors.push(['$q', '$location', 'localStorageService', 'jwtHelper', '$injector',
function($q, $location, localStorageService, jwtHelper, $injector) {
return {
request: function(config) {
var token = localStorageService.get('authToken');
config.headers = config.headers || {};
if (token && !jwtHelper.isTokenExpired(token)) {
config.headers.Authorization = 'Bearer ' + token;
}
return config;
},
requestError: function(rejection) {
return $q.reject(rejection);
},
response: function(response) {
//JWT Token: If the token is a valid JWT token, new or refreshed, save it in the localStorage
var Authentication = $injector.get('Authentication'),
storagedToken = localStorageService.get('authToken'),
receivedToken = response.headers('Authorization');
if(receivedToken) {
receivedToken = Authentication.fetchJwt(receivedToken);
}
if(receivedToken && !jwtHelper.isTokenExpired(receivedToken) && (storagedToken !== receivedToken)) {
//Save Auth token to local storage
localStorageService.set('authToken', receivedToken);
}
return response;
},
responseError: function(rejection) {
var Authentication = $injector.get('Authentication');
switch (rejection.status) {
case 401:
// Deauthenticate the global user
Authentication.signout();
break;
case 403:
// Add unauthorized behaviour
break;
}
return $q.reject(rejection);
}
};
}
]);
}
]);

Resources