Angular authentication with JSON Web tokens.
This module is for authenticating with a remote REST API, and attaching the auth token to all subsequent $http
xhr
requests to the API.
The module has the following features
- Basic (username/password) authentication
- Automatic token refreshing when it is about to expire,
- Persisting the token to
localstorage
so it is available between sessions - Token based authentication (useful for authenticating a confirmed registration email).
Install through npm:
npm install angular-jwt-auth --save
- Require the
ngJwtAuth
module in your angular application
import "angular"
import "angular-jwt-auth"
angular.module('app', ['ngJwtAuth'])
- (Optionally) configure the service provider
import {NgJwtAuthServiceProvider} from "angular-jwt-auth"
angular.module('app', ['ngJwtAuth'])
.config(['ngJwtAuthServiceProvider', function(ngJwtAuthServiceProvider:NgJwtAuthServiceProvider){
ngJwtAuthServiceProvider
.configure({
tokenLocation: 'token-custom',
apiEndpoints: {
base: '/api',
login: '/login-custom',
tokenExchange: '/token-custom',
refresh: '/refresh-custom',
}
})
;
}])
- Inject the
ngJwtAuthService
, initialise it then use it! The init function loads any existing token from storage and kicks off the $interval that monitors the expiry status of the token.
It is highly recommended that you register a login prompt factory (See below), as this will allow the interceptor to prompt your users for their login details when an api request that returns status code 401.
angular.module('app', ['ngJwtAuth'])
.run(['ngJwtAuthService', function(ngJwtAuthService){
ngJwtAuthService.init();
}])
.controller('AppCtrl', ['$scope', 'ngJwtAuthService', function($scope, ngJwtAuthService){
$scope.login = function(username, password){
ngJwtAuthService.authenticateCredentials(username, password)
.then(function(authenticatedUser){
console.log("Login Success!", authenticatedUser);
})
.catch(function(err){
console.error(err);
})
};
}])
To handle prompting the user for authentication, angular-jwt-auth provides a registration method to allow the application to provide a function that resolves a deferred promise for user credentials, and returns a promise that the user has attempted authentication. The auth service will then attempt to log in with the resolved credentials. If an API call returns with a 401 response, the service will intercept the response, and login prompt function will run, giving the user a prompt to re-enter their credentials. If their login is successful, the API call that was previously rejected will be reattempted.
Full typescript example from the Spira project using a modal from angular materials $mdDialog
:
Note this example is in typescript, but it is the same process in plain javascript.
namespace app.guest.login {
export const namespace = 'app.guest.login';
class LoginConfig {
static $inject:string[] = ['ngJwtAuthServiceProvider',];
constructor(private ngJwtAuthServiceProvider:NgJwtAuthServiceProvider) {
ngJwtAuthServiceProvider
.configure({
tokenLocation: 'token-custom',
apiEndpoints: {
base: '/api',
login: '/login-custom',
tokenExchange: '/token-custom',
refresh: '/refresh-custom',
}
});
}
}
class LoginController {
static $inject = ['$rootScope', '$mdDialog', '$mdToast', 'ngJwtAuthService', 'deferredCredentials', 'loginSuccess', 'userService'];
constructor(private $rootScope:global.IRootScope,
private $mdDialog:ng.material.IDialogService,
private $mdToast:ng.material.IToastService,
private ngJwtAuthService:NgJwtAuthService,
private deferredCredentials:ng.IDeferred<NgJwtAuth.ICredentials>,
private loginSuccess:{promise:ng.IPromise<NgJwtAuth.IUser>},
private userService:common.services.user.UserService) {
this.handleLoginSuccessPromise();
}
/**
* Register the login success promise handler
*/
private handleLoginSuccessPromise() {
//register error handling and close on success
this.loginSuccess.promise
.then(
(user) => this.$mdDialog.hide(user), //on success hide the dialog, pass through the returned user object
null,
(err:Error) => {
if (err instanceof NgJwtAuthCredentialsFailedException) {
this.$mdToast.show(
(<any>this.$mdToast).simple()
.hideDelay(2000)
.position('top')
.content(err.message)
.parent('#loginDialog')
);
} else {
console.error(err);
}
}
);
}
/**
* allow the user to manually close the dialog
*/
public cancelLoginDialog() {
this.ngJwtAuthService.logout(); //make sure the user is logged out
this.$mdDialog.cancel('closed');
}
/**
* Attempt login
* @param username
* @param password
*/
public login(username, password) {
let credentials:ICredentials = {
username: username,
password: password,
};
this.deferredCredentials.notify(credentials); //resolve the deferred credentials with the passed creds
}
}
angular.module(namespace, [])
.config(LoginConfig)
.controller(namespace + '.controller', LoginController);
}
- Better documentation with examples in typescript.
- Site hosted on github showing off examples with material