springboot+angularjs JWT auth - angularjs

I have a problem with generate jwt token. I'm using Springboot and AngularJs. I spent a lot of time looking for any solution, but found nothing. I would also ask (if it will works), how can I keep token in application. Should I use $localStorage or $Window or something else?
#RequestMapping(value = "/user-login")
public ResponseEntity<Map<String, Object>> login(#RequestParam String email, #RequestParam String password) throws IOException {
String token = null;
User appUser = userRepository.findByEmail(email);
Map<String, Object> tokenMap = new HashMap<String, Object>();
if (appUser != null && appUser.getPassword().equals(password)) {
token = Jwts.builder().setSubject(email).claim("roles", appUser.getRoles()).setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, "secretkey").compact();
tokenMap.put("token", token);
tokenMap.put("user", appUser);
return new ResponseEntity<Map<String, Object>>(tokenMap, HttpStatus.OK);
} else {
tokenMap.put("token", null);
return new ResponseEntity<Map<String, Object>>(tokenMap, HttpStatus.UNAUTHORIZED);
}
}
angular controller
angular.module('app')
.controller('LoginController', function ($http, AuthService, $rootScope, $scope, $location){
var vm = this;
$scope.login = function () {
// requesting the token by usename and passoword
$http({
url: 'user-login',
method: "POST",
params: {
email: $scope.email,
password: $scope.password
}
})
.then(function success(res){
$scope.password = null;
// checking if the token is available in the response
if (res.token) {
vm.message = '';
// setting the Authorization Bearer token with JWT token
$http.defaults.headers.common['Authorization'] = 'Bearer ' + res.token;
// setting the user in AuthService
AuthService.user = res.user;
$rootScope.authenticated = true;
// going to the home page
$location.path('/home');
}
else {
// if the token is not present in the response then the
// authentication was not successful. Setting the error message.
vm.message = 'Authetication Failed !';
}
},function error (error) {
vm.message = 'Authetication Failed !';
});
}
});
I have receiving always null token and 401 status.
The code seems correct. Any ideas where I'm making mistake?

Related

AngularJS: Refresh expired JWT token on 401 response

I'm facing issue on refreshing expired JWT token based on 401 (unauthorized) header response. What i want is when user get 401 (header) response, than a new (refresh) JWT should generated by calling specific service (api).
I'm sending XSRF-TOKEN & access_token (JWT) in header response and these are working fine. I even also can get refresh (expired) token by calling api manually. But can't get it worked with 401 (header) response.
I've a factory that take care of this promise and intercepts header requests. My (factory) code looks like this.
angular.module('myApp').factory('httpRequestInterceptor', httpRequestInterceptor);
function httpRequestInterceptor($cookies, $rootScope, $q, $location, $injector) {
var replays = [];
var refreshTokenPromise;
var factory = {
request: request,
responseError: responseError
};
return factory;
//////////
function requestTodoWhenDone() {
var token = store.get('token');
return $http({
method: 'POST',
url: ApiEndpoint.url,
params: {
grant_type: 'refresh',
id_token: $cookies.get('access_token')
}
})
.success(function(response) {
// Set the refreshed token.
$cookies.put('access_token', response.data.access_token);
})
.then(function(){
// Attempt to retry the request if request config is passed.
if( !angular.isUndefined(requestTodoWhenDone) && requestTodoWhenDone.length > 0 ) {
// Set the new token for the authorization header.
requestTodoWhenDone.headers = {
'Authorization': 'Bearer ' + $cookies.get('access_token')
};
// Run the request again.
return $http(requestTodoWhenDone);
}
});
}
//////////
// Add authorization token to headers
function request(config) {
config.headers = config.headers || {};
if ($cookies.get('access_token')) {
config.headers.Authorization = 'Bearer ' + $cookies.get('access_token');
}
return config;
}
// Intercept 401s and redirect you to login
function responseError(response, requestTodoWhenDone) {
if (response.status === 401 && $cookies.get('access_token')) {
return checkAuthorization(response);
}
return $q.reject(response);
/////////
function checkAuthorization(res) {
return $q(function(resolve, reject) {
var replay = {
success: function(){
$injector.get('$http')(res.config).then(resolve, reject);
},
cancel: function(){
reject(res);
}
};
replays.push(replay);
console.log(replays);
if (!refreshTokenPromise) {
refreshTokenPromise = $injector.get('requestTodoWhenDone') // REFRESH TOKEN HERE
.refreshToken()
.then(clearRefreshTokenPromise)
.then(replayRequests)
.catch(cancelRequestsAndRedirect);
}
});
////////////
function clearRefreshTokenPromise(auth) {
refreshTokenPromise = null;
return auth;
}
function replayRequests(auth) {
replays.forEach(function(replay) {
replay.success();
});
replays.length = 0;
return auth;
}
function cancelRequestsAndRedirect() {
refreshTokenPromise = null;
replays.forEach(function(replay) {
replay.cancel();
});
replays.length = 0;
$cookies.remove('token');
var $state = $injector.get('$state');
// SET YOUR LOGIN PAGE
$location.path('/login');
}
}
}
}
Based on above code I'm getting following error in console when token expires (401 response).
Console Error
Error: "[$injector:unpr] Unknown provider: requestTodoWhenDoneProvider <- requestTodoWhenDone
Any help on this would be highly appreciable.
Thanks.
Ok i ended up with different way that solves the issue. But i still can't be able to redirect user to login page when my token inactive time is also expires (this happens after jwt expires).
Here is the code.
authInterceptor.service.js
angular.module('someApp').factory('AuthorizationTokenService', AuthorizationTokenService);
AuthorizationTokenService.$inject = ['$q', '$injector', '$cookies'];
function AuthorizationTokenService($q, $injector, $cookies) {
// Local storage for token
var tokenVM = {
accessToken: null
};
// Subscribed listeners which will get notified when new Access Token is available
var subscribers = [];
// Promise for getting new Access Token from backend
var deferedRefreshAccessToken = null;
var service = {
getLocalAccessToken: getLocalAccessToken,
refreshAccessToken: refreshAccessToken,
isAccessTokenExpired: isAccessTokenExpired,
subscribe: subscribe
};
return service;
////////////////////////////////////
// Get the new Access Token from backend
function refreshAccessToken() {
// If already waiting for the Promise, return it.
if( deferedRefreshAccessToken ) {
return deferedRefreshAccessToken.promise
} else {
deferedRefreshAccessToken = $q.defer();
// Get $http service with $injector to avoid circular dependency
var http = $injector.get('$http');
http({
method: 'POST',
url: 'api_url',
params: {
grant_type: 'refresh',
id_token: $cookies.get('access_token')
}
})
.then(function mySucces(response) {
var data = response.data;
if( data ){
// Save new Access Token
$cookies.put('access_token', data.access_token);
if( $cookies.get('access_token') ) {
// Resolve Promise
deferedRefreshAccessToken.resolve(data.access_token);
// Notify all subscribers
notifySubscribersNewAccessToken(data.access_token);
deferedRefreshAccessToken = null;
}
}
}, function myError(error) {
deferedRefreshAccessToken.reject(error);
deferedRefreshAccessToken = null;
});
return deferedRefreshAccessToken.promise;
}
}
function getLocalAccessToken() {
// get accesstoken from storage - $cookies
if ( $cookies.get('access_token') ) {
var access_token = $cookies.get('access_token')
return access_token;
}
}
function isAccessTokenExpired() {
// Check if expiresAt is older then current Date
}
function saveToken(accessToken) {
// get accesstoken from storage - $cookies
var access_token = $cookies.put('access_token');
console.log('access_token ' + access_token);
return access_token;
}
// This function will call all listeners (callbacks) and notify them that new access token is available
// This is used to notify the web socket that new access token is available
function notifySubscribersNewAccessToken(accessToken) {
angular.forEach(subscribers, function(subscriber) {
subscriber(accessToken);
});
}
// Subscribe to this service. Be notifyed when access token is renewed
function subscribe(callback) {
subscribers.push(callback);
}
}
Than in config (app.js) I've following code which intercepts appropriate header(s) and refresh (request) api on 401 response.
Here is the config code
config.$inject = ['$stateProvider', '$urlRouterProvider', '$httpProvider'];
function config($stateProvider, $urlRouterProvider, $httpProvider) {
// Push httpRequestInterceptor
// $httpProvider.interceptors.push('httpRequestInterceptor');
//Intercept all http requests
$httpProvider.interceptors.push(['$injector', '$q', "AuthorizationTokenService", "$cookies", function ($injector, $q, AuthorizationTokenService, $cookies) {
var cachedRequest = null;
return {
request: function (config) {
//If request if for API attach Authorization header with Access Token
if (config.url.indexOf("api") != -1) {
// var accessToken = AuthorizationTokenService.getLocalAccessToken();
console.log('cookie ' + $cookies.get('access_token'));
config.headers.Authorization = 'Bearer ' + $cookies.get('access_token');
}
return config;
},
responseError: function (response) {
switch (response.status) {
// Detect if reponse error is 401 (Unauthorized)
case 401:
// Cache this request
var deferred = $q.defer();
if(!cachedRequest) {
// Cache request for renewing Access Token and wait for Promise
cachedRequest = AuthorizationTokenService.refreshAccessToken();
}
// When Promise is resolved, new Access Token is returend
cachedRequest.then(function(accessToken) {
cachedRequest = null;
if (accessToken) {
// Resend this request when Access Token is renewed
$injector.get("$http")(response.config).then(function(resp) {
// Resolve this request (successfully this time)
deferred.resolve(resp);
},function(resp) {
deferred.reject();
console.log('success: refresh token has expired');
});
} else {
// If any error occurs reject the Promise
console.log('error: refresh token has expired');
deferred.reject();
}
}, function(response) {
// If any error occurs reject the Promise
cachedRequest = null;
deferred.reject();
return;
});
return deferred.promise;
}
// If any error occurs reject the Promise
return $q.reject(response);
}
};
}]);
}
The code is working fine on 401 (response) case which happens when JWT expires. But its not redirecting me to login page (In this case I've added console in promise request in config instead of redirection code)
Please help on this, thanks...

Handling 401 (Unauthorized) angularjs Authentication using JWT

I have an angularJS web application which uses the web api and jwt. I followed tutorial on the internet here > angularjs-jwt-auth everything is working fine when I login using credentials from my own api,returns token on console as it should.
But the issue comes when I try to register a new user, nothing happens and console is throwing me an error Failed to load resource: the server responded with a status of 401 (Unauthorized). When I use api from tutorial is working fine so am little bit lost, please help!!
My code
(function () {
function authInterceptor(API, auth) {
return {
// automatically attach Authorization header
request: function (config) {
config.headers = config.headers || {};
var token = auth.getToken();
if (config.url.indexOf(API) === 0 && token) {
config.headers.Authorization = 'Bearer ' + token;
}
return config;
},
response: function (res) {
if (res.config.url.indexOf(API) === 0 && res.data.token) {
auth.saveToken(res.data.token);
}
return res;
},
}
}
// Services
function authService($window) {
var srvc = this;
srvc.parseJwt = function (token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace('-', '+').replace('_', '/');
return JSON.parse($window.atob(base64));
};
srvc.saveToken = function (token) {
$window.localStorage['jwtToken'] = token
};
srvc.logout = function (token) {
$window.localStorage.removeItem('jwtToken');
};
srvc.getToken = function () {
return $window.localStorage['jwtToken'];
};
srvc.saveUsername = function (username) {
$window.localStorage['username'] = username;
}
srvc.getUsername = function () {
return $window.localStorage['username'];
}
srvc.isAuthed = function () {
var token = srvc.getToken();
if (token) {
var params = srvc.parseJwt(token);
return Math.round(new Date().getTime() / 1000) <= params.exp;
} else {
return false;
}
}
}
function userService($http, API, auth) {
var srvc = this;
srvc.register = function (first_name, last_name, email, password, role, gender, phone_number) {
return $http.post(API + '/api/v1/users/', { // <-- Registration link here
first_name: first_name,
last_name: last_name,
email: email,
password: password,
role: role,
gender: gender,
phone_number: phone_number
});
}
srvc.login = function (username, password) {
return $http.post(API + '/api/v1/token/auth/', { // <-- Login link here
username: username,
password: password
});
};
return srvc;
}
// We won't touch anything in here
function MainCtrl(user, auth, $location, $state, $rootScope) {
var self = this;
function handleRequest(res) {
var token = res.data ? res.data.token : null;
if (token) {
$location.path('/portfolio');
console.log('Bearer:', token);
auth.saveUsername($scope.username);
$rootScope.username = auth.getUsername();
}
// self.message = res.data.message;
}
self.login = function () {
user.login(self.username, self.password)
.then(handleRequest, handleRequest)
}
self.register = function () {
user.register(self.first_name, self.last_name, self.username, self.email, self.password, self.role, self.gender, self.phone_number)
.then(handleRequest, handleRequest)
}
self.logout = function () {
auth.logout && auth.logout();
$location.path('/login');
}
self.isAuthed = function () {
return auth.isAuthed ? auth.isAuthed() : false
}
}
angular
.module('App', ['ui.router'])
.factory('authInterceptor', authInterceptor)
.service('user', userService)
.service('auth', authService)
.constant('API', 'link-to-my-api') // <-- API Link here
.config(function ($stateProvider, $urlRouterProvider, $httpProvider) {
Since JWT verification is working for the tutorial's API, the fault lies in your authentication server i.e. creation/verification of JWT token) and not in the snippet above (the client handling)
You are getting 401 due to an incorrect JWT token created or incorrect validation of the JWT token
A brief explaination of how JWT authenticates.
In the example, the user first signs into the authentication server using the authentication server’s login system.
The authentication server then creates the JWT and sends it to the user.
When the user makes API calls to the application, the user passes the JWT along with the API call.
In this setup, the application server would be configured to verify that the incoming JWT are created by the authentication server

AngularJs authentication jwt

Hi I followed a tutorial on the web. Everything work but I would encode bas64 with a secret or jwt but I don't know how. Can you help me please ?
(function () {
'use strict';
angular
.module('app')
.factory('AuthenticationService', Service);
function Service($http, $localStorage) {
var service = {};
service.Login = Login;
service.Logout = Logout;
return service;
function Login(username, password, callback) {
$http.post('/api/authenticate', { username: username, password: password })
.success(function (response) {
// login successful if there's a token in the response
if (response.token) {
// store username and token in local storage to keep user logged in between page refreshes
$localStorage.currentUser = { username: username, token: response.token };
// add jwt token to auth header for all requests made by the $http service
$http.defaults.headers.common.Authorization = 'Bearer ' + response.token;
// execute callback with true to indicate successful login
callback(true);
} else {
// execute callback with false to indicate failed login
callback(false);
}
});
}
function Logout() {
// remove user from local storage and clear http auth header
delete $localStorage.currentUser;
$http.defaults.headers.common.Authorization = '';
}
}
})();
and my service :
function run($rootScope, $http, $location, $localStorage) {
// keep user logged in after page refresh
if ($localStorage.currentUser) {
$http.defaults.headers.common.Authorization = 'Bearer ' + $localStorage.currentUser.token;
}
// redirect to login page if not logged in and trying to access a restricted page
$rootScope.$on('$locationChangeStart', function (event, next, current) {
var publicPages = ['/login'];
var restrictedPage = publicPages.indexOf($location.path()) === -1;
if (restrictedPage && !$localStorage.currentUser) {
$location.path('/login');
}
});
}
and the nodeJs :
function setupFakeBackend($httpBackend) {
var testUser = { username: 'test', password: 'test', firstName: 'Test', lastName: 'User' };
// fake authenticate api end point
$httpBackend.whenPOST('/api/authenticate').respond(function (method, url, data) {
// get parameters from post request
var params = angular.fromJson(data);
// check user credentials and return fake jwt token if valid
if (params.username === testUser.username && params.password === testUser.password) {
return [200, { token: 'fake-jwt-token' }, {}];
} else {
return [200, {}, {}];
}
});
$httpBackend.whenGET(/^\w+.*/).passThrough();
}
Thank you for your answer :)
JSON Web Tokens are composed of three JSON objects encoded to base 64 seperated by a . character.
header.payload.signiture
The example found at jwt.io eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ decodes to three JSON strings separated by .
If you wish to pull out the seperate componets you must first split the String
// es6
let myJwt = getToken();
let jwtParts = myJwt.split('.').map(part => btoa(part));
console.log(`header: ${jwtParts[0]}, payload: ${jwtParts[1]}, sig: ${jwtParts[2]}`)
On the server side you should be using the signing (for login) and verifying functions (for subsequent authentication) found in your JWT library i.e https://github.com/auth0/node-jsonwebtoken
Let me know if that was not quite what you are looking for

How to set default header values in Angular Resource?

The following approach does not work:
angular.module('myApp.myModule').factory('MyResource', function($resource, $cookies) {
var token = $cookies.get('token');
var user = $cookies.get('username');
console.log ("Token: "+token+" User: "+user);
return $resource(
'http://my-rest-api/whatever/:id',
{
headers: {
'token': token,
'user': user
}
}
)});
Console shows the correct data, but they were not sent..
That's the part somewhere in the related Controller (excerpt):
var getEntryOne = MyResource.get({ id: 1 }, function() {
console.log("Result: "+getEntryOne);
});
I get the "Message: Token invalid", I see the request-http-headers in firebug, they were not setted.
You are setting headers for get request then it should be there in get option of $resource
$resource('http://my-rest-api/whatever/:id',{},{
get:{
method:"GET",
headers:{
'token': token,
'user': user
}
},
});
If you wanted to add this header information to each request, then you could have http inteceptor which will be add header information on each request.
app.service('MyResourceInterceptor', ['$cookies', function($cookies) {
var token = $cookies.get('token'),
user = $cookies.get('username'),
service = this;
service.request = function(config) {
config.headers['token'] = token;
config.headers['user'] = user;
return config;
};
}]);
app.config([ '$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('MyResourceInterceptor');
}]);

Can't remove JSON web tokens from headers for one XHR request in angular

I need to use web tokens for authentication and to access my REST API. I am also trying to access a weather API, but cannot access it because the headers are sending a x-access-token and I am getting this error:
Request header field x-access-token is not allowed by
Access-Control-Allow-Headers.
I've tried the following to reset the headers token to undefined for that particular request. Unfortunately when I inspect the config object in the browser console the user's token is still there. Please help!
weatherService.js
//Make a request to GET weather by zip code
weatherFactory.getWeather = function(zip){
$http.get('http://api.openweathermap.org/data/2.5/forecast/city?id=524901',
{
headers: {'x-access-token': undefined}
})
//Resolve our promise with the data requested
.success(function(data){
console.log(data);
})
//Promise will be rejected
.error(function(err){
console.log('Error getting data');
});
return $q.reject(zip);
};//end getWeather
app.js
//./public/app/app.js
angular.module('swellsApp', [
// 'ngAnimate',
'appRoutes',
'userService',
'weatherService',
'mainCtrl',
'userCtrl',
'weatherCtrl'
])
//application configuration to integrate tokens into our requests
.config(function($httpProvider){
//attach auth interceptor to the http requests
$httpProvider.interceptors.push('AuthInterceptor');
});
authService.js
// authService.js
angular.module('authService', [])
// ============================================
//auth factory to login and get information
//inject $http for communicating with the API
//inject $q to return promise objects
//inject AuthToken to manage tokens
// ============================================
.factory('Auth', function($http, $q, AuthToken){
//create auth factory object
var authFactory = {};
// handle login for users
// Post request to /api/authenticate
authFactory.login = function(username, password){
//return the promise object and its data
return $http.post('/api/authenticate', {
username: username,
password: password
})
.success(function(data){
AuthToken.setToken(data.token);
return data;
});
};
//log a user out by clearing the token useing AuthToken factory
authFactory.logout = function(){
//clear the token
AuthToken.setToken();
};
//check if a user is logged in and check if there is a local token
authFactory.isLoggedIn = function(){
if(AuthToken.getToken())
return true;
else
return false;
};
//get the logged in user
authFactory.getUser = function(){
if(AuthToken.getToken())
return $http.get('/api/me');
else
return $q.reject({message: "User doesn't have a token"});
};
//return auth factory object
return authFactory;
})//End Auth
// ============================================
// factory for handling tokens
// inject $window to store token on the client-side
// ============================================
.factory('AuthToken', function($window){
var authTokenFactory = {};
//get the token out of local storage
authTokenFactory.getToken = function(){
return $window.localStorage.getItem('token');
};
//set the token or clear the token
//if token is passed, set token - if there is no token, clear it from local storage
authTokenFactory.setToken = function(token){
if(token)
$window.localStorage.setItem('token', token);
else
$window.localStorage.removeItem('token');
};
//return auth token factory
return authTokenFactory;
})//End AuthToken
// ============================================
// application configuration to integrate token into requests
// ============================================
.factory('AuthInterceptor', function($q, $location, AuthToken){
var interceptorFactory = {};
//attach the token to all HTTP requests
interceptorFactory.request = function(config){
//grab the token
var token = AuthToken.getToken();
//If token exists then add it to the header as x-access-token
if(token)
config.headers['x-access-token'] = token;
console.log(config);
return config;
};
//On response errors
interceptorFactory.responseError = function(response){
//If server returns a 403 forbidden response
if(response.status == 403)
$location.path('/login');
//return the errors from the server as a promise
return $q.reject(response);
}
//return interceptorFactory
return interceptorFactory;
});//End AuthInterceptor
In the request interceptor you can use config.url to see if request is going to other api.
Following is an update to your existing code in the interceptor
//attach the token to all HTTP requests - except weather API
interceptorFactory.request = function (config) {
var isWeatherAPI = config.url.indexOf('api.openweathermap.org') > -1;
// don't modify weather api headers
if (!isWeatherAPI) {
//grab the token
var token = AuthToken.getToken();
//If token exists then add it to the header as x-access-token
if (token) {
config.headers['x-access-token'] = token;
}
}
return config;
};
When you are sending the token instead of "x-access-token" try headers.Authorization = "Bearer " + token;

Resources