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 ]
})
Inside your
updateToken
method you are callingsuper.post
which would be equivalent toHttp.prototype.post.apply(this...)
, andsuper.post
will call internallythis.request()
.The
this
context being your customHttpService
, it ends up in a recusrsive call of theHttpService
request
method. You should callsuper.request
instead :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.