5

I am using Angular 2 with TypeScript on the front-end. I am trying to implement a http interceptor that is setting the authorization header on each request. If the access token expires I am trying to retry the request, get a new access token with the refresh token and change the header of the current request, before the retry.

How to update the request header in the retryWhen operator?

For example here is the HttpInterceptor:

export class HttpInterceptor extends Http { get(url: string, options?: RequestOptionsArgs): Observable<Response> { return super.get(url, this.setRequestAuthorizationHeader(options)).retryWhen((errors: any) => this.errorHandler(errors)); } private setRequestAuthorizationHeader(options?: RequestOptionsArgs): RequestOptionsArgs { // some checks // get accessToken from localStorage options.headers.append('Authorization', 'Bearer ' + accessToken); } private errorHandler(errors) { return errors.switchMap((err) => { if (err.status === 401) { let closedSubject = new Subject(); this.authenticationService.refreshToken() .subscribe(data => { // How to update authorization header? This doesn't work. this.defaultOptions.headers.append('Authorization', 'Bearer ' + data.accessToken); closedSubject.next(); }); return <any>closedSubject; } else { return Observable.throw(err.json()); } }); } } 
8
  • Beware that using an external service to refresh the token (I guess authenticationService uses Http too), you will end up in circular dependency HttpInterceptor needs authenticationService that needs HttpInterceptor. Commented Apr 7, 2017 at 12:33
  • 1
    Thanks, n00dl3. I am loading the Http in authenticationService dynamicly with the injector. Commented Apr 7, 2017 at 12:41
  • could you add the authentication service code ? Commented Apr 7, 2017 at 13:42
  • I would use catch instead of retryWhen, the later one will replay the same Observable... Commented Apr 7, 2017 at 13:52
  • 1
    If you have 4 parallel requests, and all of them have expired token, then all of them will run refreshToken(). Right? This is very confusing because 4 requests will make 4 refreshToken with the same token. 1 will return the right token and the other 3 requests will fail. Right? Am I wrong? What is the solution? Commented Jul 18, 2017 at 12:55

1 Answer 1

3

I would use catch instead of retryWhen as the latter one replay the same observable, and the parameters have already been set.

BTW, your Subject is useless in errorHanlder :

export class HttpInterceptor extends Http { get(url: string, options ? : RequestOptionsArgs): Observable < Response > { return super.get(url, this.setRequestAuthorizationHeader(options)).catch(errors => this.errorHandler(errors, url, options)) }); } private setRequestAuthorizationHeader(options ? : RequestOptionsArgs): RequestOptionsArgs { // some checks // get accessToken from localStorage options.headers.append('Authorization', 'Bearer ' + accessToken); return options } private errorHandler(err: any, url: string, options ? : RequestOptionsArgs) { if (err.status === 401) { return this.authenticationService.refreshToken() .switchMap(data => { // save accessToken to localStorage return super.get(url, this.setRequestAuthorizationHeader(options)); }); } return Observable.throw(err.json()); } 

Also note that using state like this.defaultOptions is probably not your best choice, using anobservable would be more appropriate.

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

7 Comments

How do you get url and options in the errorHandler? If I make a new request with the url and options with the new auth header I will lose the subscribers from failed request that I want to be executed after the request is done. That's why I am trying to do it with retryWhen.
" If I make a new request with the url and options with the new auth header I will lose the subscribers that I want to be executed after the request is done." ---> definitely not .
for url and option you can pass them in the parameters. I edited.
Okay... So the returned observable in the errorHandler will combine with the old observable and the data from the new request will be pushed to the subscribers to failed request? Am I right?
that's it, if catch is reached, observers will get data from the observable it returns.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.