Skip to content

Update auth credential /challenge body#407

Merged
carsonp6 merged 3 commits intomainfrom
04-27-update_auth_credential__challenge_body
Apr 28, 2026
Merged

Update auth credential /challenge body#407
carsonp6 merged 3 commits intomainfrom
04-27-update_auth_credential__challenge_body

Conversation

@carsonp6
Copy link
Copy Markdown
Contributor

@carsonp6 carsonp6 commented Apr 28, 2026

TL;DR

The POST /auth/credentials/{id}/challenge endpoint now accepts a request body containing a clientPublicKey for passkey credential re-challenges.

What changed?

The challengeAuthCredential endpoint now accepts an optional JSON request body with a clientPublicKey field. For PASSKEY credentials, this field is required and must be a client-generated P-256 public key in hex-encoded uncompressed SEC1 format (130 hex characters, 04-prefixed). Grid bakes this key into the Turnkey session-creation payload that the returned challenge is computed from, sealing the resulting session signing key to the client.

The field is ignored for EMAIL_OTP and OAUTH credentials, where the credential type alone is sufficient to issue a challenge. Two request body examples are documented: one for passkey re-challenges (with clientPublicKey) and one for email-OTP or OAuth re-challenges (empty body).

How to test?

  • Issue a POST /auth/credentials/{id}/challenge for a PASSKEY credential with a valid clientPublicKey in the request body and confirm a PasskeyAuthChallenge is returned.
  • Issue the same request without a clientPublicKey for a PASSKEY credential and confirm an appropriate error is returned.
  • Issue a POST /auth/credentials/{id}/challenge for an EMAIL_OTP or OAUTH credential with an empty body and confirm the challenge is issued successfully.

Why make this change?

Previously, the challenge endpoint had no way to bind the resulting Turnkey session to a specific client. By requiring the client to supply its ephemeral public key upfront, Grid can embed it into the session-creation payload before computing the challenge, ensuring the session signing key is cryptographically sealed to the client that initiated the challenge.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
grid-flow-builder Ready Ready Preview, Comment Apr 28, 2026 3:46am

Request Review

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@carsonp6 carsonp6 marked this pull request as ready for review April 28, 2026 02:59
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 28, 2026

Greptile Summary

This PR updates the POST /auth/credentials/{id}/challenge endpoint to accept an optional JSON request body containing a clientPublicKey field, which is required for PASSKEY credentials so Grid can bind the resulting Turnkey session signing key to the specific client. The previous reviewer's concern about missing schema constraints (pattern, minLength, maxLength) has been addressed in the new AuthCredentialChallengeRequest schema.

Confidence Score: 5/5

Safe to merge — purely OpenAPI spec documentation changes with no implementation code, and the schema constraints are correctly applied.

No P0 or P1 findings. The only pre-existing P2 concern (OAUTH credential response behaviour undocumented) was already flagged in a previous review. The schema pattern, length constraints, and example values are all correct.

No files require special attention.

Important Files Changed

Filename Overview
openapi/components/schemas/auth/AuthCredentialChallengeRequest.yaml New schema for the challenge request body; includes correct pattern, minLength, and maxLength constraints on clientPublicKey. No required array, which is appropriate since the field is only conditionally required at the server level.
openapi/paths/auth/auth_credentials_{id}_challenge.yaml Adds requestBody (required: false) referencing the new schema, with two examples (passkey and emailOtpOrOauth). Updated PASSKEY description to mention the clientPublicKey requirement. OAUTH credential response behaviour remains undocumented in the endpoint description (pre-existing concern).
openapi.yaml Bundled spec — mirrors the modular YAML changes: adds requestBody, new AuthCredentialChallengeRequest component schema, and updated PASSKEY description.
mintlify/openapi.yaml Mintlify-targeted bundled spec — identical changes to openapi.yaml; no issues.

Sequence Diagram

sequenceDiagram
    participant C as Client
    participant G as Grid API
    participant T as Turnkey

    Note over C,T: PASSKEY re-challenge flow (new)
    C->>C: Generate ephemeral P-256 key pair
    C->>G: POST /auth/credentials/{id}/challenge
    Note right of C: { clientPublicKey: "04..." }
    G->>G: Validate clientPublicKey format
    Note right of G: 04-prefix, 130 hex chars
    G->>T: Create session payload baking in clientPublicKey
    T-->>G: PasskeyAuthChallenge (challenge, requestId, expiresAt)
    G-->>C: 200 PasskeyAuthChallenge
    C->>C: navigator.credentials.get(challenge)
    C->>G: POST /auth/credentials/{id}/verify
    Note right of C: Request-Id: requestId

    Note over C,G: EMAIL_OTP / OAUTH re-challenge flow (unchanged)
    C->>G: POST /auth/credentials/{id}/challenge
    Note right of C: {} (empty body)
    G-->>C: 200 AuthMethod (OTP sent out-of-band)
Loading

Reviews (2): Last reviewed commit: "Address review feedback: extract challen..." | Re-trigger Greptile

Comment thread openapi/paths/auth/auth_credentials_{id}_challenge.yaml Outdated
…chema, add P-256 hex constraints

- Move the inline `requestBody` schema out of `auth_credentials_{id}_challenge.yaml`
  into a new `AuthCredentialChallengeRequest.yaml` component to satisfy the
  `no-inline-request-schema` redocly rule.
- Apply `pattern`, `minLength`, `maxLength` to `clientPublicKey` so generated
  validators enforce the 130-char `04`-prefixed P-256 hex format the
  description already documents (greptile review feedback).
- Regenerate the bundled `openapi.yaml` and `mintlify/openapi.yaml`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@carsonp6
Copy link
Copy Markdown
Contributor Author

@greptileai

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 28, 2026

✱ Stainless preview builds

This PR will update the grid SDKs with the following commit messages.

kotlin

feat(api): add clientPublicKey parameter to credential resendChallenge method

openapi

feat(api): add clientPublicKey parameter to auth credentials challenge

python

feat(api): add client_public_key parameter to credentials resend_challenge

typescript

feat(api): add clientPublicKey param to auth credentials resendChallenge
grid-openapi studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅

grid-python studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅build ⏭️lint ⏭️test ⏭️

grid-kotlin studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅build ⏭️lint ⏭️test ⏭️

grid-typescript studio · code

Your SDK build had at least one "note" diagnostic.
generate ✅build ⏭️lint ⏭️test ⏭️


This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-04-28 04:54:37 UTC

The previous spec said `POST /auth/credentials` for `PASSKEY` returned a
`PasskeyAuthChallenge` with the first-authentication challenge inline.
Implementation never did this — it returns a plain `AuthMethod` and
expects the integrator to call `POST /auth/credentials/{id}/challenge`
followed by `POST /auth/credentials/{id}/verify` to produce the first
session. Update the spec to match: registration is registration,
authentication goes through the dedicated `/challenge` + `/verify` path
for all three credential types.

Concretely:
- `auth_credentials.yaml`: 201 description, PASSKEY example, and
  endpoint description updated; response schema switched from
  `AuthCredentialResponseOneOf` to `AuthMethodResponse` directly
  (uniform across all three credential types).
- `AuthCredentialResponseOneOf.yaml` description: drop the registration
  reference; this oneOf is now used only by `/challenge`.
- `AuthMethodResponse.yaml` description: note both uses (direct
  registration response + EMAIL_OTP/OAUTH branch of the challenge oneOf).
- Regenerate bundled `openapi.yaml` and `mintlify/openapi.yaml`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@carsonp6 carsonp6 merged commit a95bc2e into main Apr 28, 2026
8 checks passed
@carsonp6 carsonp6 deleted the 04-27-update_auth_credential__challenge_body branch April 28, 2026 04:52
pengying added a commit that referenced this pull request Apr 28, 2026
Drops clientPublicKey from the required list on all three verify
request bodies (EmailOtpCredentialVerifyRequestFields,
OauthCredentialVerifyRequestFields, PasskeyCredentialVerifyRequestFields).

PASSKEY verify no longer needs the field at all — clientPublicKey
moves to /auth/credentials/{id}/challenge in #407, and Grid bakes it
into the session-creation payload there. Sending it on /verify is
inert.

EMAIL_OTP and OAUTH still use the field on /verify (that's where the
session signing key is sealed for those types), but making it optional
keeps the schema flexible if a client supplies it via a different path
in the future, and avoids a breaking 4xx for callers that omit it.

Field definition stays in place on each schema — only the 'required'
constraint is dropped. Bundle regenerated via 'make build'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pengying added a commit that referenced this pull request Apr 28, 2026
Drops clientPublicKey from the required list on all three verify
request bodies (EmailOtpCredentialVerifyRequestFields,
OauthCredentialVerifyRequestFields, PasskeyCredentialVerifyRequestFields).

PASSKEY verify no longer needs the field at all — clientPublicKey
moves to /auth/credentials/{id}/challenge in #407, and Grid bakes it
into the session-creation payload there. Sending it on /verify is
inert.

EMAIL_OTP and OAUTH still use the field on /verify (that's where the
session signing key is sealed for those types), but making it optional
keeps the schema flexible if a client supplies it via a different path
in the future, and avoids a breaking 4xx for callers that omit it.

Field definition stays in place on each schema — only the 'required'
constraint is dropped. Bundle regenerated via 'make build'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pengying added a commit that referenced this pull request Apr 28, 2026
Reflects the API change in #407 — for PASSKEY credentials,
clientPublicKey now goes on POST /auth/credentials/{id}/challenge
instead of /verify. Grid bakes the public key into the Turnkey
session-creation payload that the returned challenge is computed
from, so the resulting session signing key is sealed to the device
that requested the challenge.

Knock-on flow change: registration response is now a plain AuthMethod
(no inline PasskeyAuthChallenge). Both first-time activation and
reauthentication for PASSKEY follow the same three-step shape:
register → /challenge (with clientPublicKey) → /verify (with
Request-Id, no clientPublicKey).

EMAIL_OTP and OAUTH are unchanged — clientPublicKey stays on /verify
for those types; /challenge takes an empty body.

Updates:
- snippets/global-accounts/authentication.mdx — overview, passkey
  registration sequence diagram + TS/Kotlin/Swift code samples,
  parameter map split into challenge + assertion tables, passkey
  reauth diagram, additional-credential activation prose
- snippets/global-accounts/overview.mdx — Quickstart Step 7 now
  generates the keypair before calling /challenge with
  clientPublicKey, and the /verify body drops it
- snippets/global-accounts/client-keys.mdx — clarifies which call
  carries clientPublicKey for each credential type
- snippets/sandbox-global-account-magic.mdx — passkey example now
  shows the /challenge + /verify pair; verify body drops
  clientPublicKey

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pengying added a commit that referenced this pull request Apr 28, 2026
Drops clientPublicKey from the required list on all three verify
request bodies (EmailOtpCredentialVerifyRequestFields,
OauthCredentialVerifyRequestFields,
PasskeyCredentialVerifyRequestFields).

PASSKEY verify no longer needs the field at all — clientPublicKey
moves to /auth/credentials/{id}/challenge in #407, and Grid bakes it
into the session-creation payload there. Sending it on /verify is
inert.

EMAIL_OTP and OAUTH still use the field on /verify (that's where the
session signing key is sealed for those types), but making it optional
keeps the schema flexible if a client supplies it via a different path
in the future, and avoids a breaking 4xx for callers that omit it.

Field definition stays in place on each schema — only the 'required'
constraint is dropped. Bundle regenerated via 'make build'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants