Passkeys and WebAuthn: what runs where
The short answer
Every WebAuthn ceremony - passkey registration and passkey authentication - runs exclusively on LuxID's Universal Login and Account pages, in the login.luxid.lu / account.luxid.lu origin. Your application never calls WebAuthn APIs.
There is no navigator.credentials.create() or navigator.credentials.get() in Partner code, no attestation to verify, no challenge to generate, no public key to store. Your integration is the same redirect-based Authorization Code + PKCE flow as for any other sign-in method - see Add login to your Application. Whether the user completed the flow with a password, an OTP, or a passkey is something you learn afterwards, from the token claims.
This follows from how WebAuthn works. In WebAuthn terms, the Relying Party of the ceremony is the origin the credential is bound to - and LuxID passkeys are bound to login.luxid.lu. (Careful with vocabulary: in LuxID documentation "Relying Party" usually means your application as an OIDC RP; in the WebAuthn spec, the Relying Party of the passkey ceremony is LuxID itself.) A passkey bound to login.luxid.lu can only ever be exercised by a page served from that origin - that origin binding is precisely what makes passkeys phishing-resistant.
What runs where
| Step | Where it runs | Your involvement |
|---|---|---|
| Passkey registration (enrolment) | LuxID Account (https://account.luxid.lu/ (opens in a new tab) > Security) - user-initiated | None - you may encourage enrolment, you cannot trigger it |
| Passkey authentication ceremony | Universal Login on login.luxid.lu | None - you redirected the user there, as for any sign-in |
| Challenge generation and assertion verification | LuxID backend | None |
| Public key storage | LuxID | None - Partners never see or store passkey material |
| Deciding the outcome | Your application, from the acr / amr claims in the ID token | Validate the token, then apply your policy |
The sequence, end to end:
What your application observes
When the user signed in with a passkey, the ID token carries:
{
"acr": "urn:luxid:acr:level:substantial",
"amr": ["fido"]
}
amr: ["fido"]is the value LuxID emits for a WebAuthn / passkey authentication, for both platform passkeys (Face ID, Windows Hello, synced passkeys) and roaming hardware keys - the claim does not distinguish between them.- In a password-plus-passkey flow (passkey as second factor), you see
amr: ["pwd", "fido"]. - A passkey sign-in reaches
acrlevelsubstantial.
Design your access control around acr (the assurance level reached) rather than sniffing amr for a specific method, unless you have a regulatory or contractual requirement for a particular factor type. Full claim semantics: Passwordless and passkeys and Authentication levels.
Requesting a passkey-capable flow
There is nothing to enable: passkey support is active for every LuxID Application, and Universal Login automatically offers "Login with Passkey" to any user who has one enrolled.
What you can express is a minimum assurance requirement. The canonical request form is the URN scale:
acr_values=urn:luxid:acr:level:substantial
A passkey sign-in satisfies substantial; so do other MFA combinations - requesting a level requires strength, not a specific method. Note the per-Partner caveat: LuxID interprets acr_values against a mapping agreed with each Partner at integration time, so confirm the accepted values for your Application with LuxID rather than assuming any form works untested. See Authentication levels.
And as always: verify the acr claim in the token you receive. Requesting a level does not guarantee it was reached; enforcing your minimum is your responsibility.
Native apps: passkeys are another reason to use the system browser
For native iOS and Android apps, the WebAuthn ceremony still runs on the Universal Login page - so the page must run somewhere the platform authenticator can reach it:
- iOS: open the authorisation URL in
ASWebAuthenticationSession(opens in a new tab). The session runs in the platform browser context, where iCloud Keychain passkeys and the Face ID / Touch ID prompts are available. - Android: use Custom Tabs (opens in a new tab). The tab runs in the user's browser, with Google Password Manager passkeys available.
An embedded WebView breaks passkeys outright: the platform credential store is not exposed to arbitrary WebViews, so users who prefer passkeys are forced back to passwords - on top of losing SSO and violating RFC 8252 (opens in a new tab). Using a WebView for LuxID authentication is a violation of the LuxID Agreement in any case; see Native and mobile apps.
If a user's device or browser cannot complete the passkey ceremony, Universal Login falls back to password plus another enrolled factor automatically - your application does not need to handle the fallback, and the amr claim will simply reflect the method that actually completed.
Common misconceptions
"We want to embed the passkey prompt in our own page for a smoother UX."
Not supported, and not possible. LuxID passkeys are origin-bound to login.luxid.lu; a WebAuthn call from your origin cannot exercise them, and LuxID exposes no API to delegate the ceremony. The redirect to Universal Login is the integration.
"Do we need to register an RP ID, origin, or attestation settings with LuxID?" No. WebAuthn configuration is entirely LuxID's side of the fence. You register redirect URIs, not WebAuthn parameters.
"Can our app trigger passkey enrolment for a signed-in user?"
No. Enrolment is a user-controlled action in the LuxID Account (opens in a new tab) Security settings. You can encourage it - for example by linking users to the public help page at https://www.luxid.lu/en/resources/passkeys (opens in a new tab) when their session's amr lacks fido - but you cannot initiate it.
"Does amr: ["fido"] tell us whether a synced passkey or a hardware key was used?"
No - the claim is the same for both. If you believe you need hardware-key-only access, discuss it with LuxID.
"Can we use WebAuthn ourselves, inside our application?"
Yes - but that is your own, separate credential system, bound to your origin, managed and stored by you, for your own purposes (for example, an app-level step-up on a sensitive action). It has no relationship to LuxID passkeys, LuxID tokens, or the amr claim.
Related pages
- Passwordless and passkeys - the full feature description: authenticator types, fallback, security properties
- Passwordless and passkey UX - what users see, and how to talk about passkeys in your UI
- Authentication levels -
acr_values,acr,amr, and step-up patterns - Add login to your Application - the quickstart, including the native-app requirements