Skip to content

Commit 03dc1c7

Browse files
committed
docs: document realistic sandbox passkeys
1 parent 0047de2 commit 03dc1c7

5 files changed

Lines changed: 25 additions & 9 deletions

File tree

.github/workflows/stainless-action.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
org: ${{ env.STAINLESS_ORG }}
3838
project: ${{ env.STAINLESS_PROJECT }}
3939
oas_path: ${{ env.OAS_PATH }}
40-
guess_config: true
40+
config_path: .stainless/stainless.yml
4141

4242
merge:
4343
if: github.event.action == 'closed' && github.event.pull_request.merged == true
@@ -58,3 +58,4 @@ jobs:
5858
org: ${{ env.STAINLESS_ORG }}
5959
project: ${{ env.STAINLESS_PROJECT }}
6060
oas_path: ${{ env.OAS_PATH }}
61+
config_path: .stainless/stainless.yml

mintlify/openapi.yaml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mintlify/snippets/sandbox-global-account-magic.mdx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
The Grid sandbox accepts a small set of magic values for Global Account flows, so you can exercise the full request shape without standing up Turnkey, WebAuthn, or an OIDC provider. OTP, passkey, and wallet signatures use fixed sandbox-only values. OAuth uses JWT-shaped sandbox OIDC tokens: sandbox skips real IdP signature verification, but still validates the token claims, freshness, credential identity, and verify-time nonce binding.
1+
The Grid sandbox lets you exercise Global Account auth flows without moving real money. Email OTP uses the fixed sandbox code `000000`. Passkey auth can use the same browser WebAuthn ceremony as production. Wallet signatures use fixed sandbox-only values. OAuth uses JWT-shaped sandbox OIDC tokens: sandbox skips real IdP signature verification, but still validates token claims, freshness, credential identity, and verify-time nonce binding.
22

3-
A wrong magic value or sandbox OIDC authentication failure returns `401 UNAUTHORIZED` with a `reason` field that names the specific check that failed. A malformed OIDC JWT can return `400 INVALID_INPUT` before authentication starts.
3+
Sandbox-only compatibility values are still available for some flows, but they do not exercise the production-shaped client implementation. Authentication failures return `401 UNAUTHORIZED` with a `reason` field that names the specific check that failed. A malformed OIDC JWT can return `400 INVALID_INPUT` before authentication starts.
44

55
### Email OTP code
66

@@ -20,11 +20,17 @@ curl -X POST https://api.lightspark.com/grid/2025-10-13/auth/credentials/AuthMet
2020

2121
Any other code returns `401 UNAUTHORIZED` with `reason: "Invalid OTP code"`.
2222

23-
### Passkey assertion signature
23+
### Passkey WebAuthn ceremony
2424

25-
Pass `sandbox-valid-passkey-signature` as `assertion.signature` on `POST /auth/credentials/{id}/verify` when the credential type is `PASSKEY`. The sandbox accepts the rest of the assertion as-is and skips the WebAuthn signature check.
25+
For new sandbox integrations, use the same WebAuthn calls you plan to use in production.
2626

27-
Passkey reauthentication is a two-step `/challenge``/verify` flow. The `clientPublicKey` is sent on `/challenge` (so Grid can seal the session signing key to your device) — the magic value bypasses the credential check, not the HPKE plumbing, so the public key is still required.
27+
1. Generate your own WebAuthn registration challenge and call `navigator.credentials.create()`.
28+
2. Register the passkey with `POST /auth/credentials`, passing the challenge and attestation returned by the browser.
29+
3. Reauthenticate with `POST /auth/credentials/{id}/challenge`, passing the P-256 `clientPublicKey` that Grid should seal the session signing key to.
30+
4. Pass the returned `challenge` into `navigator.credentials.get()` using the returned `credentialId` in `allowCredentials`.
31+
5. Verify with `POST /auth/credentials/{id}/verify`, passing the browser assertion and echoing `Request-Id` from the challenge response.
32+
33+
The sandbox validates the registered credential ID, WebAuthn challenge, origin/RP binding, user-presence bit, assertion signature, and signature counter. A successful verify response includes `encryptedSessionSigningKey`, sealed to the `clientPublicKey`, just like production.
2834

2935
```bash
3036
# 1. /challenge with clientPublicKey
@@ -35,7 +41,7 @@ curl -X POST https://api.lightspark.com/grid/2025-10-13/auth/credentials/AuthMet
3541
"clientPublicKey": "04f45f2a..."
3642
}'
3743

38-
# 2. /verify with the magic signature, no clientPublicKey
44+
# 2. /verify with the browser assertion returned by navigator.credentials.get()
3945
curl -X POST https://api.lightspark.com/grid/2025-10-13/auth/credentials/AuthMethod:abc123/verify \
4046
-u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET" \
4147
-H "Content-Type: application/json" \
@@ -46,12 +52,14 @@ curl -X POST https://api.lightspark.com/grid/2025-10-13/auth/credentials/AuthMet
4652
"credentialId": "...",
4753
"clientDataJson": "...",
4854
"authenticatorData": "...",
49-
"signature": "sandbox-valid-passkey-signature"
55+
"signature": "..."
5056
}
5157
}'
5258
```
5359

54-
Any other signature returns `401 UNAUTHORIZED` with `reason: "Invalid passkey signature"`.
60+
<Note>
61+
The legacy sandbox-only assertion signature `sandbox-valid-passkey-signature` is still accepted for compatibility, but it skips WebAuthn verification and should not be used for production-shaped sandbox tests.
62+
</Note>
5563

5664
### OAuth (OIDC) token
5765

openapi.yaml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

openapi/components/schemas/auth/PasskeyAssertion.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
title: Passkey Assertion
22
type: object
3+
description: >-
4+
WebAuthn assertion returned by `navigator.credentials.get()`. In sandbox,
5+
Grid validates the assertion against the registered passkey credential so the
6+
client-side flow can match production. In production, Turnkey validates the
7+
WebAuthn assertion.
38
required:
49
- credentialId
510
- clientDataJson

0 commit comments

Comments
 (0)