As the title says, I am working on an Angular 4 project with OAuth authentication.
Whenever an http request responds with status code 401 I am intercepting the request, renewing the access token and retrying the failed request.
When I receive a 401, the request is correctly intercepted and the access token gets refreshed just as it should. The failed request also gets executed again, but doesn't deliver it's response to the component any longer.
So the problem is that my component, who should observe on the request response complains with the log 'Error receiving the properties' for the view before the refresh of the token and the retry of the request have taken place.
My interceptor:
import { Injectable, Inject, Injector } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpResponse,
HttpErrorResponse,
HttpEvent,
HttpInterceptor,
HttpSentEvent,
HttpHeaderResponse,
HttpProgressEvent,
HttpUserEvent
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { AuthService } from './auth.service';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { TokenManager } from '../../../util/TokenManager';
import { AuthUserResponse } from '../../../models/authUserResponse';
import 'rxjs/add/operator/switchMap';
@Injectable()
export class AuthTokenExpiredInterceptor implements HttpInterceptor {
isRefreshingToken: boolean = false;
tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);
constructor( private injector: Injector, private tokenManager: TokenManager ) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
return next.handle(this.addNewAccessTokenToHeaders(request, this.tokenManager.retrieveAccessToken()))
.do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
console.log('processing response', event);
}
return event;
},(err) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
console.log('Access_token possibly expired, trying to retrieve a new one!')
return this.handle401Error(request, next);
} else if (err.status === 400) {
console.log('Refresh_token possibly expired, redirecting to login');
return this.handle400Error(err);
}
} else {
return Observable.throw(err);
}
});
}
handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshingToken) {
console.log('in if');
this.isRefreshingToken = true;
// Reset here so that the following requests wait until the token comes back from the refreshToken call.
this.tokenSubject.next(null);
console.log('About to call renewAccessToken');
return this.injector.get(AuthService).renewAccessToken().subscribe((response) => {
let newToken = response.access_token;
if (newToken) {
console.log('Got the new access_token!');
this.tokenSubject.next(newToken);
let requestToRetry = this.addNewAccessTokenToHeaders(request, newToken);
console.log('The retried request header: ' + requestToRetry.headers.get("Authorization"));
return next.handle(requestToRetry);
} else { // No token in response
this.injector.get(AuthService).logout();
}
},
(err) => {
this.injector.get(AuthService).logout();
return Observable.throw(err)
},
() => {
console.log('handle401Error done');
this.isRefreshingToken = false;
})
} else {
console.log('In else');
return this.tokenSubject
.filter(token => token != null)
.take(1)
.switchMap(token => {
return next.handle(this.addNewAccessTokenToHeaders(request, token));
});
}
}
handle400Error(error: HttpErrorResponse) {
if (error && error.status === 400 && error.error && error.error.error === 'invalid_grant') {
this.injector.get(AuthService).logout();
}
return Observable.throw(error);
}
addNewAccessTokenToHeaders(req: HttpRequest<any>, token: string): HttpRequest<any> {
console.log('Adding the access_token to the Authorization header');
return req.clone({ setHeaders: {
Authorization: 'Bearer ' + token
}})
}
}
My Service returns an Observable
and my component:
ngOnInit(){
this.getProperties();
}
getProperties() {
this.propertyService.getProperties().subscribe(
result => {
this.properties = result;
console.log('Received response in Properties component: ' + JSON.stringify(result));
}, error => {
console.log('Error receiving the properties for the view')
},
() => { console.log('Received the properties, now they can be displayed in the view') })
}
Your function intercept must return always a Observable < HttpEvent < any > >. Your code is a bit "bizarro". The main problem I see is that you use "do" to catch the error. "do" not modify the request.
I have a intercept in this way (I hope the code can help you)