Trying to repeat a http request after refresh token with a interceptor in angular 7

后端 未结 3 1907
时光说笑
时光说笑 2021-02-03 10:52

I\'m trying to automate the refresh token requests upon receiving an error 401 with angular 7.

Between that I do not find much documentation of how to do it with angular

3条回答
  •  礼貌的吻别
    2021-02-03 11:29

    Below is the code for calling refresh token and after getting refresh token calls failed API's,

    Comments in source code would help you to understand flow. Its tested and fine for below scenarios

    1) If single request fails due to 401 then it will called for refresh token and will call failed API with updated token.

    2) If multiple requests fails due to 401 then it will called for refresh token and will call failed API with updated token.

    3) It will not call token API repeatedly

    If still anyone found new scenario where this code not working please inform me so I will test and update it accordingly.

    import { Injectable } from "@angular/core";
    import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse, HttpHeaders, HttpClient, HttpResponse } from "@angular/common/http";
    
    import { Observable } from "rxjs/Observable";
    import { throwError, BehaviorSubject } from 'rxjs';
    import { catchError, switchMap, tap, filter, take, finalize } from 'rxjs/operators';
    import { TOKENAPIURL } from 'src/environments/environment';
    import { SessionService } from '../services/session.service';
    import { AuthService } from '../services/auth.service';
    
    /**
     * @author Pravin P Patil
     * @version 1.0
     * @description Interceptor for handling requests which giving 401 unauthorized and will call for 
     * refresh token and if token received successfully it will call failed (401) api again for maintaining the application momentum
     */
    @Injectable()
    export class RefreshTokenInterceptor implements HttpInterceptor {
    
        private isRefreshing = false;
        private refreshTokenSubject: BehaviorSubject = new BehaviorSubject(null);
    
    
        constructor(private http: HttpClient, private sessionService: SessionService, private authService: AuthService) { }
    
        /**
         * 
         * @param request HttpRequest
         * @param next HttpHandler
         * @description intercept method which calls every time before sending requst to server
         */
        intercept(request: HttpRequest, next: HttpHandler): Observable> {
            // Taking an access token
            const accessToken = sessionStorage.getItem('ACCESS_TOKEN');
            // cloing a request and adding Authorization header with token
            request = this.addToken(request, accessToken);
            // sending request to server and checking for error with status 401 unauthorized
            return next.handle(request).pipe(
                catchError(error => {
                    if (error instanceof HttpErrorResponse && error.status === 401) {
                        // calling refresh token api and if got success extracting token from response and calling failed api due to 401                    
                        return this.handle401Error(request, next);
                    } // If api not throwing 401 but gives an error throwing error
                    else {
                        return throwError(error);
                    }
                }));
        }
    
        /**
         * 
         * @param request HttpRequest
         * @param token token to in Authorization header
         */
        private addToken(request: HttpRequest, token: string) {
            return request.clone({
                setHeaders: { 'Authorization': `Bearer ${token}` }
            });
        }
    
        /**
         * This method will called when any api fails due to 401 and calsl for refresh token
         */
        private handle401Error(request: HttpRequest, next: HttpHandler) {
            // If Refresh token api is not already in progress
            if (this.isRefreshing) {
                // If refreshTokenInProgress is true, we will wait until refreshTokenSubject has a non-null value
                // – which means the new token is ready and we can retry the request again
                return this.refreshTokenSubject
                    .pipe(
                        filter(token => token != null),
                        take(1),
                        switchMap(jwt => {
                            return next.handle(this.addToken(request, jwt))
                        }));
            } else {
                // updating variable with api is in progress
                this.isRefreshing = true;
                // Set the refreshTokenSubject to null so that subsequent API calls will wait until the new token has been retrieved
                this.refreshTokenSubject.next(null);
    
                const refreshToken = sessionStorage.getItem('REFRESH_TOKEN');
                // Token String for Refresh token OWIN Authentication
                const tokenDataString = `refresh_token=${refreshToken}&grant_type=refresh_token`;
                const httpOptions = {
                    headers: new HttpHeaders({
                        'Content-Type': 'application/x-www-form-urlencoded',
                        'X-Skip-Interceptor': ''
                    })
                };
                return this.http.post(TOKENAPIURL, tokenDataString, httpOptions)
                    .pipe(switchMap((tokens) => {
                        this.isRefreshing = false;
                        this.refreshTokenSubject.next(tokens.access_token);
                        // updating value of expires in variable                    
                        sessionStorage.setItem('ACCESS_TOKEN', tokens.access_token);
                        return next.handle(this.addToken(request, tokens.access_token));
                    }));
            }
        }
    }
    

提交回复
热议问题