I use an Angular service currently for all of my backend API calls. These API calls must send a token as part of their Authorization header. In order to verify I have a valid accessToken I need to check it and possibly refresh it if it has expired.
I am using the latest Amplify / Congito for my authentication.
Based on what I have search so far the best way to get the token as well as refresh the accessToken if expired is to run the following:
async handleToken () {
try {
const cognitoUser = await Auth.currentAuthenticatedUser();
const currentSession = cognitoUser.signInUserSession;
cognitoUser.refreshSession(currentSession.refreshToken, (err, session) => {
return session.accessToken.jwtToken
});
} catch (e) {
}
}
This will return the current or newly refreshed accessToken to send.
My issue is I do not know how I can make sure to call this before every API call to make sure I send the latest token.
After searching more I found that this cannot be achieved via OnInit or in the constructor.
Here is an example of an API call.
getCustomerList(): Observable<CustomerList[]> {
this.handleToken();
const url = hosturl +'c_l';
const httpOptions = {
headers: new HttpHeaders({
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
'Authorization': ls || '{}'
}),
withCredentials: true,
params: {
'clientid': clientid
}
};
return this._http.get(url, httpOptions)
.pipe(
map((res) => {
console.log(res);
return <CustomerList[]> res;
}),
catchError(err => throwError(this.handleError(err))),
);
}
Note this will run the handleToken correctly, but it is not async due to the function calling it not using await.
Any advice would be appreciated.
I have no idea why you need to call this function on every API call, instead of storing the token in the localStorage and getting it from there to inject it into every API call. Anyway, you can create your own interceptor (that inspects and transform HTTP requests from your application to a server), then inject the token into the requests' header like the following:
// import { from, Observable } from 'rxjs';
// import { switchMap } from 'rxjs/operators';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
// Use RxJS `from` function to convert `Promise` to an `Observable`:
return from(this.handleToken()).pipe(
// Use RxJS `switchMap` operator to map the token's Observable to the next.handle one:
switchMap((token) => {
// Inject the token into the request's header, and pass it to the next request handler:
return next.handle(
req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`),
})
);
})
);
}
// Update the handleToken function to return a `Promise`:
handleToken(): Promise<string> {
return Auth.currentAuthenticatedUser().then((cognitoUser) =>
cognitoUser.refreshSession(
cognitoUser.signInUserSession.refreshToken,
(err, session) => {
return session.accessToken.jwtToken;
}
)
);
}
}
Read more about creating your own interceptor here: https://angular.io/guide/http#intercepting-requests-and-responses