Add passkey authentication endpoints and fix passkey data handling in UI

- Introduced `/auth/passkey/challenge` and `/auth/passkey/verify` endpoints for passkey authentication.
- Adjusted `PasskeyManagement.vue` to correctly parse nested passkey data.
This commit is contained in:
Jeppe Bundgaard
2026-02-23 23:02:40 +01:00
parent 0f309f6ed4
commit 4e7def8559
2 changed files with 133 additions and 1 deletions
+132
View File
@@ -1131,6 +1131,138 @@ paths:
'401':
$ref: '#/components/responses/Unauthorized'
/auth/passkey/challenge:
post:
tags:
- Authentication
summary: Initiate passkey authentication challenge
description: Generates a WebAuthn PublicKeyCredentialRequestOptions payload. If customer_number is provided, allowCredentials will be populated with existing passkeys for that account. Otherwise, a challenge is issued for discoverable credentials.
operationId: passkeyChallenge
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
customer_number:
type: integer
description: Optional customer's e-conomic customer number
example: 12345
responses:
'200':
description: Challenge generated
content:
application/json:
schema:
type: object
properties:
challenge_token:
type: string
description: Temporary token binding the challenge to the login attempt
publicKey:
type: object
properties:
challenge:
type: string
description: Base64URL-encoded challenge
rpId:
type: string
description: Relying party ID (domain)
timeout:
type: integer
description: Timeout in milliseconds
userVerification:
type: string
enum: [required, preferred, discouraged]
allowCredentials:
type: array
items:
type: object
properties:
type:
type: string
example: public-key
id:
type: string
description: Base64URL-encoded credential ID
transports:
type: array
items:
type: string
'400':
$ref: '#/components/responses/BadRequest'
/auth/passkey/verify:
post:
tags:
- Authentication
summary: Verify passkey authentication and start session
description: Verifies the WebAuthn assertion and challenge token. Returns a session token on success.
operationId: passkeyVerify
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- challenge_token
- id
- response
properties:
challenge_token:
type: string
description: The token returned by the challenge endpoint
id:
type: string
description: The credential ID (base64url)
response:
type: object
required:
- clientDataJSON
- authenticatorData
- signature
properties:
clientDataJSON:
type: string
description: Base64URL-encoded client data
authenticatorData:
type: string
description: Base64URL-encoded authenticator data
signature:
type: string
description: Base64URL-encoded signature
userHandle:
type: string
nullable: true
description: Base64URL-encoded user handle
responses:
'200':
description: Verification successful, session started
content:
application/json:
schema:
oneOf:
- type: object
required: [token]
properties:
token:
type: string
description: Bearer token for customer
- type: object
required: [session]
properties:
session:
type: string
description: Session token for subuser
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/auth/logout:
get:
tags:
@@ -24,7 +24,7 @@ const loadPasskeys = async () => {
isLoading.value = true;
try {
const response = await SessionUser.request('/account/security/passkeys', 'GET');
passkeys.value = response.data || [];
passkeys.value = response.data.data || [];
} catch (error) {
console.error('Failed to load passkeys:', error);
} finally {