Hi I am trying to check when a token is about to expire and reload it with the access token. The main problem is that to reload it I have to make a http call to get the new token and then I will re save it in the session storage. The main problem is that because the http is async it reloads the token, but because I dont know how to wait until this call is finish to retry the original call with the new token it fail.
This is the code:
AuthService: This is the service that is being called to update the token
async reloadIdTokenFromAccess(accessToken : string | null) {
console.log("Performing call to reload token");
const resp = await this.httpClient.post<IdTokenDTO>(`${environment.backendUrl}/auth/tokenReload`,accessToken).toPromise();
this.setIdToken(resp.idToken);
this.setExpireAt(resp.expireAt);
console.log("Data update in session storage")
}
interceptor
@Injectable()
export class HttpTokenInterceptor implements HttpInterceptor {
constructor(private authServ : AuthService) {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
let token: string | null = this.authServ.getIdToken();
let expiration: number | null = this.authServ.getExpireAt();
let reloaded : boolean = false;
var currentTime = new Date().getTime();
if(expiration != null && !request.url.includes("tokenReload") && !request.url.includes("login")){
if(expiration <= currentTime + 60000 && this.authServ.getAccessToken() != null){
reloaded = true;
console.log("Token expiration Time near to expire, reloading token")
this.authServ.reloadIdTokenFromAccess(this.authServ.getAccessToken());
token = this.authServ.getIdToken();
console.log("Finish all full reloading")
}
}
if(reloaded){
console.log("Starting the request with the new token")
}
let originalReq = request;
if(token){
request = originalReq.clone({
setHeaders: {
authorization: `Bearer ${ token }`,
'Content-Type': 'application/json'
}
});
}
return next.handle(request);
}
}
If I check the logs I see the following:
As you can check the request is being performen before the token is reloaded. How can i force this request to be finish before continue with the method?
Just something similar like in java a CompletableFuture with the get() method to block the code until it finish
I also have test things like this without success
async reloadIdTokenFromAccess(accessToken : string | null) {
console.log("Performing call to reload token");
await this.httpClient.post<IdTokenDTO>(`${environment.backendUrl}/auth/tokenReload`,accessToken).subscribe( (resp : IdTokenDTO) => {
this.setIdToken(resp.idToken);
this.setExpireAt(resp.expireAt);
console.log("updates token in session storage")
})
}
import { Injectable } from '@angular/core';
import {HttpRequest,HttpHandler,HttpEvent,HttpInterceptor} from '@angular/common/http';
import { from, Observable, Subscription } from 'rxjs';
import { AuthService } from '../services/auth.service';
@Injectable()
export class HttpTokenInterceptor implements HttpInterceptor {
constructor(private authServ : AuthService) {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
return from(this.handle(request, next));
}
async handle(request: HttpRequest<any>, next: HttpHandler) {
const expiration: number | null = this.authServ.getExpireAt();
const currentTime = new Date().getTime();
let reloaded: boolean = false;
if (expiration != null && !request.url.includes('tokenReload') &&!request.url.includes('login')) {
if (expiration <= currentTime + 60000 && this.authServ.getAccessToken() != null) {
reloaded = true;
console.log('Token expiration Time near to expire, reloading token');
await this.authServ.reloadIdTokenFromAccess(this.authServ.getAccessToken());
console.log('Finish of call reaload token');
}
}
const token = this.authServ.getIdToken();
if (reloaded){
console.log('Starting the request with the new token');
}
const originalReq = request;
if (token) {
request = originalReq.clone({
setHeaders: {
authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
}
return next.handle(request).toPromise();
}
}
I am desesperate. dont know what to do (maybe continue studing courses and tutorials but i have dedlines) :(
Thanks in advance.
In your authService
, return the http response using Observables
reloadIdTokenFromAccess(accessToken : string | null): Observable<IdTokenDTO> {
console.log("Performing call to reload token");
return this.httpClient.post<IdTokenDTO>(`${environment.backendUrl}/auth/tokenReload`,accessToken);
}
And, in your interceptor, you would proceed like this:
intercept(
request: HttpRequest<unknown>,
next: HttpHandler
): Observable<HttpEvent<unknown>> {
const expiration: number | null = this.authServ.getExpireAt();
const currentTime = new Date().getTime();
let reloaded: boolean = false;
if (
expiration != null &&
!request.url.includes('tokenReload') &&
!request.url.includes('login')
) {
if (
expiration <= currentTime + 60000 &&
this.authServ.getAccessToken() != null
) {
reloaded = true;
console.log('Token expiration Time near to expire, reloading token');
// unwrap your response from the observable
this.authServ
.reloadIdTokenFromAccess(this.authServ.getAccessToken())
.pipe(
tap((response: any) => {
this.authServ.setIdToken(response.idToken);
this.authServ.setExpireAt(response.expireAt);
console.log('Data update in session storage');
})
)
.subscribe(() => this.addToken(request, reloaded));
}
}
const req = this.addToken(request, reloaded);
return next.handle(req);
}
private addToken(request: HttpRequest<unknown>, reloaded: boolean) {
const token = this.authServ.getIdToken();
console.log('Finish all full reloading');
if (reloaded) console.log('Starting the request with the new token');
const originalReq = request;
if (token) {
request = originalReq.clone({
setHeaders: {
authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
}
return request;
}
You should use higher-order observables. Here's a nice read https://blogs.msmvps.com/deborahk/higher-order-observable/
Try doing the following:
reloadIdTokenFromAccess(accessToken : string | null) : Observable<IdTokenDTO> {
console.log("Performing call to reload token");
return this.httpClient.post<IdTokenDTO>(`${environment.backendUrl}/auth/tokenReload`,accessToken)
.tap( (resp : IdTokenDTO) => {
this.setIdToken(resp.idToken);
this.setExpireAt(resp.expireAt);
console.log("updates token in session storage")
});
}
tap
will just intercept and save the token everytime you reload and it will return the observable which can then be combined with another observable.
@Injectable()
export class HttpTokenInterceptor implements HttpInterceptor {
constructor(private authServ : AuthService) {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
const expiration: number | null = this.authServ.getExpireAt();
const currentTime = new Date().getTime();
let reloaded: boolean = false;
if (expiration != null && !request.url.includes('tokenReload') &&!request.url.includes('login')) {
if (expiration <= currentTime + 60000 && this.authServ.getAccessToken() != null) {
console.log('Token expiration Time near to expire, reloading token');
return this.reloadIdTokenFromAccess(this.authServ.getAccessToken()).pipe(
concatMap(token => {
request = request.clone({
setHeaders: {
authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
}
});
return next.handle(request);
})
)
console.log('Finish of call reaload token');
}
}
return next.handle(request);
}
}
concatMap
will wait for the previous HTTP Observable to complete before mapping the new value to an HTTP Observable
I am finally using a timer that every X minutes check the token. Silent reload. I put this timer in the main component of the application and thats it.
Thanks anyway.
export class MainComponent implements OnInit, OnDestroy{
...
public subscription : Subscription;
ngOnInit(): void {
this.subscription = timer(0, (300 *1000)).pipe().subscribe(() => {
//console.log("ejecutando recarga del token");
const expiration: number | null = this.authServ.getExpireAt();
const currentTime = new Date().getTime();
let reloaded: boolean = false;
if (expiration != null) {
if (expiration <= currentTime + (350 * 1000) && this.authServ.getAccessToken() != null) {
reloaded = true;
//console.log('Token expiration Time near to expire, reloading token');
this.authServ.reloadIdTokenFromAccess(this.authServ.getAccessToken());
}
}
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.