---
title: "Verify an authentication credential"
method: POST
path: "/auth/credentials/{id}/verify"
tags: ["Embedded Wallet Auth"]
---

# Verify an authentication credential

`POST /auth/credentials/{id}/verify`

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 `PASSKEY` credentials, the client completes a WebAuthn assertion (`navigator.credentials.get()`) against the Grid-issued `challenge` returned from either `POST /auth/credentials` (first authentication) or `POST /auth/credentials/{id}/challenge` (reauthentication), and submits the resulting `assertion` along with the client-generated public key. The `requestId` that accompanied the challenge must be echoed in the `Request-Id` header so Grid can correlate the assertion with the pending challenge; 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.

## Path parameters

- `id` string, required

## Headers

- `Request-Id` string

## Request body

- union
  - EmailOtpCredentialVerifyRequest
    - `type` 'EMAIL_OTP', required — Discriminator value identifying this as an email OTP verification.
    - `otp` string, required — The one-time password received by the user via email.
    - `clientPublicKey` string, required — 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.
  - OauthCredentialVerifyRequest
    - `type` 'OAUTH', required — Discriminator value identifying this as an OAuth verification.
    - `oidcToken` string, required — OIDC ID token issued by the identity provider. For reauthentication after a prior session expired, supply a fresh token — the token's `iat` claim must be less than 60 seconds before the request timestamp. Grid fetches the issuer's signing key from the `iss` claim's `.well-known` OpenID configuration and verifies the token signature.
    - `clientPublicKey` string, required — 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.
  - PasskeyCredentialVerifyRequest
    - `type` 'PASSKEY', required — Discriminator value identifying this as a passkey verification.
    - `assertion` PasskeyAssertion, required
      - `credentialId` string, required — Base64url-encoded credential identifier returned during the WebAuthn assertion. Corresponds to `PublicKeyCredential.rawId`.
      - `clientDataJson` string, required — Base64url-encoded JSON client data collected by the browser during the WebAuthn `navigator.credentials.get()` call. Corresponds to `AuthenticatorAssertionResponse.clientDataJSON` from the WebAuthn spec — Grid's field name is intentionally camelCased as `clientDataJson` (lowercase JSON) for consistency with the rest of the API; the value is the same bytes the browser returns. Contains the challenge, origin, and `type: "webauthn.get"`.
      - `authenticatorData` string, required — Base64url-encoded authenticator data returned by the authenticator during the assertion. Corresponds to `AuthenticatorAssertionResponse.authenticatorData`.
      - `signature` string, required — Base64url-encoded signature produced by the authenticator over `authenticatorData || SHA-256(clientDataJSON)`. Corresponds to `AuthenticatorAssertionResponse.signature`. The signature byte format is determined by the credential's public-key algorithm — DER-encoded ECDSA for ES256 (P-256, typical for passkeys), PKCS#1 v1.5 for RS256, or a raw 64-byte signature for EdDSA.
      - `userHandle` string — Base64url-encoded user handle returned by the authenticator. Corresponds to `AuthenticatorAssertionResponse.userHandle`. Populated (and required by the WebAuthn spec) for discoverable credentials — resident keys used in the "Sign in with passkey" autofill flow — and typically present for passkey registrations. Omit this field entirely for non-discoverable credentials specified via `allowCredentials` where the authenticator returns no user handle.
    - `clientPublicKey` string, required — 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.

## Response `200`

Authentication credential verified and session issued

- AuthSession — An authentication session on an Embedded Wallet internal account. Returned from `GET /auth/sessions` (list) and `POST /auth/credentials/{id}/verify` (on credential verification). Only the verify response includes `encryptedSessionSigningKey` — it is delivered exactly once at the moment the session is issued and is never returned by the list endpoint.
  - `id` string, required — System-generated unique identifier for the session. Pass this value to `DELETE /auth/sessions/{id}` to revoke the session before `expiresAt`. Overrides the `id` inherited from `AuthMethod` so this response identifies the session rather than the authenticating credential.
  - `accountId` string, required — Identifier of the internal account that this credential authenticates.
  - `type` 'OAUTH' | 'EMAIL_OTP' | 'PASSKEY', required — The type of authentication credential. - `OAUTH`: OpenID Connect (OIDC) token issued by an identity provider such as Google or Apple. - `EMAIL_OTP`: A one-time password delivered to the user's email address. - `PASSKEY`: A WebAuthn passkey bound to the user's device.
  - `nickname` string, required — Human-readable identifier for this credential. For EMAIL_OTP credentials this is the email address; for OAUTH credentials it is typically the email claim from the OIDC token; for PASSKEY credentials it is the nickname provided at registration time.
  - `createdAt` string, date-time, required — Creation timestamp.
  - `updatedAt` string, date-time, required — Last update timestamp.
  - `encryptedSessionSigningKey` string — HPKE-encrypted session signing key, sealed to the `clientPublicKey` supplied on the verify request. Encoded as a base58check string: the decoded payload is a 33-byte compressed P-256 encapsulated public key followed by AES-256-GCM ciphertext. The client decrypts this key with its private key and uses it to sign subsequent Embedded Wallet requests until `expiresAt`. Only returned from `POST /auth/credentials/{id}/verify` (where the session is first issued). Omitted from responses that simply surface existing sessions (e.g. `GET /auth/sessions`) — Grid does not retain the plaintext key after the client has decrypted it.
  - `expiresAt` string, date-time, required — Timestamp after which the session is no longer valid and the `encryptedSessionSigningKey` must not be used to sign further requests.

## Other responses

- `400` — Bad request
- `401` — 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`). Also returned for `PASSKEY` when `Request-Id` is missing, does not match an unexpired pending challenge for this credential, or was already consumed.
- `404` — Authentication credential not found
- `500` — Internal service error

## Changes

- **2026-04-27** `5b8a8161eeb3` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/lightsparkdev/apis/grid-api/changes/auth/credentials/:id/verify/post.md)

---

[API](https://skmtc.dev/lightsparkdev/apis/grid-api.md) · [All operations](https://skmtc.dev/lightsparkdev/apis/grid-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/lightsparkdev/grid-api/revisions/5b8a8161eeb3/schema)
