attilan

Interceptor w/ refresh token

Oct 19th, 2021
750
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { catchError, delay, filter, switchMap, take } from 'rxjs/operators';
  2. import {
  3.   HttpErrorResponse,
  4.   HttpEvent,
  5.   HttpHandler,
  6.   HttpInterceptor,
  7.   HttpRequest,
  8. } from '@angular/common/http';
  9. import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
  10. import { Injectable, Injector } from '@angular/core';
  11. import { v4 as uuidv4 } from 'uuid';
  12. import { TranslateService } from '@ngx-translate/core';
  13. import { environment } from '@environments/environment';
  14. import { AppConfig } from '@config/app.config';
  15. import { HttpService } from '@shared/modules/http/http.service';
  16. import { EndpointsConfig } from '@config/endpoints.config';
  17. import { AuthService } from '../auth/services/auth.service';
  18. import { GlobalLoaderService } from '../global-loader/services/global-loader.service';
  19. import { version } from '../../../../../package.json';
  20.  
  21. @Injectable()
  22. export class WsInterceptor implements HttpInterceptor {
  23.   private isRefreshing = false;
  24.   private readonly refreshToken$ = new BehaviorSubject<string>(null);
  25.  
  26.   constructor(
  27.     private loader: GlobalLoaderService,
  28.     private injector: Injector,
  29.     private translate: TranslateService,
  30.     private httpService: HttpService
  31.   ) {}
  32.  
  33.   intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  34.     let requestClone = req.clone();
  35.     const auth = this.injector.get<AuthService>(AuthService);
  36.     const apiCall =
  37.       requestClone.urlWithParams.includes(environment.apiUrl) &&
  38.       !requestClone.url.includes('assets');
  39.  
  40.     if (!apiCall) {
  41.       return next.handle(requestClone);
  42.     }
  43.  
  44.     requestClone = this.setCommonHeaders(requestClone);
  45.  
  46.     return next.handle(requestClone).pipe(
  47.       catchError((error) => {
  48.         if (error instanceof HttpErrorResponse && error.status === 401) {
  49.           const skippedUrls = [EndpointsConfig.refresh, EndpointsConfig.login];
  50.           if (!skippedUrls.some((route) => requestClone.url.includes(route))) {
  51.             return this.handle401Error(requestClone, next);
  52.           }
  53.  
  54.           // unsuccessful refresh call
  55.           auth.logout(true);
  56.         }
  57.  
  58.         return throwError(error);
  59.       })
  60.     );
  61.   }
  62.  
  63.   setCommonHeaders(req: HttpRequest<any>): HttpRequest<any> {
  64.     return req.clone({
  65.       withCredentials: true, // to send cookies with the request
  66.       setHeaders: this.getHeaders(),
  67.     });
  68.   }
  69.  
  70.   private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
  71.     if (this.isRefreshing) {
  72.       return this.refreshToken$.pipe(
  73.         filter((token) => token != null),
  74.         take(1),
  75.         switchMap(() => {
  76.           return next.handle(this.setCommonHeaders(request));
  77.         })
  78.       );
  79.     }
  80.  
  81.     this.isRefreshing = true;
  82.     this.refreshToken$.next(null);
  83.  
  84.     return of(null).pipe(
  85.       delay(300),
  86.       switchMap(() => {
  87.         return this.httpService
  88.           .postWithHeaders(EndpointsConfig.refresh, {}, this.getHeaders())
  89.           .pipe(
  90.             switchMap(({ token }) => {
  91.               this.isRefreshing = false;
  92.               this.refreshToken$.next(token);
  93.               return next.handle(this.setCommonHeaders(request));
  94.             }),
  95.             catchError((err) => {
  96.               return throwError(err);
  97.             })
  98.           );
  99.       })
  100.     );
  101.   }
  102.  
  103.   private getHeaders(): { [key: string]: string } {
  104.     const auth = this.injector.get<AuthService>(AuthService);
  105.  
  106.     let headers: { [key: string]: string } = {
  107.       'Accept-Language': this.translate.currentLang || AppConfig.defaultLanguage,
  108.       'x-client-version': version,
  109.       'x-client-env': environment.name,
  110.       'x-request-id': uuidv4(),
  111.     };
  112.  
  113.     const CSRFToken = auth.authStorage.readCSRFToken();
  114.  
  115.     if (environment.authStorageStrategy === 'cookie' && CSRFToken) {
  116.       headers = { ...headers, 'x-csrf-token': CSRFToken };
  117.     }
  118.  
  119.     return headers;
  120.   }
  121. }
  122.  
RAW Paste Data