Authentication strength and step-up
Use acr_values and max_age to require stronger authentication.
For some operations "the user has signed in" is not enough — deleting data, changing security settings, viewing sensitive information all need confirmation that it is the user right now. MuoCeon expresses authentication strength with ACR (Authentication Context Class Reference), and lets you require a minimum level in the authorization request.
Two strength levels
| Level | URN | Meaning | Typically achieved by |
|---|---|---|---|
| AAL1 | urn:muoceon:acr:aal1 | Baseline authentication | Knowledge factors such as a password or recovery code |
| AAL2 | urn:muoceon:acr:aal2 | Possession-factor authentication | passkey, or an authenticator on the device |
The check is "meets or exceeds", not exact equality: after signing in with a passkey, requesting aal1 is satisfied as well.
How to require a higher strength
Add two parameters to the authorization request:
&acr_values=urn:muoceon:acr:aal2
&max_age=300acr_valuesis an ordered preference list (space-separated); the server picks the highest level it can satisfy. Send only the level below which you will not accept.max_ageis the maximum accepted authentication age in seconds.max_age=300means "authentication must have happened within 5 minutes";max_age=0demands immediate re-authentication — do not use it unless genuinely required.
What happens when strength is insufficient
This is the part of the flow that is most transparent to third parties:
- The user's current strength is insufficient, or the authentication age exceeds the limit;
- The consent UI receives
401 INSUFFICIENT_USER_AUTHENTICATION, and the consent ticket is not consumed; - The user is taken to the step-up page and completes stronger authentication (for example inserting a passkey);
- Afterwards they return to the consent UI with the original parameters and confirm again;
- The callback you finally receive is identical to the normal flow — it merely arrives a few dozen seconds later.
Do not implement step-up yourself
The step-up page, tickets and assertions are internal MuoCeon mechanisms. All you do is declare the requirement in the authorization request; the consent UI handles the retry. If you build compensation such as "wait a moment and retry the authorization" in your callback, you will end up submitting an already-successful authorization twice.
acr and auth_time in the token
Tokens carry the facts of this authentication so you can make a second judgement:
{
"acr": "urn:muoceon:acr:aal2",
"auth_time": 1760000000
}acr: the level actually reached in this authentication.auth_time: when the authentication happened, as a Unix timestamp in seconds.
For high-sensitivity endpoints, do an explicit server-side check: acr meets the requirement and auth_time is recent enough. A token being valid is not the same as the user having authenticated just now.
When to require AAL2
| Scenario | Recommendation |
|---|---|
| Read-only display of username, email | Not required; AAL1 suffices |
| Changing account security settings, deleting data | Require aal2 plus a small max_age |
Long-term offline access (offline_access) | Require aal2 at first authorization, then rely on refresh tokens |
| Background batch jobs | Do not use an interactive flow; consider a machine client |
Over-requiring sharply reduces consent rates
A user without a passkey will be stuck on the step-up page by acr_values=aal2. Unless the business
genuinely needs it, do not require AAL2 for ordinary sign-in.