how to refresh the access token using custom http in angular 2?

Ali picture Ali · Mar 7, 2017 · Viewed 7.2k times · Source

I am using token based authentication in my application. My backend is developed using restful service(spring).The backend code is very well generating the required the access token and refresh tokens with timelines, So I have overidden the http class with following:

export class customHttp extends Http {
   headers: Headers = new Headers({ 'Something': 'Something' });
    options1: RequestOptions = new RequestOptions({ headers: this.headers });
    private refreshTokenUrl = AppSettings.REFRESH_TOKEN_URL;
    constructor(backend: ConnectionBackend,
        defaultOptions: RequestOptions,private refresh:OauthTokenService) {
        super(backend, defaultOptions);
    }
    request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
    console.log("custom http ");
        return super.request(url, options)
            .catch((err) => {
                if (err.status === 401) {
                    console.log(" custome http 401 ");
                    //   refresh the token
                    this.refresh.refresh().subscribe((tokenObj)=>{
                              console.log("tokenobj ");
                    })
                 } else {
                    console.log("err " + err);
                }
            }); } } 

I am getting stuck in refreshing the token at refresh() method as I am getting cyclic dependency error so I tried to use refresh service in another module but no luck. I am using the same approach as mentioned in this Handling refresh tokens using rxjs Any help would be great!

Answer

Dragonfly picture Dragonfly · Mar 9, 2017

This is what worked for me:

 request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
    //adding access token to each http request before calling super(..,..)
    let token = this.authenticationService.token;
    if (typeof url === 'string') {
        if (!options) {
            options = { headers: new Headers() };
        }
        options.headers.set('Authorization', `Bearer ${token}`);
    }
    else {
        url.headers.set('Authorization', `Bearer ${token}`);
    }
    return super.request(url, options)
      .catch((error) => {
            //if got authorization error - try to update access token
            if (error.status = 401) {
                return this.authenticationService.updateToken()
                    .flatMap((result: boolean) => {
                        //if got new access token - retry request
                        if (result) {
                            return this.request(url, options);
                        }
                        //otherwise - throw error
                        else {
                            return Observable.throw(new Error('Can\'t refresh the token'));
                        }

                    })
            }
            else {
                Observable.throw(error);
            }
        })
}

UPDATE: authenticationService.updateToken() implementation should depend on authorization provider/authorization mechanism you use. In my case it is OAuth Athorization Server, so implementation basically sends post request with refresh token in the body to configured token url and returns updated access and refresh tokens. tokenEndPointUrl is configured by OAuth and issues access and refresh tokens (depending on grant_type sent). Because i need to refresh token i set grant_type to refresh_token. Code looks similar to:

updateToken(): Observable<boolean> {
    let body: string = 'refresh_token=' + this.refreshToken + '&grant_type=refresh_token';

    return this.http.post(tokenEndPointUrl, body, this.options)
        .map((response: Response) => {
            var returnedBody: any = response.json();
            if (typeof returnedBody.access_token !== 'undefined'){
              localStorage.setItem(this.tokenKey, returnedBody.access_token);
              localStorage.setItem(this.refreshTokenKey, returnedBody.refresh_token);
            return true;
        }
        else {
            return false;
        }
        })
}

Hope it helps