-
Notifications
You must be signed in to change notification settings - Fork 268
[DOC] Add Passkey Docs #1333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
KaveeshaPiumini
wants to merge
1
commit into
asgardeo:docs
Choose a base branch
from
KaveeshaPiumini:docs
base: docs
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
[DOC] Add Passkey Docs #1333
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,4 +12,10 @@ Thunder | |
| npm | ||
| pnpm | ||
| [Vv]ite | ||
| [Pp]asswordless | ||
| [Uu]sernameless | ||
| APIs | ||
| UIs | ||
| iCloud | ||
| hostname | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "position": 5, | ||
| "label": "Authentication", | ||
| "collapsible": true | ||
| } | ||
|
|
6 changes: 6 additions & 0 deletions
6
docs/content/guides/authentication/passwordless-authentication/_category_.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "position": 1, | ||
| "label": "Passwordless Authentication", | ||
| "collapsible": true | ||
| } | ||
|
|
257 changes: 257 additions & 0 deletions
257
docs/content/guides/authentication/passwordless-authentication/passkeys.mdx
KaveeshaPiumini marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,257 @@ | ||
| --- | ||
| title: Passkeys | ||
| description: Configure and use Passkeys for passwordless sign-in | ||
| --- | ||
|
|
||
| # Passkeys | ||
|
|
||
| Use passkeys to offer phishing-resistant, passwordless authentication. Thunder exposes WebAuthn-based APIs to register passkey credentials and to authenticate users with those credentials. | ||
|
|
||
| ## Overview | ||
|
|
||
| Passkeys are a modern, secure alternative to passwords based on the WebAuthn standard. They use public-key cryptography to provide: | ||
|
|
||
| - **Phishing-resistant authentication**: Passkeys are bound to your domain and cannot be used on fake sites | ||
| - **Passwordless experience**: Users authenticate with biometrics, PINs, or security keys instead of remembering passwords | ||
| - **Cross-device compatibility**: Passkeys sync across devices via platform authenticators (e.g., iCloud Keychain, Google Password Manager) | ||
|
|
||
| Thunder supports passkeys through three approaches: | ||
|
|
||
| 1. **Thunder Gate (Hosted UI)**: Use Thunder's hosted authentication pages (`thunder-gate`) by configuring passkeys in your application settings—no custom UI or API calls needed | ||
| 2. **Atomic API approach**: Direct HTTP endpoints (`/register/passkey/*` and `/auth/passkey/*`) for full control over the registration and authentication flow | ||
| 3. **Flow-based approach**: Integrate passkeys into orchestrated authentication/registration flows via the `/flow/execute` API, combining passkeys with other authentication methods | ||
|
|
||
| All approaches follow the WebAuthn standard ceremony: | ||
| - **Registration**: Generate a credential creation challenge, collect the attestation from the browser's `navigator.credentials.create()`, and store the credential | ||
| - **Authentication**: Generate an assertion challenge, collect the signed assertion from `navigator.credentials.get()`, and verify it | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Serve the UI over HTTPS with a hostname that matches your WebAuthn Relying Party ID (RP ID). | ||
| - Add allowed origins for WebAuthn to your deployment configuration. Example (`repository/conf/deployment.yaml`): | ||
|
|
||
| ```yaml | ||
| passkey: | ||
| allowed_origins: | ||
| - "https://localhost:8090" | ||
| - "https://localhost:3000" | ||
| ``` | ||
|
|
||
| - Use a WebAuthn-capable browser (recent Chrome, Edge, Safari, or Firefox); you can confirm support at https://passkeys.dev/device-support/ or by checking `window.PublicKeyCredential` in the browser console. | ||
| - Ensure users already exist or are created beforehand in Thunder for passkey registration. | ||
|
|
||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
KaveeshaPiumini marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ## Use Thunder Gate (Hosted UI) | ||
|
|
||
| The simplest way to enable passkeys is through Thunder Gate, Thunder's hosted authentication and registration UI. This approach uses OAuth2/OIDC authorization flow: | ||
|
|
||
| 1. **Create an application** in the Thunder Develop console (or via the Application API). | ||
| 2. **Configure authentication flows** for the application: | ||
| - Navigate to **Applications** → Select your application → **Flows** tab | ||
| - Select an **Authentication Flow** that includes passkey authentication (e.g., "Passkey Authentication" or "Basic + Passkey Authentication and Registration Flow") | ||
| - Optionally, select a **Registration Flow** that includes passkey registration (e.g., "Passkey Registration Flow") | ||
| - The flow builder UI lets you customize which executors run and configure relying party settings | ||
| 3. **Integrate with your application**: | ||
| - Redirect users to Thunder's OAuth2 authorize endpoint: | ||
| ``` | ||
| https://localhost:8090/oauth2/authorize?client_id=<your-client-id>&redirect_uri=<your-callback>&response_type=code&scope=openid | ||
| ``` | ||
| - Thunder automatically redirects to Thunder Gate (e.g., `https://localhost:5190/gate/signin`) based on the `gate_client` configuration in `deployment.yaml` | ||
| - Thunder Gate renders the authentication UI based on your selected flow, handling passkey WebAuthn ceremonies in the browser | ||
| - After successful authentication, users are redirected back to your application with an authorization code (exchange it for tokens via `/oauth2/token`) | ||
|
|
||
| This approach requires **no custom UI development** or direct WebAuthn API calls from your application. Thunder Gate (`thunder-gate`) handles: | ||
| - Rendering sign-in/registration prompts based on the configured flow | ||
| - Passkey registration during user sign-up (if registration flow includes passkey executor) | ||
| - Passkey authentication during sign-in | ||
| - Fallback to other authentication methods (password, social login, etc.) as defined in the flow | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - All WebAuthn ceremony handling (`navigator.credentials.create()` and `.get()`) | ||
|
|
||
| **When to use this approach:** | ||
| - You want a quick setup without building custom authentication UIs | ||
| - You're using Thunder as an OAuth2/OIDC provider for your applications | ||
| - You want Thunder to manage the full authentication experience with customizable flows | ||
|
|
||
| **When to use API-based approaches:** | ||
| - You need full control over the UI/UX beyond what flow configuration offers | ||
| - You're building a mobile application or SPA with custom authentication flows that don't fit OAuth2 redirect flow | ||
| - You want to embed authentication directly in your application without redirects | ||
KaveeshaPiumini marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ## Use Passkey Atomic API in Your Application | ||
| > The registration and authentication flows below describe the **atomic API** approach (direct `/register/passkey/*` and `/auth/passkey/*` calls). | ||
|
|
||
| ### Registration Flow | ||
|
|
||
| 1) **Start registration** – create WebAuthn creation options and a session token. | ||
|
|
||
| ```bash | ||
| curl -k -X POST https://localhost:8090/register/passkey/start \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "userId": "<user-id>", | ||
| "relyingPartyId": "localhost", | ||
| "relyingPartyName": "Thunder", | ||
| "authenticatorSelection": { | ||
| "userVerification": "preferred" | ||
| }, | ||
| "attestation": "none" | ||
| }' | ||
| ``` | ||
|
|
||
| Response fields: | ||
| - `publicKeyCredentialCreationOptions`: pass directly to `navigator.credentials.create()` (after Base64URL→ArrayBuffer conversion). | ||
| - `sessionToken`: required for the finish call. | ||
|
|
||
| 2) **Run WebAuthn ceremony in the browser** – call `navigator.credentials.create()` with the returned options. See the sample implementation in `samples/apps/react-vanilla-sample/src/services/authService.ts`. | ||
|
|
||
| 3) **Finish registration** – send the attestation result with the session token. | ||
|
|
||
| ```bash | ||
| curl -k -X POST https://localhost:8090/register/passkey/finish \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "publicKeyCredential": { | ||
| "id": "<credential-id>", | ||
| "type": "public-key", | ||
| "response": { | ||
| "clientDataJSON": "<base64url>", | ||
| "attestationObject": "<base64url>" | ||
| } | ||
| }, | ||
| "sessionToken": "<session-token>", | ||
| "credentialName": "My laptop key" | ||
| }' | ||
| ``` | ||
|
|
||
| On success, the API returns passkey registration metadata for the newly created credential (`CredentialID`, `CredentialName`, and `CreatedAt`). | ||
KaveeshaPiumini marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ### Authentication Flow | ||
|
|
||
| 1) **Start authentication** – request assertion options. | ||
|
|
||
| ```bash | ||
| curl -k -X POST https://localhost:8090/auth/passkey/start \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "userId": "<user-id-optional>", | ||
| "relyingPartyId": "localhost" | ||
| }' | ||
| ``` | ||
|
|
||
| - `userId` is optional for usernameless authentication. | ||
| - Response fields: | ||
| - `publicKeyCredentialRequestOptions`: pass to `navigator.credentials.get()`. | ||
| - `sessionToken`: required for finish. | ||
|
|
||
| 2) **Run WebAuthn assertion in the browser** – call `navigator.credentials.get()` with the options. | ||
|
|
||
| 3) **Finish authentication** – send the assertion result with the session token. | ||
|
|
||
| ```bash | ||
| curl -k -X POST https://localhost:8090/auth/passkey/finish \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "publicKeyCredential": { | ||
| "id": "<credential-id>", | ||
| "type": "public-key", | ||
| "response": { | ||
| "clientDataJSON": "<base64url>", | ||
| "authenticatorData": "<base64url>", | ||
| "signature": "<base64url>", | ||
| "userHandle": "<base64url>" | ||
| } | ||
| }, | ||
| "sessionToken": "<session-token>", | ||
| "skipAssertion": false | ||
KaveeshaPiumini marked this conversation as resolved.
Show resolved
Hide resolved
KaveeshaPiumini marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }' | ||
| ``` | ||
|
|
||
| On success, the API returns an authentication response compatible with other Thunder auth flows. | ||
|
|
||
| ## Use Passkeys With Flow/Execute | ||
|
|
||
| Passkeys also work through the flow engine (`POST /flow/execute`), which returns dynamic prompts and additional data. | ||
|
|
||
| ### Authentication With Flow/Execute | ||
|
|
||
| 1) **Start the flow** – send an initial flow request. | ||
|
|
||
| ```bash | ||
| curl -k -X POST https://localhost:8090/flow/execute \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "applicationId": "<app-id>", | ||
| "flowType": "AUTHENTICATION" | ||
| }' | ||
| ``` | ||
|
|
||
| - The response includes a `flowId` and a step `action` for passkeys. `data.additionalData.passkeyChallenge` contains the WebAuthn request options. The passkey session token is stored server-side in the flow context runtime data and is managed by the server; the client does not need to read or send it. | ||
|
|
||
| 2) **Run WebAuthn in the browser** – call `navigator.credentials.get()` with the decoded `passkeyChallenge`. | ||
|
|
||
| 3) **Continue the flow** – post the assertion back to the flow engine. | ||
|
|
||
| ```bash | ||
| curl -k -X POST https://localhost:8090/flow/execute \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "flowId": "<flow-id-from-step>", | ||
| "action": "<action-ref-from-step>", | ||
| "inputs": { | ||
| "credentialId": "<credential-id>", | ||
| "clientDataJSON": "<base64url>", | ||
| "authenticatorData": "<base64url>", | ||
| "signature": "<base64url>", | ||
| "userHandle": "<base64url-optional>" | ||
| } | ||
| }' | ||
| ``` | ||
|
|
||
| - On success, the next response either completes the flow (with an assertion) or advances to the next step. | ||
|
|
||
| ### Registration With Flow/Execute | ||
|
|
||
| > Passkey registration in a flow must run **after the user is created or identified**. Ensure the flow provisions the user (e.g., collecting username/email and running provisioning) or resolves an existing user before the passkey register start node, as shown in the bundled flow definitions. | ||
|
|
||
| 1) **Start a registration flow** – send an initial flow request. | ||
|
|
||
| ```bash | ||
| curl -k -X POST https://localhost:8090/flow/execute \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "applicationId": "<app-id>", | ||
| "flowType": "REGISTRATION" | ||
| }' | ||
| ``` | ||
|
|
||
| > Reminder: Calling `/flow/execute` with a flow that only runs the passkey registration executor is impractical; the flow must first provision or resolve the user so registration has a valid subject. | ||
|
|
||
| - The response includes a `flowId`, a registration step `action`, and `data.additionalData.passkeyCreationOptions` with the WebAuthn creation options. The passkey session state is maintained server-side and associated with the flow, so clients only need to carry `flowId` (and the step `action`) between calls. Ensure the flow has already collected the user identifier before this step because registration requires a user ID. | ||
|
|
||
| 2) **Run WebAuthn in the browser** – call `navigator.credentials.create()` with the decoded `passkeyCreationOptions`. | ||
|
|
||
| 3) **Finish registration in the flow** – post the attestation back to the flow engine. | ||
|
|
||
| ```bash | ||
| curl -k -X POST https://localhost:8090/flow/execute \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "flowId": "<flow-id-from-step>", | ||
| "action": "<action-ref-from-step>", | ||
| "inputs": { | ||
| "credentialId": "<credential-id>", | ||
| "clientDataJSON": "<base64url>", | ||
| "attestationObject": "<base64url>", | ||
| "credentialName": "My laptop key" | ||
| } | ||
| }' | ||
| ``` | ||
|
|
||
| - On success, the response includes credential metadata (e.g., `passkeyCredentialID`) or advances to the next step of the flow. | ||
|
|
||
| ## Common Issues | ||
|
|
||
| - **Origin mismatch**: The browser origin must be listed under `passkey.allowed_origins` and must match the RP ID domain. | ||
| - **HTTP instead of HTTPS**: WebAuthn requires HTTPS in production. | ||
| - **Stale session token**: Use the `sessionToken` from the most recent start call for each ceremony. | ||
| - **Unsupported platform authenticator**: Adjust `authenticatorSelection` (e.g., `authenticatorAttachment`) in the start request to match the target device. | ||
| - **User not found**: Ensure the user exists in Thunder before registering a passkey or start authentication with a valid user ID (unless using usernameless auth). | ||
KaveeshaPiumini marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.