if you don’t know what is JWT you have a nice reading ahead. lots of links and information so I won’t post any.
In your app.module.ts file:
// Ref.: https://github.com/angular/angular/issues/11262
export function httpFactory(backend: XHRBackend, options: RequestOptions, global: AppState, toastCtrl: ToastController, eventService: EventsService) {
return new ExtendedHttpService(backend, options, global, toastCtrl, eventService);
}
before the @ngMoidule declaration.
And in the same file, in the providers section:
// Ref.: https://gist.github.com/mrgoos/45ab013c2c044691b82d250a7df71e4c#gistcomment-2041630
{
provide: Http,
useFactory: httpFactory,
deps: [XHRBackend, RequestOptions, AppState, ToastController, EventsService]
}
Remember to import all required dependencies.
In our project we had to make a complete “extended-http.service.ts” to handle expired tokens in a silent way (here goes the complete file):
// Ref.: https://gist.github.com/mrgoos/45ab013c2c044691b82d250a7df71e4c
import { Injectable } from '@angular/core';
import { Request, XHRBackend, RequestOptions, Response, Http, RequestOptionsArgs, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { ToastController } from "ionic-angular";
import { AppState } from "../app/app.global";
import { EventsService } from "./events-service";
@Injectable()
export class ExtendedHttpService extends Http {
constructor(backend: XHRBackend,
defaultOptions: RequestOptions,
public global: AppState,
public toastCtrl: ToastController,
public eventService: EventsService) {
super(backend, defaultOptions);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
//do whatever
if (typeof url === 'string') {
if (!options) {
options = { headers: new Headers() };
}
this.setHeaders(options);
} else {
this.setHeaders(url);
}
return super.request(url, options).catch(this.catchErrors());
}
private catchErrors() {
return (res: Response) => {
if (res.status === 401 || res.status === 403) {
let jsonRes: any = res.json();
console.log('http interceptor catchErrors', jsonRes);
if(jsonRes.errorCode === 11){ // Token has expired
this.refreshToken(this.global.get('refreshToken'))
.map((res) => res.json() )
.subscribe((data) => {
console.log('refreshToken', data);
let newToken: string = data.token;
this.global.set('token', newToken);
},
err => {
let jsonErr: any = err.json();
if(jsonErr.error === 'Unauthorized' && jsonErr.path === '/api/token'){
// RefreshToken has expired
// Show toast "Debe reingresar sus credenciales"
this.presentToast('Debe reingresar sus credenciales.', true, 'LoginPage');
}
}
);
}
if(res.status === 403){
this.presentToast('Debe reingresar sus credenciales.', true, 'LoginPage');
}
}
return Observable.throw(res);
};
}
private setHeaders(objectToSetHeadersTo: Request | RequestOptionsArgs) {
//add whatever header that you need to every request
//in this example I add header token by using authService that I've created
objectToSetHeadersTo.headers.set('Content-Type', 'application/json');
objectToSetHeadersTo.headers.set('X-Requested-With', 'XMLHttpRequest');
let token: string = this.global.get('token');
token = (!!token)?token:'';
objectToSetHeadersTo.headers.set('X-Authorization', 'Bearer ' + token);
}
private refreshToken(refreshToken: string = ''): Observable<Response> {
let BASE_URL: string = this.global.get('apiServer');
let headers: Headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('X-Requested-With', 'XMLHttpRequest');
headers.append('X-Authorization', 'Bearer ' + refreshToken);
return super.request(BASE_URL + '/token', { headers: headers });
}
private toastActive: boolean = false;
private presentToast(message: string, showCloseButton: boolean = false, targetPage?: string) {
if(!this.toastActive){
let toast = this.toastCtrl.create({
message: message,
duration: (!showCloseButton)?1750:undefined,
showCloseButton: showCloseButton,
closeButtonText: 'Cerrar',
position: 'bottom',
dismissOnPageChange: true
});
toast.dismissAll();
toast.present().then(() => {
this.toastActive = true;
toast.onDidDismiss(() => {
console.log('Dismissed toast');
this.toastActive = false;
if(!!targetPage){
this.eventService.broadcast('openPage', targetPage);
}
});
});
}
}
}
We also use an EventService (found in https://stackoverflow.com/questions/34700438/global-events-in-angular-2 )
to call other module’s functions using event broadcasting (kind of ala AngularJS way).
Please try to understand the code, don’t just copy & paste it, otherwise you wion’t even understand how to solve your problem.
Best regards.
J