I am trying to implement refresh tokens on my Angular2 app. I am taking an optimistic approach and instead of checking if the access token expired before making the request, I am making the request and if it returns a 401 code, I will refresh the access token by requesting a new one and saving it to local storage.
Here is my code snippet:
getWithParams<T>(serviceUrl: string, params: URLSearchParams): Observable<T> {
return super.getWithParams<T>(serviceUrl, params)
.retryWhen((error) => {
return error
.filter((e) => e.status === 401)
.scan((acc, value) => {
return acc + 1;
}, 0)
.takeWhile(acc => acc < 3)
.flatMap(() => this.tokenRefreshService.refreshToken())
.delay(1000);
});
}
It is important to mention that super.getWithParams setsthe access token on the request headers by retrieving it from the local storage.
The method call tokenRefreshService.refreshToken() gets a new access token and saves it to local storage.
The problem that I am facing is that when the request is retried it is using the old access token, that is, it is no calling super.getWithParams to rebuild the request again. It just retries the exsiting observable.
Is there a way of building the request again? or chainging the request header of the observable that failed?
Actually retryWhen()
resubscribes to its source so you can use it to your advantage. This example should simulate your situation:
let token = 'token';
let counter = 0;
const source$ = Rx.Observable.defer(() => {
console.log('Observable.defer(), token: ' + token);
return Rx.Observable.of(token);
})
.map(token => {
if (counter++ < 3) {
throw new Error('invalid token');
}
return token;
})
.retryWhen((error) => {
return error
.filter(() => true) // or whatever...
.do(() => token = token + 'bla'); // update the token
})
.map(token => { // create the request
return "I'm a request with token: " + token;
});
source$.subscribe(
res => console.log(res),
err => console.log('error: ' + err),
() => console.log('complete')
);
See live demo: https://jsbin.com/roduqi/5/edit?js,console
This three times throws an error with invalid token and updates it every time.
Notice, that on every error I'm creating a new source Observable with Observable.defer
.
This example print to console:
Observable.defer(), token: token
Observable.defer(), token: tokenbla
Observable.defer(), token: tokenblabla
Observable.defer(), token: tokenblablabla
I'm a request with token: tokenblablabla
complete
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With