1

I want to refresh the token before the custom http request if it is expired. I try my code when I'm sure that the token is expired but it gives the following console result:

Token refresh is required  app.js:1:92855
updateToken() method inside  app.js:1:93301
tokenNotExpired?: false  app.js:1:92674
Token refresh is required  app.js:1:92855
updateToken() method inside  app.js:1:93301
tokenNotExpired?: false  app.js:1:92674
Token refresh is required  app.js:1:92855
updateToken() method inside  app.js:1:93301
tokenNotExpired?: false  app.js:1:92674
Token refresh is required  app.js:1:92855
updateToken() method inside  app.js:1:93301
tokenNotExpired?: false  app.js:1:92674
............ lots of the same sentences and finally exception:

EXCEPTION: Uncaught (in promise): Error: Error in :0:0 caused by: too much recursion
k@http://localhost/xxx/node_modules/zone.js/dist/zone.min.js:1:11750
............

As I understand during refreshing the token it goes into an infinite loop. I've tested the updateToken() method somewhere else with a button and it works fine.

What am I doing wrong?

custom http service

import { Injectable } from '@angular/core';
import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers } from '@angular/http';
import { tokenNotExpired } from "angular2-jwt";
import { Observable } from "rxjs/Observable";

@Injectable()
export class HttpService extends Http {

    constructor (backend: XHRBackend, options: RequestOptions) {
        let token = localStorage.getItem('access_token'); // your custom token getter function here
        options.headers.set('Authorization', `Bearer ${token}`);
        super(backend, options);
    }

    request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
        let token = localStorage.getItem('access_token');

        if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
            if (!options) {
                // let's make option object
                options = {headers: new Headers()};
            }
            options.headers.set('Authorization', `Bearer ${token}`);
        } else { // we have to add the token to the url object
            url.headers.set('Authorization', `Bearer ${token}`);
        }

        console.log("tokenNotExpired?: " + tokenNotExpired('access_token'));

        if(tokenNotExpired('access_token')){ // if token is NOT expired

            return super.request(url, options).catch(this.catchAuthError(this));

        }else{  // if token is expired
            console.log("Token refresh is required");
            return this.updateToken()
                .flatMap((result: boolean) => {
                    console.log("updateToken result");
                    console.log(result);
                    if (result) {
                        return super.request(url, options).catch(this.catchAuthError(this));
                    } else {
                        return Observable.throw(new Error('Can\'t refresh the token'));
                    }

                });
        }
    }

    updateToken(): Observable<any> {

        console.log("updateToken() method inside");

        let body: string = 'refresh_token=' + localStorage.getItem('refresh_token') + '&client_id=' + localStorage.getItem('resource')  + '&grant_type=refresh_token';

        return super.post(
            localStorage.getItem("authUrl"),
            body,
            new RequestOptions({headers: new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' })})
        )
            .map((response: Response) => {
                let returnedBody: any = response.json();
                console.log("post returnedBody");
                console.log(returnedBody);
                if (typeof returnedBody.access_token !== 'undefined'){
                    localStorage.setItem('access_token', returnedBody.access_token);
                    localStorage.setItem('refresh_token', returnedBody.refresh_token);

                    console.log("Token Refreshed: refreshed access_token");
                    console.log(localStorage.getItem('access_token'));

                    return true;
                }
                else {
                    return false;
                }
            });
    }

    private catchAuthError (self: HttpService) {
        return (res: Response) => {
            console.log(res);
            return Observable.throw(res);
        };
    }
}

app module

@NgModule({
  imports: [ .......... ],
  declarations: [ ....... ],
  providers: [
    {
      provide: HttpService,
      useFactory: (backend: XHRBackend, options: RequestOptions) => {
        return new HttpService(backend, options);
      },
      deps: [XHRBackend, RequestOptions]
    }

  ],
  bootstrap: [ Application ]
})
4
  • super.post inside updateToken() will call this.request internally... this being your custom HttpService. Commented May 15, 2017 at 14:43
  • @n00dl3 Can I call the post without involving the HttpService then? Commented May 15, 2017 at 14:48
  • nope, you should call super.request instead. (if it does not call any overriden method, of course). Commented May 15, 2017 at 14:49
  • yes, that's not the same signature... check my answer.... Commented May 15, 2017 at 15:06

1 Answer 1

2

Inside your updateToken method you are calling super.post which would be equivalent to Http.prototype.post.apply(this...), and super.post will call internally this.request().

The this context being your custom HttpService, it ends up in a recusrsive call of the HttpService request method. You should call super.request instead :

return super.request(
    new Request({
      method: RequestMethod.Post,
      url: localStorage.getItem("authUrl"),
      body,
      headers: new Headers({
        'Content-Type': 'application/x-www-form-urlencoded'
      })
    })

  )
  .map((response: Response) => {
    let returnedBody: any = response.json();
    console.log("post returnedBody");
    console.log(returnedBody);
    if (typeof returnedBody.access_token !== 'undefined') {
      localStorage.setItem('access_token', returnedBody.access_token);
      localStorage.setItem('refresh_token', returnedBody.refresh_token);

      console.log("Token Refreshed: refreshed access_token");
      console.log(localStorage.getItem('access_token'));

      return true;
    } else {
      return false;
    }
  });

Also note that it might not be the best idea to create a custom Http service.

But maybe you could create a service that gets http injected, because you probably won't need to be authenticated just to fetch some simple static data from an ajax call.

That would also avoid the problem you encountered with recursive call stack exceeded.

@Injectable()
export class MyAuhtHttpService{

  constructor(private http:Http){}

}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.