0

The case is when calling a service in the back-end and if I get error code indicates that it’s expired then in front-end First : request a valid jwt token. Second: rerequest the original http request after getting a valid token. The first step is done successfully but it’s not the case in the second one. This is the code in the interceptor

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {


    if (!request.url.endsWith('login') && !request.url.endsWith('refreshtoken')) {
      request = this.addAuthenticationToken(request);
    }
    return next.handle(request).pipe(catchError(err => {
      Log.log("Error Status: " + err.status);
      // invalid token or bad request
      if (err.status == this.NOT_VALID_TOKEN_OR_BAD_REQUEST_ERROR_CODE) {
        this.authenticationService.logOut();
        return EMPTY;
      }
      else if (err.status == this.TOKEN_EXPIRED_ERROR_CODE) { // token is expired
        this. doRefreshToken(request, next);
      }
    }
    ));
  }

doRefreshToken(request, next) {
    return this.authenticationService.refreshToken().subscribe((resp: HttpResponse<any>) => {
      Log.log("in subscripe refresh token")
      Log.log(resp.headers.get(this.AUTH_HEADER));
      StorageManagementUtil.setLocaltStorage(resp.headers.get(this.AUTH_HEADER), <PortalRsponseTransaction>resp.body);
    },
      (error) => { Log.log(error) },
      () => {
        Log.log("on complete()")
        request = this.addAuthenticationToken(request);
        return next.handle(request);
      });
  }

And this is refresh token service

refreshToken() {
    let map = new TSMap();
    map.set(this.RQUEST_BODY_KEY_SESSION_TOKEN, StorageManagementUtil.readFromLocalStorage(StorageManagementUtil.SESSION_TOKEN));
    var requsetBody = JSON.stringify(map.toJSON());
    let request = new PortalRequestTransaction(requsetBody);
    return this.http.post<PortalRsponseTransaction>(fullURL, request, {
      observe: 'response',
      responseType: 'json'
    });
  }

And this is a screenshot from network tap while inspecting

https://i.ibb.co/vqLTLh2/1.png

The question is why recalling the original service is not done after getting refresh token? And why calling service is done twice? (if we ignore the ones of OPTIONS request-type).

I'm beginner in angular so I wish I could provide sufficient information to figure out the problem.

jsmin
  • 372
  • 2
  • 12

2 Answers2

0

1) 401 means unauthorized, it seems you are not sending the correct authorization header( Bearer {JWT-Token} ) in the request.

2) For refresh token, better use rxjs-retry , which will help you to handle refresh logic either on error or on expiry

Code follows:

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {


    if (!request.url.endsWith('login') && !request.url.endsWith('refreshtoken')) {
      request = this.addAuthenticationToken(request);
    }
    return next.handle(request).pipe(
    retryWhen(err => {
     if (err.status == this.TOKEN_EXPIRED_ERROR_CODE) { // token is expired
       return this. doRefreshToken(request, next);
      }
    }),
    catchError(err => {
      Log.log("Error Status: " + err.status);
      // invalid token or bad request
      if (err.status == this.NOT_VALID_TOKEN_OR_BAD_REQUEST_ERROR_CODE) {
        this.authenticationService.logOut();
        return EMPTY;
      }
      else if (err.status == this.TOKEN_EXPIRED_ERROR_CODE) { // token is expired
        this. doRefreshToken(request, next); // return an observable
      }
    }
    ));
  }

The above code snippet, will hit retry if there is an error, it will first refresh token, return an observable, then calls again the getAll.

Ankii32
  • 106
  • 3
  • 1) I return 401 from back-end to indicate that the token is expired.The error status code will not affect the behavior of the code. 2) I need to retry after request refreshtoken and add the new valid one to the header of the original request, so I don't think rxjs-retry will be useful to achieve that. – jsmin Jun 15 '19 at 13:29
  • Thanks for your help, I tried your solution and for some reason it runs nonstop. I've post the code which work in my case. – jsmin Jun 16 '19 at 21:48
0

This solution is inspired by this answer using switchMap

 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!request.url.endsWith('login') && !request.url.endsWith('refreshtoken')) {
      request = this.addAuthenticationToken(request);
    }
    return next.handle(request).pipe(
      catchError((err) => {
        // invalid token or bad request
        if (err.status == this.NOT_VALID_TOKEN_OR_BAD_REQUEST_ERROR_CODE) {
          this.authenticationService.logOut();
          return EMPTY;
        }
        else if (err.status == this.TOKEN_EXPIRED_ERROR_CODE) {
          return this.handleRefreshToken(request, next);
        }
      })
    );
  }


  handleRefreshToken(request: HttpRequest<any>, next: HttpHandler) {

    return this.authenticationService.refreshToken().pipe(
      switchMap((tokenResp) => {
        StorageManagementUtil.setLocaltStorage(tokenResp.headers.get(this.AUTH_HEADER), <PortalRsponseTransaction>tokenResp.body);
        request = this.addAuthenticationToken(request);
        return next.handle(request);
      }),
      catchError(error => {
        if (error.status == this.NOT_VALID_TOKEN_OR_BAD_REQUEST_ERROR_CODE) {
          this.authenticationService.logOut();
          return EMPTY;
        }
      })
    );
  }
jsmin
  • 372
  • 2
  • 12