0

Auth Service is not logging out on refresh token 401 error occurred. Instead it goes to catchError.ts. How can the application handle this.

Scenarios 1. Initially get access token using Basic Authorization header. 2. Make server call with access token. 3. Once access toekn is expired, call the refresh token service. 4. On refresh token call is success, use the new token for rest of the service call. 5. On refresh toeken failure, 401 error will be returned, but the application is not able to catch the error and its never logging out.

Using Angular 4 Http Interceptor

This is the interceptor

import { Injectable, Injector } from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse, HttpErrorResponse, HttpClient} from '@angular/common/http';
import * as AppUtils from '../common/app.utils';
import { AuthService } from './auth.service';
import {Observable} from 'rxjs/Rx';
import { 
    Router,
    Event, 
    NavigationStart, RoutesRecognized, NavigationEnd, NavigationCancel, NavigationError
} from '@angular/router'
import { LoaderService } from '../loader/loader.service'
import { BehaviorSubject } from "rxjs/BehaviorSubject";
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
import { EmptyObservable } from 'rxjs/observable/EmptyObservable';
import { catchError, filter, take, switchMap, finalize } from "rxjs/operators";
import { SharedService } from '../common/services/shared.service';

declare var swal: any;

@Injectable()
export class TokenInterceptor implements HttpInterceptor {

 isRefreshingToken: boolean = true;
 tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);
 constructor(private inj: Injector, private router: Router) {}

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
     const auth = this.inj.get(AuthService)  
       return next.handle(this.addToken(request))
      .pipe(
      catchError((error, ca) => {
        if (error instanceof HttpErrorResponse) {
          switch ((<HttpErrorResponse>error).status) {
            case 401:
              return this.handle401Error(request, next, auth)
            default:
              return ErrorObservable.create(error);
          }
        } else {
          return ErrorObservable.create(error);
        }
      })
      )
  }

addToken(req: HttpRequest<any>): HttpRequest<any> {

    let customReq: any;
    let client_id = 'test'; 
    let client_secret= 'secret';
    let basicheader = btoa(client_id +':'+ client_secret);
    if(req.url.indexOf("token?grant_type") < 0 || req.url.indexOf("token?grant_type") == 0){
            customReq = req.clone({
                headers: req.headers.set('Content-Type', 'application/json')
                                    .set('Authorization','Bearer '+ localStorage.getItem(AppUtils.STORAGE_ACCOUNT_ACCESS_TOKEN))
            })
                //Set estCode and perscode for Alshif Users
                if(localStorage.getItem(AppUtils.ALSHIFA_TRUSTED_LOGIN)!=null && localStorage.getItem(AppUtils.ALSHIFA_TRUSTED_LOGIN)==="Y"){
                    req.headers.append(AppUtils.ALSHIFA_USER_INFO, localStorage.getItem(AppUtils.DEFAULT_INSTITUTE) +":"+
                    localStorage.getItem(AppUtils.PERSON_CODE));
                }

      }else{
        customReq = req.clone({
          headers: req.headers.set('Authorization','Basic '+ basicheader)
      })
      }
        return customReq;

    }

     handle400Error(error) {
         console.log("400 error");
        if (error && error.status === 400 && error.error && error.error.error === 'invalid_grant') {
            // If we get a 400 and the error message is 'invalid_grant', the token is no longer valid so logout.
            return this.logoutUser();
        }

        return EmptyObservable.create();
    }


    handle401Error(req: HttpRequest<any>, next: HttpHandler, auth : any) {
        if (this.isRefreshingToken) {
            this.isRefreshingToken = false;


    let customReq: any;
    let client_id = 'test'; 
    let client_secret= 'secret';
    let basicheader = btoa(client_id +':'+ client_secret);
    auth.refreshToken().subscribe((token) => {

                    console.log("token " +JSON.stringify(token));
                    if (token instanceof HttpErrorResponse) {
      if (token.status === 401) {
console.log("error");
      }
                    }
                    if (token) {
                        localStorage.setItem(AppUtils.STORAGE_ACCOUNT_ACCESS_TOKEN, token["access_token"]);
                        localStorage.setItem(AppUtils.STORAGE_ACCOUNT_REFRESH_TOKEN, token["refresh_token"]);
                        localStorage.setItem(AppUtils.STORAGE_ACCOUNT_EXPIRES_IN, token["expires_in"]);
                        this.tokenSubject.next(token["access_token"]);
                        return next.handle(this.addToken(req));
                    }

                    console.log("refresh failed");
                    // If we don't get a new token, we are in trouble so logout.
                     this.logoutUser();
                     return EmptyObservable.create();

              }, (err) => {
                    console.log("error  2" +err);
                    // If there is an exception calling 'refreshToken', bad news so logout.
                     this.logoutUser();
                      return EmptyObservable.create();
              },() => { 
                    console.log("token finally)");
                    this.isRefreshingToken = true;
                });

              //  )
        } else {
            console.log("this.tokenSubject "+ this.tokenSubject);
            console.log("this.tokenSubjec2 "+ this.tokenSubject.filter(token => token != null));
            return this.tokenSubject
                .filter(token => token != null)
                .take(1)
                .switchMap(token => {
                    return next.handle(this.addToken(req));
                });


        }

    }



 logoutUser(){
     swal({
            title: 'Session Expired',
            text: 'your session has been expired please re login',

            timer: 5000,
            onOpen: () => {
              swal.showLoading()
            }
          }).then((result) => {
            if (result.dismiss === 'timer') {
              this.router.navigate([AppUtils.BACKEND_API_AUTHENTICATE_PATH]);
              window.location.reload();
            }
          })
   localStorage.clear();
   this.router.navigate([AppUtils.BACKEND_API_AUTHENTICATE_PATH]);
  // window.location.reload();
   //return Observable.throw("");
 }

This is the auth service class

 import { Injectable, Component, EventEmitter } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpClient, HttpResponse, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import 'rxjs/add/operator/map';
import {Observable} from 'rxjs/Rx';
import 'rxjs/add/operator/retry';
import * as AppUtils from '../common/app.utils';
import { Router } from '@angular/router';
import { SharedService } from '../common/services/shared.service';
import 'rxjs/add/operator/catch';

declare var swal: any;

@Injectable()
export class AuthService {

  cachedRequests: Array<HttpRequest<any>> = [];

  constructor(private router: Router, private http : HttpClient) {

  }

  refreshToken(): Observable<any>{

    let client_id = 'irsauth'; 
    let client_secret= 'secret';
    let basicheader = btoa(client_id +':'+ client_secret);
    let headers = new HttpHeaders();
    headers.set('Authorization', 'Basic ' + basicheader);
    return this.http.get(AppUtils.REFRESH_TOKEN + localStorage.getItem(AppUtils.STORAGE_ACCOUNT_REFRESH_TOKEN), { headers: headers })
    .catch((e: any) => Observable.throw(this.errorHandler(e)))


  }

 errorHandler(error: any): void {
   console.log("auth error")
   Observable.throw("");

  }


 logoutUser(){
       localStorage.clear();
   this.router.navigate([AppUtils.BACKEND_API_AUTHENTICATE_PATH]);
  // window.location.reload();
   //return Observable.throw("");
 }
}


}
georgeawg
  • 48,608
  • 13
  • 72
  • 95
user630209
  • 1,123
  • 4
  • 42
  • 93

1 Answers1

0

This works for me. Check if this helps. Basically it's a wrapper on Angular Http service.

callHttpGet(url: string) {
  let headers = new Headers({ 
          "Content-Type": "application/json", 
          "Authorization": "Bearer " + 
          localStorage.getItem("accessToken") 
  });
  let options = new RequestOptions({ headers: headers });

  var me = this;
  return this.http.get(url, options)
    .catch(initialError => {
      if (initialError && 
            initialError.status === 401 && 
            initialError.json()["message"] === "Token expired") {
        // token might be expired, try to refresh token
        var tokenObject = new Object();
        tokenObject["token"] = localStorage.getItem("refreshToken");
        return me.authenticatorService.authenticate(tokenObject)
          .flatMap(res => {
            return me.http.get(url, me.options);
          })
          .catch(error => {
            if (error && 
                    error.status === 401 && 
                    (error.json()["message"] === "Refresh token expired" || 
                    error.json()["message"] === "Token expired")) {
              localStorage.clear();
              this.router.navigate(['/login']);
              return Observable.throw(error);
            }
            else {
              return Observable.throw(error);
            }
          });
      }
      else {
        return Observable.throw(initialError);
      }
    });
}
Saptarshi Basu
  • 8,640
  • 4
  • 39
  • 58