Angular 2 and JWT authentication

You can pass Authorization headers in couple of ways, solution I presented here is a bit more elaborate cause it could include loading spinner and centralized error handling for each http request.

Extending Http service

First thing to do is to create custom Http service cause we need to pass some headers in each request.

import { Injectable } from '@angular/core';
import {
Http,
RequestOptions,
RequestOptionsArgs,
Response,
Request,
Headers,
XHRBackend
} from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import { MyCustomRequestOptions } from '../_helpers/request-options';
@Injectable ()
export class HttpService extends Http {
public token: string;
apiUrl = 'http://example.com/api/';
constructor(
backend: XHRBackend,
defaultOptions: MyCustomRequestOptions
) {
super(backend, defaultOptions);
}

From the code above everything is pretty clear, only new thing is MyCustomRequestOptions class which is injected into constructor.

Appending headers

Usual way of doing JWT authentication is by passing Authorization header with the actual token. There are other ways like passing the token via query parameter but then we wouldn’t need headers.

To be able to pass custom headers to http service we need to extend BaseRequestOptions class.

import { BaseRequestOptions } from '@angular/http';
export class MyCustomRequestOptions extends BaseRequestOptions {
public token: string;
constructor (customOptions?: any) {
super();
let user = JSON.parse(localStorage.getItem('user'));
this.token = user && user.token;
this.headers.append('Content-Type', 'application/json');
this.headers.append('Authorization', 'Bearer ' + this.token );

}
}

From constructor you can see that I am calling superclass constructor first and after that I am extracting value of token from local storage.

You can get that value however you want in my case I stored user object in local storage and it posses JWT token.

Back to custom Http service

Now I need to overwrite each http method that I am going to use, that is the place to display some spinner to the user and do error handling.

// HttpService
get(url: string, options?: RequestOptionsArgs): Observable<any> {
return super.get(this.getFullUrl(url), this.requestOptions(options))
.catch() // Catch exception here
.do((res: Response) => {
// Handle success, maybe display notification
}, (error: any) => {
// Handle errors
})
.finally(() => {
// Request completed
});
}
private getFullUrl(url: string): string {
return this.apiUrl + url;
}
private requestOptions(options?: RequestOptionsArgs): RequestOptionsArgs {
if (options == null) {
options = new MyCustomRequestOptions();
}
if (options.headers == null) {
options.headers = new Headers();
}
return options;
}

For this example I took get method, I am also using few helper methods for full url and request options.

I will import this service into CoreModule, cause it extends built-in class it needs to have service provider defined and factory:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { XHRBackend, RequestOptions } from '@angular/http';
import { HttpService } from './http.service';
import { httpServiceFactory } from '../_factories/http-service.factory';
import { AngularReduxRequestOptions } from './angular-redux-request.options';
@NgModule({
imports: [
CommonModule
],
exports: [
],
declarations: [
],
providers: [
{
provide: HttpService,
useFactory: httpServiceFactory,
deps: [XHRBackend, RequestOptions]
}
]
})
export class CoreModule { }

Factory

I am storing my factories inside _factories directory:

import { XHRBackend } from '@angular/http';
import { AngularReduxRequestOptions } from '../core/angular-redux-request.options';
import { HttpService } from '../core/http.service';
function httpServiceFactory(backend: XHRBackend, options: AngularReduxRequestOptions) {
return new HttpService(backend, options);
}
export { httpServiceFactory };

Closing

The only thing left now is importing CoreModule into AppModule.

Source code

Entire source code is published on GitHub.