-
Notifications
You must be signed in to change notification settings - Fork 167
(Draft proposal): Janssen Passkey Enhancement Spec
The current agama-passkey project provides a functional WebAuthn/FIDO2 enrollment and authentication flow, but it is scoped narrowly: one login flow, one enrollment flow, one device management screen. To drive real passkey adoption — the boss's stated goal of breaking 10% and beyond — Janssen needs to expand in three directions:
- More Agama Projects covering the full adoption lifecycle (not just "add a passkey")
- AdminUI observability so operators can see how the rollout is going
- Documentation reframed around "passkey adoption" rather than FIDO technical jargon
| Component | What it does | Gap |
|---|---|---|
org.gluu.agama.passkey.main |
Password login → passkey list/mgmt | No passkey-first / passwordless path |
org.gluu.agama.passkey.add |
Enroll a new passkey | No "passkey append" nudge post-login |
org.gluu.agama.passkey.list |
View/rename passkeys, auth with passkey | No delete, no last-used info, no device type |
org.gluu.agama.passkey.nickname |
Name a newly enrolled passkey | Nickname is required — blocks flow if skipped |
FidoValidator |
WebAuthn assertion (login) | No Conditional UI / autofill support |
FidoEnroller |
WebAuthn attestation (register) |
platformAuthn flag commented out; no resident key policy config |
ScimFido2Helper |
SCIM Fido2Devices CRUD | Filters out deviceData — loses authenticator type info |
| AdminUI | (none specific to passkeys) | No adoption metrics, no per-user passkey status |
-
passkey-add.ftlhhas a debugalert()left in the registration success path -
FidoValidator.assertionRequest()has dead code (builds a JSON string then overwrites it) -
ScimFido2Helper.getFidoDeviceByUser()silently drops entries that havedeviceData— this excludes platform authenticators on some platforms - Nickname is mandatory with no skip/default — friction at enrollment completion
- No Conditional UI (
autocomplete="username webauthn") on the login form — misses the "one-tap" experience
The article's framework maps directly to missing Agama flows. Each should be a standalone deployable project.
Purpose: After a user successfully logs in with password+OTP, detect if they have no passkey registered and prompt them to add one.
Trigger: Wraps any existing authentication flow as a step-up.
Key logic:
- Check
EnrollmentHelper.getMFAUserInfoByFido2(uid)— ifcount == 0, show nudge screen - Offer "Add a passkey now" (triggers
org.gluu.agama.passkey.add) or "Remind me later" (skippable, with configurable snooze via user attribute) - Track nudge impressions vs. conversions in a custom LDAP/DB attribute
Why it matters: This is the "Automatic Passkey Append" pattern from the Corbado guide — the single highest-impact driver of adoption.
Purpose: An identifier-first login flow where passkey is the primary method, password is the fallback.
Flow:
- User enters username (identifier-first)
- Conditional UI (
autocomplete="username webauthn") triggers browser passkey picker automatically - If no passkey exists or user declines → fall back to password
- On successful password login with no passkey → trigger nudge (3.1.1)
Frontend change needed in main.ftlh:
<input type="text" name="username" autocomplete="username webauthn" ...>This enables the browser's native passkey autofill without any button press.
Why it matters: The article calls this the "identifier-first approach" and "One-Tap Login" — it's the UX pattern that drives the highest login success rates.
Purpose: When a user loses all passkeys (device lost/replaced), provide a secure re-enrollment path without requiring a support call.
Flow:
- User attempts passkey login → fails
- Offer "I lost my passkey" link
- Verify identity via email OTP or backup code
- Immediately prompt to enroll a new passkey on the current device
- Optionally: integrate with identity verification provider for high-assurance recovery
Why it matters: The article identifies account recovery cost as one of the top 3 problems passkeys solve — but only if the recovery path itself is digitalized.
Purpose: Require passkey re-authentication before high-risk operations (password change, MFA settings, payment confirmation).
Flow:
- User is already logged in (session exists)
- Sensitive action triggers step-up
- Assert passkey for the current user's
uid(scoped assertion) - On success, grant elevated session claim
Why it matters: Demonstrates passkeys as phishing-resistant MFA, not just a password replacement — important for regulated industries.
Purpose: Allow login on a desktop using a passkey stored on a mobile device.
Flow:
- User clicks "Use a passkey from another device"
- WebAuthn
transport: ["hybrid"]triggers QR code display - User scans with phone, authenticates with biometric
- Desktop session is established
Note: This is largely handled by the browser/OS via the CTAP2 hybrid transport — the Agama flow mainly needs to not block it. Current code deletes authenticatorAttachment in passkey-add.ftlh which is correct, but the assertion flow should also avoid restricting transports.
3.2.1 Adoption Metrics Panel
A new "Passkey Adoption" section in AdminUI showing:
| Metric | Source | How to compute |
|---|---|---|
| Total users | LDAP/DB user count | Existing |
| Users with ≥1 passkey | SCIM Fido2Devices count > 0 per user | New query |
| Adoption % | (users with passkey / total users) × 100 | Derived |
| Passkeys enrolled (total) | Sum of all Fido2Device entries | New query |
| Avg passkeys per enrolled user | total passkeys / enrolled users | Derived |
| New enrollments (last 7/30 days) | Fido2Device creationDate filter |
New query |
| Passkey login events (last 7/30 days) | Auth server event log filter | New query |
3.2.2 Per-User Passkey Status
In the existing user detail view, add a "Passkeys" tab showing:
- List of enrolled passkeys with nickname, creation date, last used date, authenticator type (platform vs. roaming)
- Admin ability to revoke a passkey (calls SCIM DELETE on Fido2Device)
- "Send enrollment nudge" button (triggers email with enrollment link)
3.2.3 Rollout Configuration Panel
Allow admins to configure the nudge/adoption strategy without code changes:
- Enable/disable post-login nudge globally
- Set nudge frequency (e.g., show every N logins until enrolled)
- Define which user groups are in the "passkey rollout wave"
- Set minimum passkey adoption target (shows progress bar toward goal)
Implementation approach: These metrics can be surfaced via a new Janssen Config API endpoint (e.g., GET /jans-config-api/api/v1/passkey/stats) that aggregates from SCIM and the auth event log. AdminUI calls this endpoint to render the dashboard.
Currently ScimFido2Helper drops entries with deviceData. This field contains authenticator type (platform/roaming), AAGUID, and transport hints. It should be:
- Preserved and surfaced in the passkey list UI (show "Face ID", "Windows Hello", "Security Key" icons)
- Used in AdminUI to break down adoption by authenticator type
The SCIM Fido2Device schema should track when a passkey was last used for authentication. This enables:
- Users to identify and remove stale passkeys
- AdminUI to show "active" vs. "dormant" passkeys
- Adoption metrics to distinguish enrolled-but-never-used from actively-used passkeys
The Jans auth server should emit structured audit events for:
-
PASSKEY_ENROLLED— user enrolled a new passkey -
PASSKEY_AUTH_SUCCESS— user authenticated with a passkey -
PASSKEY_AUTH_FAILURE— passkey assertion failed (with reason) -
PASSKEY_NUDGE_SHOWN— adoption nudge was displayed -
PASSKEY_NUDGE_ACCEPTED/PASSKEY_NUDGE_DISMISSED
These feed the AdminUI dashboard and can be forwarded to SIEM.
Add GET /.well-known/fido2-configuration response field conditionalMediationSupported: true and ensure the assertion endpoint supports mediation=conditional requests (no allowCredentials required when username is unknown).
Reframe all user-facing and developer-facing docs around "passkey adoption" language.
| Current (FIDO jargon) | Proposed (adoption language) |
|---|---|
| "FIDO2 device" | "passkey" |
| "attestation" | "passkey enrollment" / "adding a passkey" |
| "assertion" | "signing in with a passkey" |
| "authenticator" | "device" or "security key" |
| "relying party" | "your app" or "your service" |
| "AAGUID" | (hide from end users entirely) |
| "WebAuthn" | (developer docs only) |
- "Getting Started with Passkeys" — operator guide, replaces "FIDO2 Configuration"
- "Passkey Adoption Playbook" — how to roll out passkeys to your users (mirrors the Corbado article's 11-step framework but Janssen-specific)
- "Measuring Passkey Adoption" — how to use the AdminUI dashboard and interpret metrics
- "Passkey User Guide" — end-user-facing, embeddable in help centers
- "Passkey Recovery Guide" — what to do when you lose your passkey
- Remove debug
alert()frompasskey-add.ftlh— ships to production users - Fix dead code in
FidoValidator.assertionRequest()— content string built then overwritten - Make nickname optional — default to device type or "My Passkey" if blank; remove
requiredfrom nickname input - Stop filtering out
deviceDataentries inScimFido2Helper— platform authenticators are being silently dropped
- New Agama project:
agama-passkey-adopt(post-login nudge) - Add Conditional UI support to
main.ftlhlogin form (autocomplete="username webauthn") - AdminUI: Passkey Adoption Metrics panel
- New Agama project:
agama-passkey-first(passkey-first login)
- New Agama project:
agama-passkey-recovery - New Agama project:
agama-passkey-step-up - Add
lastAccessTimeto Fido2Device SCIM schema - Passkey adoption event logging in auth server
- AdminUI: Per-user passkey management tab
- AdminUI: Rollout configuration panel
- Reframe all FIDO docs around passkey adoption language
- Write Passkey Adoption Playbook for operators
- Write end-user Passkey Guide
| Metric | Baseline | 3-month target | 12-month target |
|---|---|---|---|
| Passkey adoption rate | ~0% (no visibility) | 10% | 40% |
| Passkey login rate (of total logins) | ~0% | 5% | 30% |
| Avg time to enroll passkey | N/A | < 60 seconds | < 45 seconds |
| Account recovery tickets | Baseline TBD | -10% | -40% |