Authentication
The authentication flow allows any user to log in with a registered passkey, without providing a password. It consists of two steps and ends with a session or token being issued depending on the configured auth_action.
Authorization header.Get verification options
Generate a WebAuthn challenge and request options for the client.
POST /api/passkeys/verify/options
Headers
| Header | Value |
|---|---|
Content-Type | application/json |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
credential_id | string | Yes | Base64url-encoded credential ID of the passkey to verify |
{
"credential_id": "credential-id-base64url"
}
Response — WebAuthn PublicKeyCredentialRequestOptions
{
"challenge": "base64url-encoded-random-challenge",
"allowCredentials": [
{
"id": "credential-id",
"type": "public-key"
}
],
"timeout": 60000,
"userVerification": "preferred"
}
Verify a passkey
Verify the assertion returned by the authenticator.
POST /api/passkeys/verify
Headers
| Header | Value |
|---|---|
Content-Type | application/json |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Base64url-encoded credential ID |
rawId | string | Yes | Base64-encoded raw credential ID |
type | string | Yes | Must be "public-key" |
response.clientDataJSON | string | Yes | Base64-encoded client data JSON |
response.authenticatorData | string | Yes | Base64-encoded authenticator data |
response.signature | string | Yes | Base64-encoded signature |
response.userHandle | string | No | Base64-encoded user handle (optional) |
{
"id": "credential-id-base64url",
"rawId": "credential-id-base64",
"type": "public-key",
"response": {
"clientDataJSON": "base64-encoded-client-data-json",
"authenticatorData": "base64-encoded-authenticator-data",
"signature": "base64-encoded-signature"
}
}
Response — 200 OK with the passkeeable information
{
"passkeeable_id": 42,
"passkeeable_type": "App\\Models\\User",
"passkey": {
"id": 1
}
}
Authenticate and get a token
Verify the passkey assertion and create an authenticated session or token in a single step.
POST /api/passkeys/login
This endpoint performs the same verification as POST /api/passkeys/verify but additionally identifies the user and delegates authentication to the configured auth_action.
Headers
| Header | Value |
|---|---|
Content-Type | application/json |
Request body — identical to /api/passkeys/verify
{
"id": "credential-id-base64url",
"rawId": "credential-id-base64",
"type": "public-key",
"response": {
"clientDataJSON": "base64-encoded-client-data-json",
"authenticatorData": "base64-encoded-authenticator-data",
"signature": "base64-encoded-signature",
"userHandle": null
}
}
Response — 200 OK — shape depends on the configured auth_action:
{
"user": {
"id": 42,
"name": "John Doe",
"email": "john@example.com"
}
}
{
"user": {
"id": 42,
"name": "John Doe",
"email": "john@example.com"
},
"token": "1|plain-text-sanctum-token"
}
{
"user": {
"id": 42,
"name": "John Doe",
"email": "john@example.com"
},
"token": "plain-text-passport-token",
"expires_at": "2026-04-26T10:00:00.000000Z"
}
id (credential ID) against the credential_id column in the passkeys table. If no matching passkey is found, the request returns a 404 error.