Upstream MFA carry-over
When a user signs in through a social sign-in provider that already enforces multi-factor authentication (MFA), Ory can trust that second factor instead of asking for another one. This avoids redundant prompts when your project enforces AAL2 through step-up authentication and the upstream identity provider has already verified the user with a strong factor.
How it works
Every time a user signs in or registers through an OIDC provider, Ory reads two optional claims from the upstream ID token:
acr— the Authentication Context Class Reference reported by the provider. It tells the relying party which authentication policy was satisfied (for exampleurn:okta:loa:2fa:anyorhttp://schemas.openid.net/pape/policies/2007/06/multi-factor).amr— the Authentication Methods References array. Each entry names a factor type the user completed (for examplepwd,mfa,otp,hwk,fpt).
You configure two allowlists per provider:
aal2_acr_values— if the upstreamacrclaim matches any value in this list, Ory marks the resulting session as AAL2.aal2_amr_values— if any entry in the upstreamamrarray matches a value in this list, Ory marks the resulting session as AAL2.
When neither list matches, the session stays at AAL1. If your project enforces session.whoami.required_aal=highest_available,
the user is then prompted for a Kratos-managed second factor as usual. Both fields are optional; leaving them empty preserves the
current behavior of always issuing AAL1 sessions through this provider.
The upstream acr and amr values are persisted on the session's authentication_methods entry as upstream_acr and
upstream_amr for auditing. They appear on /sessions/whoami and in webhook payloads, so downstream systems can record which
upstream factors were used.
Provider support
Not every OIDC provider returns acr or amr. The table below lists the providers Ory ships with and whether their ID tokens
include these claims.
| Provider | acr | amr | Notes |
|---|---|---|---|
| Generic OIDC | Yes | Yes | Works with any OIDC-compliant IdP that emits these claims (Keycloak, Microsoft Entra v1, PingFederate, Auth0 federation, Okta federation, and many others). |
| Microsoft (Entra ID / Azure AD) | v1.0 only | v1.0 only | v1.0 ID tokens include both. v2.0 tokens typically omit acr and amr. |
| Auth0 | Yes | Yes | amr contains mfa once the user completed an MFA challenge. The standard step-up acr value is http://schemas.openid.net/pape/policies/2007/06/multi-factor. |
| Okta (federation) | Yes | Yes | Predefined acr values such as urn:okta:loa:1fa:any and urn:okta:loa:2fa:any. amr values include pwd, mfa, sms, hwk, fpt, face. |
| Keycloak | Yes | Yes (recent versions) | acr is mapped from the Level-of-Authentication config. amr is populated by the dedicated AMR protocol mapper. |
| Ping Identity (PingAM, PingFederate, PingOne AIC) | Yes (opt-in) | Yes (opt-in) | Neither claim is added by default. Configure them through Authentication Journeys or "Force ACR in ID Token". |
| No | No | Google's OIDC implementation does not issue acr or amr. | |
| Apple (Sign in with Apple) | No | No | Not part of Apple's documented ID-token schema. |
| Facebook, GitHub, GitLab, Discord, Slack, Spotify, Amazon, LinkedIn, Yandex, VK, NetID, Dingtalk, Patreon | No | No | OAuth2-only or non-standard OIDC; no acr or amr surfaced. |
You can still configure the allowlist for any provider — Ory simply records the upstream values and never matches when the provider doesn't emit them.
Configuration
- Ory Console
- Ory CLI
- Go to Authentication → Social Sign-In (OIDC) in the Ory Console.
- Open the social sign-in provider you want to configure, or add a new one.
- In the provider configuration dialog, find the Upstream MFA fields:
- Elevate session to AAL2 when
acrmatches — add the upstreamacrclaim values that should mark the resulting session as AAL2. - Elevate session to AAL2 when
amrmatches — add the upstreamamrvalues (per RFC 8176) that should mark the session AAL2 when any of them appears in the upstreamamrarray.
- Elevate session to AAL2 when
- Click Save Configuration.
-
Get the Ory Identities configuration from your project and save it to a file:
ory get identity-config --project <project-id> --workspace <workspace-id> --format yaml > identity-config.yaml -
Add
aal2_acr_valuesand/oraal2_amr_valuesto the provider configuration:identity-config.yamlselfservice:
methods:
oidc:
enabled: true
config:
providers:
- id: my-okta
provider: generic
client_id: ...
client_secret: ...
issuer_url: https://example.okta.com
mapper_url: base64://...
scope:
- openid
- email
# Upstream MFA carry-over: any of these acr values
# marks the resulting Ory session as AAL2.
aal2_acr_values:
- urn:okta:loa:2fa:any
- http://schemas.openid.net/pape/policies/2007/06/multi-factor
# Or match on the upstream amr array. Any entry that
# matches one of these values elevates the session.
aal2_amr_values:
- mfa
- otp
- hwk -
Update the Ory Identities configuration:
ory update identity-config --project <project-id> --workspace <workspace-id> --file identity-config.yaml
Request a specific ACR from the provider
Some providers (notably Auth0 and Okta) only emit a strong acr claim when the relying party explicitly asks for it through the
acr_values parameter. You can do this in two ways:
-
Pass
acr_valuesas an upstream parameter on the login flow. Ory forwards the value to the provider's authorization endpoint. -
Use the
requested_claimsfield on the provider configuration to ask for theacrclaim through the OpenID Connect claims parameter:selfservice:
methods:
oidc:
config:
providers:
- id: my-okta
provider: generic
# ...
requested_claims:
id_token:
acr:
essential: true
values:
- urn:okta:loa:2fa:any
aal2_acr_values:
- urn:okta:loa:2fa:any
Inspect the result
After a successful sign-in, the Ory session reflects the upstream MFA decision. For example, fetching /sessions/whoami returns:
{
"id": "...",
"active": true,
"authenticator_assurance_level": "aal2",
"authentication_methods": [
{
"method": "oidc",
"aal": "aal2",
"completed_at": "2026-04-13T08:23:19Z",
"provider": "my-okta",
"upstream_acr": "urn:okta:loa:2fa:any",
"upstream_amr": ["pwd", "mfa"]
}
],
"identity": {
/* ... */
}
}
When the upstream acr or amr does not match the configured allowlists, the session stays at AAL1 and the upstream values are
still recorded under upstream_acr / upstream_amr for auditing.
What this does not do
- It does not call the upstream provider to perform MFA on demand. Ory only inspects what the provider already reported.
- It does not change how Kratos-managed second factors (TOTP, WebAuthn, lookup secrets) work. Local step-up still applies when the upstream did not satisfy the configured allowlist.
- It does not retroactively re-classify existing sessions. Only sessions issued after the new configuration becomes active are evaluated against the allowlists.
Troubleshooting
The session is still AAL1 after a successful login. Check that the upstream provider actually emits the claim. You can inspect
the raw ID token claims from the Jsonnet data mapper through std.extVar('claims').raw_claims.acr and
std.extVar('claims').raw_claims.amr. If the provider doesn't surface the claim, request it explicitly through acr_values or
requested_claims.
The upstream_acr field is empty even though the upstream sent it. Make sure your provider config has
claims_source: id_token (the default) or, if you set claims_source: userinfo, that the userinfo endpoint also returns the
acr claim. Some providers only include acr/amr in the ID token.
The session is AAL2 but the user never completed MFA. Audit the configured allowlists carefully. An acr value such as 0 or
urn:mace:incommon:iap:bronze typically means "no MFA was performed" — do not include those values in aal2_acr_values.