import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpClient, HttpResponse } from '@angular/common/http'; import { ConfigService } from '@arrow/bom/config'; import { AuthService } from './auth.service'; import * as cookie from './util/cookie'; import { Observable, Observer, of, NEVER } from 'rxjs'; import { map, catchError, tap, switchMap } from 'rxjs/operators'; import { includes } from 'lodash-es'; import { LoggerService } from '@arrow/bom/logger'; import { AuthTokenService } from './auth-token.service'; @Injectable() export class AuthInterceptorService implements HttpInterceptor { constructor( private config: ConfigService, private authService: AuthService, private http: HttpClient, private loggerService: LoggerService, private authToken: AuthTokenService ) {} intercept( request: HttpRequest, next: HttpHandler ): Observable> { const target = this.config.getTarget(); let token$: Observable; let clientId: string; switch (target) { case 'myarrow': if (includes(request.url, this.config.getSecurityApi())) { return next.handle(request); } clientId = this.authToken.getClientId(); token$ = this.authService.checkAndRenewTokens().pipe( map((newTokens: any) => { return newTokens && newTokens.access_token ? newTokens.access_token : ''; }) ); break; case 'arrowcom': default: token$ = of(cookie.getCookie(cookie.cookieConfig.accessToken)); if (/anonymous\/carts\/$/gi.test(request.url)) { this.loggerService.error(request.url); throw new Error('Bad request to hybris'); } } return token$.pipe( switchMap((token: string) => { if (token && !request.url.endsWith('token')) { const headers = { Authorization: `Bearer ${token}` }; if (request.url.includes('design') && target === 'myarrow') headers['X-clientid'] = clientId; request = request.clone({ setHeaders: headers }); } if ( request.url.includes(this.config.hybris.cartApiUrl) && !request.url.endsWith('token') ) { return this._refreshHybrisTokenAndRetry(request, next); } return next.handle(request); }) ); } private _refreshHybrisTokenAndRetry( request: HttpRequest, next: HttpHandler ): Observable> { return new Observable((obs: Observer) => { next .handle(request) .pipe( catchError((error: any) => { // If the error caught is not a 401, then just rethrow the error if (!error || error.status !== 401) { return of(error); } const hybrisRefreshApi = this._getHybrisTokenRefreshUrl(); const arrowfed = `arrowfed=${encodeURIComponent( cookie.getCookie('arrowfed') )}`; // Call the hybris refresh token API endpoint return this.http .post(hybrisRefreshApi, arrowfed, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }) // I guess this set the cookie with the fresh token .pipe( switchMap(response => { if (response && response['access_token']) { cookie.setCookie( cookie.cookieConfig.accessToken, response['access_token'] ); return this.http.request(request.method, request.url, { body: request.body, headers: request.headers, params: request.params, responseType: request.responseType, withCredentials: request.withCredentials }); } else { // If new token is not in response, then rethrow error // A 401 is handled elsewhere return of( new HttpResponse({ status: 500, body: 'No token in response' }) ); } }), tap((v: any) => { obs.next(new HttpResponse({ body: v })); }), catchError((e: HttpResponse) => { this.loggerService.info( 'Refresh token API call catchError. error: ' + JSON.stringify(error) ); if (e && e.status === 401) { this._redirectToLoginPage(); return of(NEVER); } obs.error(e); }) ); }) ) .subscribe( (response: any) => { obs.next(response); }, (error: any) => { obs.error(error); }, () => { obs.complete(); } ); }); } private _getHybrisTokenRefreshUrl() { return this.config.hybris.cartApiUrl + this.config.hybris.token; } private _redirectToLoginPage() { if (!this.config.isLocal) { let url = location.href; url = url.substr(0, url.indexOf('/bom-tool/') + 9); const loginUrl = `${this.config.getRoute( 'login' )}?url=${encodeURIComponent(url)}`; location.replace(loginUrl); } else { alert( 'Hybris API responded with 401. Local mode detected, login redirect not activated.' ); } } }