From 4e9f810090a5afa29835aa8d48c05e6d9a10714a Mon Sep 17 00:00:00 2001 From: Dhruv Pareek Date: Tue, 21 Apr 2026 21:41:20 -0700 Subject: [PATCH] feat: add PASSKEY branch to auth credential verify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the PASSKEY branch to `AuthCredentialVerifyRequestOneOf`, completing the create + verify flow for WebAuthn-passkey-backed authentication credentials on Embedded Wallet internal accounts. **Request shape** - `POST /auth/credentials/{id}/verify` body: `{ type: "PASSKEY", assertion, clientPublicKey }` → 200 `AuthSession`. - `{id}` is the `AuthMethod.id` returned from `POST /auth/credentials`. **Schemas added** - `PasskeyAssertion` — `{ credentialId, clientDataJson, authenticatorData, signature, userHandle? }`, all base64url-encoded strings. Shape mirrors the W3C `AuthenticatorAssertionResponse` plus the optional `userHandle`; pass-through to the downstream passkey provider. - `PasskeyCredentialVerifyRequestFields` — `{ type: "PASSKEY", assertion, clientPublicKey }` (variant single-value enum on `type`). - `PasskeyCredentialVerifyRequest` — `allOf(AuthCredentialVerifyRequest, PasskeyCredentialVerifyRequestFields)`. **Wire-up** - `AuthCredentialVerifyRequestOneOf.yaml` discriminator map extended with `PASSKEY → PasskeyCredentialVerifyRequest`. - PASSKEY example added on `POST /auth/credentials/{id}/verify`. - Endpoint description updated to cover the PASSKEY path: client runs `navigator.credentials.get()` against a platform-backend-issued challenge and submits the assertion; Grid verifies the WebAuthn signature against the stored credential. - 401 response description extended to cover passkey-specific failures (assertion signature / challenge / credential mismatch). - `.stainless/stainless.yml` registers the three new schemas and extends the "remove allOf $ref to AuthCredentialVerifyRequest" transform target list to include `PasskeyCredentialVerifyRequest.allOf[0]`. **Design notes** - The PDF verify-body shape on p.7 omits an explicit `type` field, but every other branch in `AuthCredentialVerifyRequestOneOf` carries one and the discriminator requires it. PASSKEY is modeled consistently with EMAIL_OTP and OAUTH — `type: "PASSKEY"` is required on the request body. - `assertion` is a structured object at the Grid API surface (not an opaque stamp string). WebAuthn signature verification reconstructs from the individual base64url-encoded fields, so structured + pass-through preserves byte integrity without forcing clients to JSON-encode the stamp themselves. - `clientPublicKey` format matches EMAIL_OTP/OAUTH verify: P-256 uncompressed SEC1 hex (130 chars, 0x04-prefixed). **Notes** - Pairs with `04-21-feat_add_passkey_branch_to_auth_credential_create`; together they make passkey credentials usable end-to-end. The additional-credential challenge flow gets its own PASSKEY branch in the next PR. - Bundled `openapi.yaml` and `mintlify/openapi.yaml` regenerated via `make build`. --- .stainless/stainless.yml | 4 ++ mintlify/openapi.yaml | 69 ++++++++++++++++++- openapi.yaml | 69 ++++++++++++++++++- .../AuthCredentialVerifyRequestOneOf.yaml | 2 + .../schemas/auth/PasskeyAssertion.yaml | 44 ++++++++++++ .../auth/PasskeyCredentialVerifyRequest.yaml | 4 ++ .../PasskeyCredentialVerifyRequestFields.yaml | 23 +++++++ .../auth/auth_credentials_{id}_verify.yaml | 24 +++++-- 8 files changed, 231 insertions(+), 8 deletions(-) create mode 100644 openapi/components/schemas/auth/PasskeyAssertion.yaml create mode 100644 openapi/components/schemas/auth/PasskeyCredentialVerifyRequest.yaml create mode 100644 openapi/components/schemas/auth/PasskeyCredentialVerifyRequestFields.yaml diff --git a/.stainless/stainless.yml b/.stainless/stainless.yml index 2ad63eab..d71dcf22 100644 --- a/.stainless/stainless.yml +++ b/.stainless/stainless.yml @@ -351,8 +351,11 @@ resources: oauth_credential_additional_challenge: '#/components/schemas/OauthCredentialAdditionalChallenge' oauth_credential_additional_challenge_fields: '#/components/schemas/OauthCredentialAdditionalChallengeFields' passkey_attestation: '#/components/schemas/PasskeyAttestation' + passkey_assertion: '#/components/schemas/PasskeyAssertion' passkey_credential_create_request: '#/components/schemas/PasskeyCredentialCreateRequest' passkey_credential_create_request_fields: '#/components/schemas/PasskeyCredentialCreateRequestFields' + passkey_credential_verify_request: '#/components/schemas/PasskeyCredentialVerifyRequest' + passkey_credential_verify_request_fields: '#/components/schemas/PasskeyCredentialVerifyRequestFields' exchange_rates: methods: list: @@ -865,6 +868,7 @@ openapi: target: - "$.components.schemas.EmailOtpCredentialVerifyRequest.allOf[0]" - "$.components.schemas.OauthCredentialVerifyRequest.allOf[0]" + - "$.components.schemas.PasskeyCredentialVerifyRequest.allOf[0]" keys: [ "$ref" ] codeflow: diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index 5e7c43a9..e513165f 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -3861,7 +3861,7 @@ paths: description: | Complete the verification step for a previously created authentication credential and issue a session signing key. - For `EMAIL_OTP` credentials, supply the one-time password that was emailed to the user along with a client-generated public key. For `OAUTH` credentials, supply a fresh OIDC token (`iat` must be less than 60 seconds before the request) along with the client-generated public key; this is also the reauthentication path after a prior session expired. + For `EMAIL_OTP` credentials, supply the one-time password that was emailed to the user along with a client-generated public key. For `OAUTH` credentials, supply a fresh OIDC token (`iat` must be less than 60 seconds before the request) along with the client-generated public key; this is also the reauthentication path after a prior session expired. For `PASSKEY` credentials, the client completes a WebAuthn assertion (`navigator.credentials.get()`) against a fresh challenge issued by the platform backend and submits the resulting `assertion` along with the client-generated public key; Grid verifies the WebAuthn signature against the stored credential before issuing the session. On success, the response contains an `encryptedSessionSigningKey` that is encrypted to the supplied `clientPublicKey`, along with an `expiresAt` timestamp marking when the session expires. The `clientPublicKey` is ephemeral and one-time-use per verification request. operationId: verifyAuthCredential @@ -3895,6 +3895,16 @@ paths: type: OAUTH oidcToken: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + passkey: + summary: Verify a passkey credential + value: + type: PASSKEY + assertion: + credentialId: AQIDBAUGBwgJCgsMDQ4PEA + clientDataJson: eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiLi4uIiwib3JpZ2luIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSJ9 + authenticatorData: SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAQ + signature: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE + clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 responses: '200': description: Authentication credential verified and session issued @@ -3909,7 +3919,7 @@ paths: schema: $ref: '#/components/schemas/Error400' '401': - description: Unauthorized. Returned for an invalid or expired OTP (`EMAIL_OTP`) or for an OIDC token whose signature, issuer, or `iat` freshness check failed (`OAUTH`). + description: Unauthorized. Returned for an invalid or expired OTP (`EMAIL_OTP`), for an OIDC token whose signature, issuer, or `iat` freshness check failed (`OAUTH`), or for a WebAuthn assertion whose signature, challenge, or credential match failed (`PASSKEY`). content: application/json: schema: @@ -13564,15 +13574,70 @@ components: allOf: - $ref: '#/components/schemas/AuthCredentialVerifyRequest' - $ref: '#/components/schemas/OauthCredentialVerifyRequestFields' + PasskeyAssertion: + title: Passkey Assertion + type: object + required: + - credentialId + - clientDataJson + - authenticatorData + - signature + properties: + credentialId: + type: string + description: Base64url-encoded credential identifier returned during the WebAuthn assertion. Corresponds to `PublicKeyCredential.rawId`. + example: AQIDBAUGBwgJCgsMDQ4PEA + clientDataJson: + type: string + description: Base64url-encoded client data collected by the browser during the WebAuthn `navigator.credentials.get()` call. Corresponds to `AuthenticatorAssertionResponse.clientDataJSON`. + example: eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiLi4uIiwib3JpZ2luIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSJ9 + authenticatorData: + type: string + description: Base64url-encoded authenticator data returned by the authenticator during the assertion. Corresponds to `AuthenticatorAssertionResponse.authenticatorData`. + example: SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAQ + signature: + type: string + description: Base64url-encoded signature produced by the authenticator over `authenticatorData || SHA-256(clientDataJSON)`. Corresponds to `AuthenticatorAssertionResponse.signature`. + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE + userHandle: + type: string + nullable: true + description: Optional base64url-encoded user handle returned by the authenticator, if set at registration. Corresponds to `AuthenticatorAssertionResponse.userHandle`. Omit or send `null` when absent. + example: dXNlci1oYW5kbGUtZXhhbXBsZQ + PasskeyCredentialVerifyRequestFields: + type: object + required: + - type + - assertion + - clientPublicKey + properties: + type: + type: string + enum: + - PASSKEY + description: Discriminator value identifying this as a passkey verification. + assertion: + $ref: '#/components/schemas/PasskeyAssertion' + clientPublicKey: + type: string + description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + PasskeyCredentialVerifyRequest: + title: Passkey Credential Verify Request + allOf: + - $ref: '#/components/schemas/AuthCredentialVerifyRequest' + - $ref: '#/components/schemas/PasskeyCredentialVerifyRequestFields' AuthCredentialVerifyRequestOneOf: oneOf: - $ref: '#/components/schemas/EmailOtpCredentialVerifyRequest' - $ref: '#/components/schemas/OauthCredentialVerifyRequest' + - $ref: '#/components/schemas/PasskeyCredentialVerifyRequest' discriminator: propertyName: type mapping: EMAIL_OTP: '#/components/schemas/EmailOtpCredentialVerifyRequest' OAUTH: '#/components/schemas/OauthCredentialVerifyRequest' + PASSKEY: '#/components/schemas/PasskeyCredentialVerifyRequest' AuthSession: allOf: - $ref: '#/components/schemas/AuthMethod' diff --git a/openapi.yaml b/openapi.yaml index 5e7c43a9..e513165f 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -3861,7 +3861,7 @@ paths: description: | Complete the verification step for a previously created authentication credential and issue a session signing key. - For `EMAIL_OTP` credentials, supply the one-time password that was emailed to the user along with a client-generated public key. For `OAUTH` credentials, supply a fresh OIDC token (`iat` must be less than 60 seconds before the request) along with the client-generated public key; this is also the reauthentication path after a prior session expired. + For `EMAIL_OTP` credentials, supply the one-time password that was emailed to the user along with a client-generated public key. For `OAUTH` credentials, supply a fresh OIDC token (`iat` must be less than 60 seconds before the request) along with the client-generated public key; this is also the reauthentication path after a prior session expired. For `PASSKEY` credentials, the client completes a WebAuthn assertion (`navigator.credentials.get()`) against a fresh challenge issued by the platform backend and submits the resulting `assertion` along with the client-generated public key; Grid verifies the WebAuthn signature against the stored credential before issuing the session. On success, the response contains an `encryptedSessionSigningKey` that is encrypted to the supplied `clientPublicKey`, along with an `expiresAt` timestamp marking when the session expires. The `clientPublicKey` is ephemeral and one-time-use per verification request. operationId: verifyAuthCredential @@ -3895,6 +3895,16 @@ paths: type: OAUTH oidcToken: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + passkey: + summary: Verify a passkey credential + value: + type: PASSKEY + assertion: + credentialId: AQIDBAUGBwgJCgsMDQ4PEA + clientDataJson: eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiLi4uIiwib3JpZ2luIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSJ9 + authenticatorData: SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAQ + signature: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE + clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 responses: '200': description: Authentication credential verified and session issued @@ -3909,7 +3919,7 @@ paths: schema: $ref: '#/components/schemas/Error400' '401': - description: Unauthorized. Returned for an invalid or expired OTP (`EMAIL_OTP`) or for an OIDC token whose signature, issuer, or `iat` freshness check failed (`OAUTH`). + description: Unauthorized. Returned for an invalid or expired OTP (`EMAIL_OTP`), for an OIDC token whose signature, issuer, or `iat` freshness check failed (`OAUTH`), or for a WebAuthn assertion whose signature, challenge, or credential match failed (`PASSKEY`). content: application/json: schema: @@ -13564,15 +13574,70 @@ components: allOf: - $ref: '#/components/schemas/AuthCredentialVerifyRequest' - $ref: '#/components/schemas/OauthCredentialVerifyRequestFields' + PasskeyAssertion: + title: Passkey Assertion + type: object + required: + - credentialId + - clientDataJson + - authenticatorData + - signature + properties: + credentialId: + type: string + description: Base64url-encoded credential identifier returned during the WebAuthn assertion. Corresponds to `PublicKeyCredential.rawId`. + example: AQIDBAUGBwgJCgsMDQ4PEA + clientDataJson: + type: string + description: Base64url-encoded client data collected by the browser during the WebAuthn `navigator.credentials.get()` call. Corresponds to `AuthenticatorAssertionResponse.clientDataJSON`. + example: eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiLi4uIiwib3JpZ2luIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSJ9 + authenticatorData: + type: string + description: Base64url-encoded authenticator data returned by the authenticator during the assertion. Corresponds to `AuthenticatorAssertionResponse.authenticatorData`. + example: SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAQ + signature: + type: string + description: Base64url-encoded signature produced by the authenticator over `authenticatorData || SHA-256(clientDataJSON)`. Corresponds to `AuthenticatorAssertionResponse.signature`. + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE + userHandle: + type: string + nullable: true + description: Optional base64url-encoded user handle returned by the authenticator, if set at registration. Corresponds to `AuthenticatorAssertionResponse.userHandle`. Omit or send `null` when absent. + example: dXNlci1oYW5kbGUtZXhhbXBsZQ + PasskeyCredentialVerifyRequestFields: + type: object + required: + - type + - assertion + - clientPublicKey + properties: + type: + type: string + enum: + - PASSKEY + description: Discriminator value identifying this as a passkey verification. + assertion: + $ref: '#/components/schemas/PasskeyAssertion' + clientPublicKey: + type: string + description: Client-generated P-256 public key, hex-encoded in uncompressed SEC1 format (0x04 prefix followed by the 32-byte X and 32-byte Y coordinates; 130 hex characters total). The matching private key must remain on the client. Grid encrypts the session signing key returned in the response to this public key. The key is ephemeral and one-time-use per verification request. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + PasskeyCredentialVerifyRequest: + title: Passkey Credential Verify Request + allOf: + - $ref: '#/components/schemas/AuthCredentialVerifyRequest' + - $ref: '#/components/schemas/PasskeyCredentialVerifyRequestFields' AuthCredentialVerifyRequestOneOf: oneOf: - $ref: '#/components/schemas/EmailOtpCredentialVerifyRequest' - $ref: '#/components/schemas/OauthCredentialVerifyRequest' + - $ref: '#/components/schemas/PasskeyCredentialVerifyRequest' discriminator: propertyName: type mapping: EMAIL_OTP: '#/components/schemas/EmailOtpCredentialVerifyRequest' OAUTH: '#/components/schemas/OauthCredentialVerifyRequest' + PASSKEY: '#/components/schemas/PasskeyCredentialVerifyRequest' AuthSession: allOf: - $ref: '#/components/schemas/AuthMethod' diff --git a/openapi/components/schemas/auth/AuthCredentialVerifyRequestOneOf.yaml b/openapi/components/schemas/auth/AuthCredentialVerifyRequestOneOf.yaml index 8d26c0ab..254688de 100644 --- a/openapi/components/schemas/auth/AuthCredentialVerifyRequestOneOf.yaml +++ b/openapi/components/schemas/auth/AuthCredentialVerifyRequestOneOf.yaml @@ -1,8 +1,10 @@ oneOf: - $ref: ./EmailOtpCredentialVerifyRequest.yaml - $ref: ./OauthCredentialVerifyRequest.yaml + - $ref: ./PasskeyCredentialVerifyRequest.yaml discriminator: propertyName: type mapping: EMAIL_OTP: ./EmailOtpCredentialVerifyRequest.yaml OAUTH: ./OauthCredentialVerifyRequest.yaml + PASSKEY: ./PasskeyCredentialVerifyRequest.yaml diff --git a/openapi/components/schemas/auth/PasskeyAssertion.yaml b/openapi/components/schemas/auth/PasskeyAssertion.yaml new file mode 100644 index 00000000..60ecb3d7 --- /dev/null +++ b/openapi/components/schemas/auth/PasskeyAssertion.yaml @@ -0,0 +1,44 @@ +title: Passkey Assertion +type: object +required: + - credentialId + - clientDataJson + - authenticatorData + - signature +properties: + credentialId: + type: string + description: >- + Base64url-encoded credential identifier returned during the WebAuthn + assertion. Corresponds to `PublicKeyCredential.rawId`. + example: AQIDBAUGBwgJCgsMDQ4PEA + clientDataJson: + type: string + description: >- + Base64url-encoded client data collected by the browser during the + WebAuthn `navigator.credentials.get()` call. Corresponds to + `AuthenticatorAssertionResponse.clientDataJSON`. + example: eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiLi4uIiwib3JpZ2luIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSJ9 + authenticatorData: + type: string + description: >- + Base64url-encoded authenticator data returned by the authenticator + during the assertion. Corresponds to + `AuthenticatorAssertionResponse.authenticatorData`. + example: SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAQ + signature: + type: string + description: >- + Base64url-encoded signature produced by the authenticator over + `authenticatorData || SHA-256(clientDataJSON)`. Corresponds to + `AuthenticatorAssertionResponse.signature`. + example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE + userHandle: + type: string + nullable: true + description: >- + Optional base64url-encoded user handle returned by the authenticator, + if set at registration. Corresponds to + `AuthenticatorAssertionResponse.userHandle`. Omit or send `null` + when absent. + example: dXNlci1oYW5kbGUtZXhhbXBsZQ diff --git a/openapi/components/schemas/auth/PasskeyCredentialVerifyRequest.yaml b/openapi/components/schemas/auth/PasskeyCredentialVerifyRequest.yaml new file mode 100644 index 00000000..1592544f --- /dev/null +++ b/openapi/components/schemas/auth/PasskeyCredentialVerifyRequest.yaml @@ -0,0 +1,4 @@ +title: Passkey Credential Verify Request +allOf: + - $ref: ./AuthCredentialVerifyRequest.yaml + - $ref: ./PasskeyCredentialVerifyRequestFields.yaml diff --git a/openapi/components/schemas/auth/PasskeyCredentialVerifyRequestFields.yaml b/openapi/components/schemas/auth/PasskeyCredentialVerifyRequestFields.yaml new file mode 100644 index 00000000..c4de8bc6 --- /dev/null +++ b/openapi/components/schemas/auth/PasskeyCredentialVerifyRequestFields.yaml @@ -0,0 +1,23 @@ +type: object +required: + - type + - assertion + - clientPublicKey +properties: + type: + type: string + enum: + - PASSKEY + description: Discriminator value identifying this as a passkey verification. + assertion: + $ref: ./PasskeyAssertion.yaml + clientPublicKey: + type: string + description: >- + Client-generated P-256 public key, hex-encoded in uncompressed SEC1 + format (0x04 prefix followed by the 32-byte X and 32-byte Y + coordinates; 130 hex characters total). The matching private key + must remain on the client. Grid encrypts the session signing key + returned in the response to this public key. The key is ephemeral + and one-time-use per verification request. + example: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 diff --git a/openapi/paths/auth/auth_credentials_{id}_verify.yaml b/openapi/paths/auth/auth_credentials_{id}_verify.yaml index c847f356..02244147 100644 --- a/openapi/paths/auth/auth_credentials_{id}_verify.yaml +++ b/openapi/paths/auth/auth_credentials_{id}_verify.yaml @@ -10,7 +10,12 @@ post: `OAUTH` credentials, supply a fresh OIDC token (`iat` must be less than 60 seconds before the request) along with the client-generated public key; this is also the reauthentication path after a prior - session expired. + session expired. For `PASSKEY` credentials, the client completes a + WebAuthn assertion (`navigator.credentials.get()`) against a fresh + challenge issued by the platform backend and submits the resulting + `assertion` along with the client-generated public key; Grid verifies + the WebAuthn signature against the stored credential before issuing + the session. On success, the response contains an `encryptedSessionSigningKey` @@ -51,6 +56,16 @@ post: type: OAUTH oidcToken: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 + passkey: + summary: Verify a passkey credential + value: + type: PASSKEY + assertion: + credentialId: AQIDBAUGBwgJCgsMDQ4PEA + clientDataJson: eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiLi4uIiwib3JpZ2luIjoiaHR0cHM6Ly9leGFtcGxlLmNvbSJ9 + authenticatorData: SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MdAAAAAQ + signature: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE + clientPublicKey: 04f45f2a22c908b9ce09a7150e514afd24627c401c38a4afc164e1ea783adaaa31d4245acfb88c2ebd42b47628d63ecabf345484f0a9f665b63c54c897d5578be2 responses: '200': description: Authentication credential verified and session issued @@ -66,9 +81,10 @@ post: $ref: ../../components/schemas/errors/Error400.yaml '401': description: >- - Unauthorized. Returned for an invalid or expired OTP (`EMAIL_OTP`) - or for an OIDC token whose signature, issuer, or `iat` freshness - check failed (`OAUTH`). + Unauthorized. Returned for an invalid or expired OTP (`EMAIL_OTP`), + for an OIDC token whose signature, issuer, or `iat` freshness + check failed (`OAUTH`), or for a WebAuthn assertion whose + signature, challenge, or credential match failed (`PASSKEY`). content: application/json: schema: