zaitsman
zaitsman

Reputation: 9499

Angular seamless refresh token flow

I have a system where i returns a refresh token (long-lived) and an access token (short-lived) to the Angular frontend.

Currently, it is setup for access token only. I have my HttpInterceptor setup where i inject the access token like so:

 intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        let token = this.somewhere.getToken();

        let requestToHandle: HttpRequest<any> = req;
        if (token != null && token.length > 0) {
            const clonedRequest = req.clone({ headers: req.headers.set('Authorization', 'Bearer ' + token) });
            requestToHandle = clonedRequest;
        }

        return next.handle(requestToHandle).catch((err: HttpErrorResponse) => {
             // some error handling here
        });
}

Now that i am adding refresh tokens, i want the system to try to exchange refresh token for an access token if we got a 401 (inside .catch() block).

however, i could not inject an HttpClient to my interceptor due to cyclic dependency. (i tried using Injector, too, no luck).

Update After updating Angular to 5.2.9 i can actually inject HttpClient in.

i added the code like so:

return next.handle(requestToHandle).catch((err: HttpErrorResponse) => {

    if (!(err.error instanceof Error)) {
        if (err.status === 401) {
            this.http.post<Token>('http://somewhere' + '/somerfreshtokenapi',
                {
                    refresh_token: this.someService.refreshToken
                }).subscribe(result => {
                    let requestClone = requestToHandle.clone({ headers: req.headers.set('Authorization', 'Bearer ' + result.token) });
                    requestToHandle = requestClone;
                    // how do i wire this back to `next.handle()`?
                    return this.httpClient.request(requestToHandle);
                }, e => {
                    // some error handling here
                });
        });

    }

return Observable.throw(err);
});

Upvotes: 1

Views: 362

Answers (1)

AVJT82
AVJT82

Reputation: 73337

Just use switchMap instead of subscribe and execute the failed request with the new token:

if (err.status === 401) {
  this.http.post<Token>(...)
    .switchMap(result => {
       let requestClone = requestToHandle.clone({ headers: req.headers.set('Authorization', 'Bearer ' + result.token) });
       return next.handle(requestClone)
     }, e => {
       // some error handling here
     });
}

Upvotes: 1

Related Questions