2

I've implemented this logic in Angular interceptor to intercept all the requests and inject Authorization header. It also catches all the 401 Unauthorized responses and in this case tries to refresh the token first, then retries the original request.

This works fine, except the fact that I can already know which requests will be 401 Unauthorized (when token is expires). So in case of expired token I'd like to refresh the token before sending the actual request.

I need the refreshed token in place marked as HERE @todo, or in concatMap call the refresh function, then the original request, and once finished, proceed with the rest of the code.

How can I do this? Here's what I have so far:

import {HttpHandler, HttpInterceptor, HttpRequest, HttpErrorResponse} from '@angular/common/http';
import {AuthService} from './auth.service';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/concatMap';
import 'rxjs/add/operator/concat';
import 'rxjs/add/operator/catch';
import {Injectable, Injector} from '@angular/core';
import {JwtHelper} from 'angular2-jwt';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    private auth: AuthService;
    jwtHelper = new JwtHelper();

    constructor(private injector: Injector) {
    }
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
        this.auth = this.injector.get(AuthService);

        // this.auth.token is an instance of Observable<string> (list of tokens)
        return this
            .auth
            // Get the latest token from the auth service.
            .token
            // Map the token to a request with the right header set.
            .map(token => {
                const cloned = request.clone({ headers: request.headers.set('Authorization', `Bearer ${token}`) });
                if (this.jwtHelper.isTokenExpired(token)) {
                    // here I already know that the request is not necessary, as will result in 401
                    console.log('WARN: refresh');
                    // HERE @todo: refresh the token before further processing
                }
                return cloned;
            })
            // Execute the request on the server.
            .concatMap((cloned) => {
                return next.handle(cloned);
            })
            // Catch the 401 and handle it by refreshing the token and restarting the chain
            // (where a new subscription to this.auth.token will get the latest token).
            .catch((err, restart) => {
                // If the request is 401 unauthorized, try refreshing the token before restarting.
                if (err instanceof HttpErrorResponse && err.status === 401) {
                        // this.auth.refreshToken is instance of Observable<any>
                        return this.auth.refreshToken
                            .concat(restart)
                            .catch((error, caught) => {
                                    // in case of failed refresh token, logout and redirect to login route
                                    if (error instanceof HttpErrorResponse && err.status === 401) {
                                        this.auth.logout(true);
                                    }
                                    throw caught;
                                }
                            );
                }

                throw(err);
            });
    }
}
Sfisioza
  • 3,830
  • 6
  • 42
  • 57

0 Answers0