I already have implemented logic of token saving, retrieving and I have refreshing call also. The problem is that when I intercept 403 in my HttpInterceptor, other calls that are made at the same time, also refresh the token. I would love to hold those calls until my token is refreshed. To create what I would call a 'semaphore' of requests.
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
private auth: AuthService;
constructor(private injector: Injector) {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.auth = this.injector.get(AuthService);
if(this.auth.isAuthenticated()){
request = request.clone({
setHeaders: {
Accept: 'application/json',
Authorization: `Bearer ${localStorage.getItem('access_token')}`
}
});
} else {
request = request.clone({
setHeaders: {
Accept: 'application/json'
}
});
}
return next.handle(request).catch(error => {
if (error.status === 401) {
console.log('refreshing token');
// TODO: return Refresh Token here and hold other calls
}
return Observable.throw(error);
});
}
JWT (JSON Web Token) It may also have a validity period. Once this validity period has elapsed, the server will no longer allow access to resources with this token. In this step, the user will have to get a new access token by reauthentication or with some additional method: refresh token.
Since access tokens aren't valid for an extended period because of security reasons, a refresh token helps to re-authenticate a user without login credentials. This Refresh token is never exposed to the client-side Javascript, even if our access token gets compromised it'll be expired in a very short duration.
The JWT access token is only valid for a finite period of time. Using an expired JWT will cause operations to fail. As you saw above, we are told how long a token is valid through expires_in . This value is normally 1200 seconds or 20 minutes.
– When the Access Token is expired, Angular automatically sends Refresh Token request, receives new Access Token and uses it for new request. So the server still accepts resource access from the user. – After a period of time, the new Access Token is expired again, and the Refresh Token too.
Well, I'm not able to setup an enviroment to test if this logic works properly, but I tried my best:
Your interceptor should be something like:
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
private auth: AuthService;
constructor(private injector: Injector) {
this.auth = this.injector.get(AuthService);
}
setHeaders(request) {
return function (token) {
return request.clone({
setHeaders: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
});
}
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.auth
.getToken()
.map(this.setHeaders(request))
.mergeMap(next.handle)
.catch(error => {
if (error.status === 401) {
return this.auth.refreshToken()
.map(this.setHeaders(request))
.mergeMap(next.handle);
}
return Observable.throw(error);
});
}
}
And your AuthService:
@Injectable()
export class AuthService {
private refreshTokenCall;
saveTokenInLocalStorage(token) {
localStorage.setItem('access_token', token);
}
getToken() {
if (localStorage.getItem('access_token')) {
return Observable.of(localStorage.getItem('access_token'));
}
return this.refreshToken();
}
refreshToken() {
if (!this.refreshTokenCall) {
this.refreshTokenCall = this.http.get(refreshTokenURL)
// Maybe a .map() here, it depends how the backend returns the token
.do(this.saveTokenInLocalStorage)
.finally(() => this.refreshTokenCall = null);
}
return this.refreshTokenCall;
}
}
I hope it helps you somehow.
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