feat: add PASSKEY branch to auth credential create#363
Conversation
Adds the PASSKEY branch to `AuthCredentialCreateRequestOneOf`, letting platforms register a WebAuthn passkey as an authentication credential on an Embedded Wallet internal account.
**Request shape**
- `POST /auth/credentials` body: `{ type: "PASSKEY", accountId, nickname, challenge, attestation }` → 201 `AuthMethod`.
**Schemas added**
- `PasskeyAttestation` — `{ credentialId, clientDataJson, attestationObject, transports }`, all base64url-encoded fields plus a W3C-enum-validated `transports` array (values: `usb | nfc | ble | internal | hybrid | smart-card`).
- `PasskeyCredentialCreateRequestFields` — `{ type: "PASSKEY", nickname, challenge, attestation }` (variant single-value enum on `type`).
- `PasskeyCredentialCreateRequest` — `allOf(AuthCredentialCreateRequest, PasskeyCredentialCreateRequestFields)`; wire body is `{ type, accountId, nickname, challenge, attestation }` (accountId inherited from base).
**Wire-up**
- `AuthCredentialCreateRequestOneOf.yaml` discriminator map extended with `PASSKEY → PasskeyCredentialCreateRequest`.
- PASSKEY request example added to `POST /auth/credentials`.
- Endpoint description updated to document the PASSKEY path: the platform backend issues the WebAuthn challenge (not Grid), the client completes `navigator.credentials.create()`, and the resulting `attestation` is submitted here. Activation still happens via `/verify`.
- `.stainless/stainless.yml` registers the three new schemas under `auth.credentials`.
**Design notes**
- `transports` values at the Grid API surface are the raw W3C `AuthenticatorTransport` strings as returned by `AuthenticatorAttestationResponse.getTransports()`. Clients pass them through verbatim; provider-specific translation is handled server-side. This keeps the API surface decoupled from the downstream passkey provider.
- `nickname` is user-chosen at registration (unlike EMAIL_OTP where it's derived from the email address) and appears on `AuthMethod` responses for credential listings.
- Multiple passkey credentials per internal account are allowed (no `PASSKEY_CREDENTIAL_ALREADY_EXISTS`); follows the OAUTH precedent.
**Notes**
- This PR only wires the create flow; `POST /auth/credentials/{id}/verify` gets its own PASSKEY branch in the next PR in the stack.
- Bundled `openapi.yaml` and `mintlify/openapi.yaml` regenerated via `make build`.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
✱ Stainless preview buildsThis PR will update the kotlin openapi python typescript Edit this comment to update them. They will appear in their respective SDK's changelogs. ✅ grid-openapi studio · code · diff
✅ grid-typescript studio · code · diff
✅ grid-python studio · code · diff
✅ grid-kotlin studio · code · diff
This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push. |

Adds the PASSKEY branch to
AuthCredentialCreateRequestOneOf, letting platforms register a WebAuthn passkey as an authentication credential on an Embedded Wallet internal account.Request shape
POST /auth/credentialsbody:{ type: "PASSKEY", accountId, nickname, challenge, attestation }→ 201AuthMethod.Schemas added
PasskeyAttestation—{ credentialId, clientDataJson, attestationObject, transports }, all base64url-encoded fields plus a W3C-enum-validatedtransportsarray (values:usb | nfc | ble | internal | hybrid | smart-card).PasskeyCredentialCreateRequestFields—{ type: "PASSKEY", nickname, challenge, attestation }(variant single-value enum ontype).PasskeyCredentialCreateRequest—allOf(AuthCredentialCreateRequest, PasskeyCredentialCreateRequestFields); wire body is{ type, accountId, nickname, challenge, attestation }(accountId inherited from base).Wire-up
AuthCredentialCreateRequestOneOf.yamldiscriminator map extended withPASSKEY → PasskeyCredentialCreateRequest.POST /auth/credentials.navigator.credentials.create(), and the resultingattestationis submitted here. Activation still happens via/verify..stainless/stainless.ymlregisters the three new schemas underauth.credentials.Design notes
transportsvalues at the Grid API surface are the raw W3CAuthenticatorTransportstrings as returned byAuthenticatorAttestationResponse.getTransports(). Clients pass them through verbatim; provider-specific translation is handled server-side. This keeps the API surface decoupled from the downstream passkey provider.nicknameis user-chosen at registration (unlike EMAIL_OTP where it's derived from the email address) and appears onAuthMethodresponses for credential listings.PASSKEY_CREDENTIAL_ALREADY_EXISTS); follows the OAUTH precedent.Notes
POST /auth/credentials/{id}/verifygets its own PASSKEY branch in the next PR in the stack.openapi.yamlandmintlify/openapi.yamlregenerated viamake build.