Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions mintlify/snippets/global-accounts/authentication.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ Global Accounts are initialized with an `EMAIL_OTP` credential tied to the custo

To produce a session:

- **`EMAIL_OTP`** and **`OAUTH`** — call **`POST /auth/credentials/{id}/verify`** with the OTP value (or a fresh OIDC token) plus a `clientPublicKey`. The response carries the `encryptedSessionSigningKey`.
- **`EMAIL_OTP`** — if you need a fresh OTP or encryption target, first call **`POST /auth/credentials/{id}/challenge`**. Generate a P-256 client key pair, HPKE-encrypt the OTP value plus the public key into `encryptedOtpBundle`, then call **`POST /auth/credentials/{id}/verify`**. Grid returns a `202` signed-retry challenge; stamp its `payloadToSign` with the same private key and retry with `Grid-Wallet-Signature` plus `Request-Id`. The signed retry returns the session; the private key you generated is the session signing key, so `EMAIL_OTP` responses do not include `encryptedSessionSigningKey`.
- **`OAUTH`** — call **`POST /auth/credentials/{id}/verify`** with a fresh OIDC token plus `clientPublicKey`. The response carries the `encryptedSessionSigningKey`.
- **`PASSKEY`** — call **`POST /auth/credentials/{id}/challenge`** with your `clientPublicKey` to receive a Grid-issued `challenge` and `requestId`, UTF-8 encode the challenge string for `navigator.credentials.get()`, then call **`POST /auth/credentials/{id}/verify`** with the resulting assertion and the `Request-Id` header. Grid bakes the `clientPublicKey` from the `/challenge` call into the session-creation payload, so it does **not** appear on the `/verify` body.

To add another credential, call **`POST /auth/credentials`** with the new credential details. Because the account already has an `EMAIL_OTP` credential, Grid returns a `202` signed-retry challenge. Stamp that `payloadToSign` with an active session signing key, then retry the same request with `Grid-Wallet-Signature` and `Request-Id`. The signed retry returns the new `AuthMethod`.
Expand Down Expand Up @@ -221,9 +222,14 @@ suspend fun registerPasskey(context: Context, api: MyBackendApi): EmbeddedWallet
clientPublicKeyHex = clientKeys.publicKeyHex,
)

// 6. Run WebAuthn assertion against Grid-issued challenge.
// 6. Run WebAuthn assertion against Grid-issued challenge. The returned
// value is a lowercase hex string; UTF-8 encode it to bytes, then put
// those bytes into the platform WebAuthn request format.
val webAuthnChallenge = base64UrlEncode(
challengeResp.challenge.toByteArray(Charsets.UTF_8),
)
Comment thread
greptile-apps[bot] marked this conversation as resolved.
val getOptionsJson = buildWebAuthnGetOptionsJson(
challenge = challengeResp.challenge,
challenge = webAuthnChallenge,
rpId = registerResp.rpId,
credentialId = registerResp.credentialId,
)
Expand Down Expand Up @@ -302,9 +308,10 @@ final class PasskeyCoordinator: NSObject, ASAuthorizationControllerDelegate {
clientPublicKeyHex: keys.publicKeyHex,
)

// 6. Run WebAuthn assertion against Grid-issued challenge.
// 6. Run WebAuthn assertion against Grid-issued challenge. The returned
// value is a lowercase hex string; UTF-8 encode it as challenge bytes.
let assertRequest = provider.createCredentialAssertionRequest(
challenge: challengeResp.challenge,
challenge: Data(challengeResp.challenge.utf8),
)
assertRequest.allowedCredentials = [
ASAuthorizationPlatformPublicKeyCredentialDescriptor(
Expand Down
Loading