Dealing with the 401 response from an interceptor using the HttpClientModule in Angular and JWT authentication can be a bit tricky. When the accessToken expires, it's necessary to use the refreshToken to obtain a new one before making the actual API request. The goal is to pause the request, fetch a new token, and then proceed with the request using the updated accessToken.
What is the most effective way to handle HTTP requests from within the interceptor?
Here is an example of my interceptor:
@Injectable()
export class JwtService implements HttpInterceptor {
constructor(public inj: Injector){}
intercept(req : HttpRequest<any>, next : HttpHandler) : Observable<HttpEvent<any>> {
if ( this.shouldGenerateNewAccessToken(req.url) ){
const auth = this.inj.get(AuthService);
auth.getNewAccessToken().then( token => {
if (token){
const headers = { 'Authorization' : token };
const clone = req.clone( { setHeaders : headers } );
return next.handle(clone);
} else { return next.handle(req); }
}, error => {
return next.handle(req);
});
}
else {
if (APP_CONFIG['accessToken']){
const headers = { 'Authorization' : APP_CONFIG['accessToken'] };
const clone = req.clone( { setHeaders : headers });
return next.handle(clone);
} else {
return next.handle(req);
}
}
}
shouldGenerateNewAccessToken(url : string) : Boolean {
let lastupdate = APP_CONFIG['accessTokenTimestamp'];
let now = new Date().getTime();
// Let say the token expires after 5s
if ((now - lastupdate) > 5000 && APP_CONFIG['refreshToken'] && url != APP_CONFIG['apiEndPont']+'getaccesstoken'){
return true;
}
else
return false;
}
Authentication logic
getNewAccessToken() : Promise<any>{
return new Promise( (resolve, reject)=>{
this.http.post(this.api+ 'getaccesstoken', JSON.stringify({refreshToken: APP_CONFIG['refreshToken'] }), { "headers" : this.headers } ).subscribe( data => {
let res : any = data;
APP_CONFIG['accessToken'] = res.accessToken;
APP_CONFIG['accessTokenTimestamp'] = new Date().getTime();
resolve(APP_CONFIG['accessToken']);
}, err => {console.log('error'); reject(null); })
});
}
getuserinfos(){
return this.http.get(this.api+ 'getuserinfos', { "headers" : this.headers } ).subscribe( data => {
console.log('result getUserInfos =>', data);
},
( err : HttpErrorResponse ) => {
if ( err.error instanceof Error ) { console.log('An error occurred requete login:', err.error.message); }
else {
console.log('error => ', err)
}
});
I encountered the following error when calling getUserInfos() with an expired token:
error => TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
Could this issue be related to the described behavior?
On rare occasions, an interceptor may choose to entirely manage the request itself and generate a new event stream rather than invoking next.handle(). While this is fine, keep in mind that subsequent interceptors will be bypassed completely. It is also uncommon but permissible for an interceptor to yield multiple responses on the event stream for a single request. Source