ASVS 5.0 V6.8.4 says that if your application uses a separate identity provider and expects a certain authentication strength, method or recentness for specific functions, it must verify that using information returned by the IdP. With OIDC that means claims such as acr, amr and auth_time. If the IdP does not provide this data, you need a documented fallback that assumes the minimum strength method was used. It is a Level 2 requirement.
Why it matters
A valid login does not mean a strong login. A user may have signed in with a password this morning, while changing a payout account needs MFA from the last few minutes. If the app trusts "the user is authenticated" for everything, an attacker with a stolen session or a weak sign in reaches the sensitive actions.
How to implement it
Decide per function what you require, ask the IdP for it, then check the returned token.
- Request it. Send
acr_valuesandmax_agein the authorization request for sensitive operations. - Verify it. After validating the ID token signature (V6.8.2), read the claims and compare.
- Step up. If the claims fall short, send the user back to the IdP.
REQUIRED = {
"change_payout": {"acr": {"urn:mfa"}, "max_age": 300},
"view_profile": {"acr": None, "max_age": None},
}
def authorize(action, claims):
need = REQUIRED[action]
if need["acr"] and claims.get("acr") not in need["acr"]:
return step_up(action, acr_values=" ".join(need["acr"]))
if need["max_age"]:
auth_time = claims.get("auth_time")
if auth_time is None or time.time() - auth_time > need["max_age"]:
return step_up(action, max_age=need["max_age"])
return allow()
Be careful with amr: it lists methods used, such as pwd or otp. Check for the combination you need and do not treat it as proof when the IdP is not trusted to set it.
If the IdP gives no acr, amr or auth_time, document the fallback and implement it. For example, assume single factor password and require your own step up before the sensitive action. Write that decision in the design notes so reviewers can confirm it.
How to test it
- Sign in with a password only user and call a protected function that needs MFA. You should be sent to step up.
- Sign in with MFA, wait beyond
max_age, and try again. - Alter the token claims and confirm signature validation fails first.
- Point the app at a test IdP that omits the claims and confirm the fallback applies.
Common mistakes
- Trusting a login without checking its strength.
- Reading
acrfrom an unverified token. - Missing
auth_timetreated as fresh. - Hard coding an ACR string the IdP never sends, so the check always fails open or closed without notice.
Quick check
The IdP returns no acr claim. Is the user considered MFA authenticated?
No. Without evidence, assume the weakest method and step up in your app.
What does auth_time tell you?
When the user last actively authenticated at the IdP, which lets you require recent sign in for risky actions.