I want the step-up authentication to happen every time (ie max age 0) a user tries to access specific Angular routes. I have discovered that the canActivateAuthGuard() functions provided by @okta/okta-angular do not support checks of the max age, only the ACR value. Consequently the path forward is a custom auth guard function.
I created the custom function & configured a route to use it.
When a user attempts to access the route they get challenged for a 2FA (Okta verify). However, the user never gets returned to the target route. Instead they end up in a constant loop being prompted for the 2FA (Okta Verify). I’ve provided a copy of the custom auth guard code below.
Any suggestions/assistance would be greatly appreciated.
Thanks
-------------------------
export const canActivateStepUpGuard: CanActivateFn = async (route, state) => {
const oktaAuth = inject(OKTA_AUTH);
// Check if the user is authenticated
const isAuthenticated = await oktaAuth.isAuthenticated();
if (!isAuthenticated) {
oktaAuth.setOriginalUri(state.url);
await oktaAuth.signInWithRedirect();
return false;
}
// Get the user's ID token
const idToken = oktaAuth.getIdToken();
if (idToken == undefined) {
oktaAuth.setOriginalUri(state.url);
await oktaAuth.signInWithRedirect();
return false;
}
// Check that the authentication time is recent enough
const tokenClaims = oktaAuth.token.decode(idToken);
const authTime = tokenClaims.payload.auth_time;
const currentTime = Math.floor(Date.now() / 1000);
if (!authTime || (currentTime - authTime) > 60) {
// If the authentication time is too old, force step-up authentication
oktaAuth.setOriginalUri(state.url);
await oktaAuth.signInWithRedirect(
{
acrValues: "urn:okta:loa:2fa:any",
maxAge: 60,
originalUri: state.url
}
);
return false;
}
return true;
}
It appears that the ID token auth_time is not being updated during the step up authentication process. I tried setting maxAge to 1 & also setting prompt to login in the signInWithRedirect() call but the auth_time did not change.
Your loop isn’t caused by Okta failing to refresh auth_time — it’s the guard checking the wrong claim. Per Okta’s step-up authentication guide, the claim that confirms step-up succeeded is acr, not auth_time.
auth_time records when the original authentication happened and isn’t guaranteed to change on every step-up round trip the way your (currentTime - authTime) > 60 check assumes. That’s why it stayed flat even after you switched to maxAge: 1 and prompt: ‘login’ — you were reading a claim that was never meant to signal "step-up just completed.
Could you please rewrite your code to check acr against your required Level of Authentication instead of auth_time age?
Any chance you can provide a link to the step-up auth guide you’re referring to?
Thanks for the tip on auth_time not being modified on step-up - I thought I had read somewhere that it does get updated if you make the call to signInWithRedirect() & include the prompt=login option.
I need a way to validate the age of the acr claim so that if it’s older than x then call signInWithRedirect(). From what I can see, once you have called signInWithRedirect() with the acrValues option the acr claim remains & is not cleared.
You are correct, the current canActivateAuthGuard() only support acr, but I highly recommend creating an issue against the Okta Angular library requesting this feature if you require it.
I haven’t tried looking at evaluating max_age step up checks client-side, only in the resource server against the access token. I’ll try to take a look at the id token when I can (but warning you it might be a bit before I can get to it).
Just in case, here’s my demo app showing authn recency check in an Angular + .NET API where I poked at this scenario a bit for a talk I gave.