Search code examples
angularangular-http-interceptors

Angular >4.3 & <5.2.3 - JWT Interceptor - Refresh Token


Important Angular >4.3 & <5.2.3

I use HttpClient and I create this interceptor to add the jwt token. Everyting work perfect but I have a bad practise. I use Http inside the HttpClient Interceptor. If I change

private http: Http,

to

private http: HttpClient

I get this cycle error

Cannot instantiate cyclic dependency! InjectionToken_HTTP_INTERCEPTORS ("[ERROR ->]") 

any ideas how can I make it work?

import {Injectable} from "@angular/core";
import {HttpEvent, HttpHandler, HttpInterceptor} from "@angular/common/http";
import {HttpRequest} from "@angular/common/http";
import {Observable} from "rxjs/Observable";
import {Http} from "@angular/http";
import {SiteService} from "../services/site.service";
import {Router} from "@angular/router";

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

constructor(
    private http: Http,
    private router: Router,
    private siteService: SiteService
) {}

refreshToken() {
    return this.http.get(this.siteService.apiDomain() + '/api/token?token=' + localStorage.getItem('JWToken'), {})
        .map((response: any) => {
            let data = response.json();
            return {
                token: data.token,
                permissions: data.permissions,
                user: data.user,
            };
        })
}

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const clonedRequest = req.clone({
        headers: req.headers.set('Authorization', 'Bearer ' + localStorage.getItem('JWToken'))
    });

    return next.handle(clonedRequest).catch((res) => {

        if (res.status === 401 || res.status === 403) {
            return this.refreshToken().flatMap((data) => {
                if (data.token !== '') {
                    localStorage.setItem('currentUser', JSON.stringify(data.user));
                    localStorage.setItem('currentUserPermissions', JSON.stringify(data.permissions));
                    localStorage.setItem('JWToken', data.token);
                } else {
                    localStorage.removeItem('currentUser');
                    localStorage.removeItem('currentUserPermissions');
                    localStorage.removeItem('JWToken');
                    this.router.navigate(['./auth/login']);
                    return Observable.throw(res);
                }
                const clonedRequestRepeat = req.clone({
                    headers: req.headers.set('Authorization', 'Bearer ' + localStorage.getItem('JWToken'))
                });
                return next.handle(clonedRequestRepeat);
            })
        } else {
            return Observable.throw(res);
        }

    });

}
}

Another important thing for those who will use this interceptor for their project but irrelevant to the current problem is to set headers to the refresh token response at least some seconds.

->header('Cache-Control', 'public, max-age=45')
->header('Expires', date('D, d M Y H:i:s ', time() + 45).'GMT');

Solution

  • I resolved simply not setting authService in constructor but getting in the intercept function.

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // Get the auth header from the service.
        const auth = this.inj.get(AuthenticationService);
        const authToken = auth.getAuthorizationToken();
        ...
    }
    

    Hou have to add Injector to your constructor first

    constructor(
                ...
                private inj: Injector
    ) {}