From ad0846b316dd1e1b783e26b587b45131315e0525 Mon Sep 17 00:00:00 2001 From: Dhruv Pareek Date: Tue, 21 Apr 2026 18:38:36 -0700 Subject: [PATCH] feat: add OAUTH branch to auth credential create MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the OAUTH branch to `AuthCredentialCreateRequestOneOf`, letting platforms register an OIDC-backed authentication credential on an Embedded Wallet internal account. **Request shape** - `POST /auth/credentials` body: `{ type: "OAUTH", accountId, oidcToken }` → 201 `AuthMethod`. **Schemas added** - `OauthCredentialCreateRequestFields` — `{ type: "OAUTH", oidcToken }` (variant single-value enum on `type`). - `OauthCredentialCreateRequest` — `allOf(AuthCredentialCreateRequest, OauthCredentialCreateRequestFields)`; wire body is `{ type, accountId, oidcToken }` (accountId inherited from the base). **Wire-up** - `AuthCredentialCreateRequestOneOf.yaml` discriminator map extended with `OAUTH → OauthCredentialCreateRequest`. - OAuth request example added to `POST /auth/credentials`. - Endpoint description updated to note OAuth's inline `iat`/`iss` validation and that activation still goes through `/verify`. - `.stainless/stainless.yml` registers the two new schemas under `auth.credentials`. **OIDC constraints (documented on the `oidcToken` field)** - Grid fetches the issuer's signing key from the `iss` claim's `.well-known` OpenID configuration and verifies the token signature. - Token's `iat` claim must be less than 60 seconds before the request timestamp. **Notes** - Multiple OAuth credentials are allowed per internal account (no analogue to `EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS`). - This PR only wires the create flow; `POST /auth/credentials/{id}/verify` gets its own OAUTH branch in the next PR in the stack. - Bundled `openapi.yaml` and `mintlify/openapi.yaml` regenerated via `make build`. --- .stainless/stainless.yml | 2 ++ mintlify/openapi.yaml | 30 ++++++++++++++++++- openapi.yaml | 30 ++++++++++++++++++- .../AuthCredentialCreateRequestOneOf.yaml | 2 ++ .../auth/OauthCredentialCreateRequest.yaml | 4 +++ .../OauthCredentialCreateRequestFields.yaml | 19 ++++++++++++ openapi/paths/auth/auth_credentials.yaml | 12 +++++++- 7 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 openapi/components/schemas/auth/OauthCredentialCreateRequest.yaml create mode 100644 openapi/components/schemas/auth/OauthCredentialCreateRequestFields.yaml diff --git a/.stainless/stainless.yml b/.stainless/stainless.yml index 37082310..351225cc 100644 --- a/.stainless/stainless.yml +++ b/.stainless/stainless.yml @@ -344,6 +344,8 @@ resources: email_otp_credential_verify_request_fields: '#/components/schemas/EmailOtpCredentialVerifyRequestFields' email_otp_credential_additional_challenge: '#/components/schemas/EmailOtpCredentialAdditionalChallenge' email_otp_credential_additional_challenge_fields: '#/components/schemas/EmailOtpCredentialAdditionalChallengeFields' + oauth_credential_create_request: '#/components/schemas/OauthCredentialCreateRequest' + oauth_credential_create_request_fields: '#/components/schemas/OauthCredentialCreateRequestFields' exchange_rates: methods: list: diff --git a/mintlify/openapi.yaml b/mintlify/openapi.yaml index f8489079..2a8dc65c 100644 --- a/mintlify/openapi.yaml +++ b/mintlify/openapi.yaml @@ -3745,7 +3745,7 @@ paths: **First credential on an internal account** - If the target internal account does not yet have any authentication credential registered, call this endpoint with the credential details. The response is `201` with the created `AuthMethod`. For `EMAIL_OTP` credentials, this call also triggers a one-time password email to the address on the customer record tied to the internal account; the credential must be activated via `POST /auth/credentials/{id}/verify` before it can sign requests. + If the target internal account does not yet have any authentication credential registered, call this endpoint with the credential details. The response is `201` with the created `AuthMethod`. For `EMAIL_OTP` credentials, this call also triggers a one-time password email to the address on the customer record tied to the internal account; the credential must be activated via `POST /auth/credentials/{id}/verify` before it can sign requests. For `OAUTH` credentials, the supplied `oidcToken` is validated inline against the issuer's `.well-known` OpenID configuration (the token's `iat` must be less than 60 seconds before the request); activation still happens via `POST /auth/credentials/{id}/verify`. **Adding an additional credential** @@ -3782,6 +3782,12 @@ paths: value: type: EMAIL_OTP accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + oauth: + summary: Register an OAuth credential + value: + type: OAUTH + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + oidcToken: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature responses: '201': description: Authentication credential created successfully @@ -13275,13 +13281,35 @@ components: allOf: - $ref: '#/components/schemas/AuthCredentialCreateRequest' - $ref: '#/components/schemas/EmailOtpCredentialCreateRequestFields' + OauthCredentialCreateRequestFields: + type: object + required: + - type + - oidcToken + properties: + type: + type: string + enum: + - OAUTH + description: Discriminator value identifying this as an OAuth credential. + oidcToken: + type: string + description: OIDC ID token issued by the identity provider (e.g. Google, Apple). Grid fetches the issuer's signing key from the `iss` claim's `.well-known` OpenID configuration and verifies the token signature. The token's `iat` claim must be less than 60 seconds before the request timestamp. + example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature + OauthCredentialCreateRequest: + title: OAuth Credential Create Request + allOf: + - $ref: '#/components/schemas/AuthCredentialCreateRequest' + - $ref: '#/components/schemas/OauthCredentialCreateRequestFields' AuthCredentialCreateRequestOneOf: oneOf: - $ref: '#/components/schemas/EmailOtpCredentialCreateRequest' + - $ref: '#/components/schemas/OauthCredentialCreateRequest' discriminator: propertyName: type mapping: EMAIL_OTP: '#/components/schemas/EmailOtpCredentialCreateRequest' + OAUTH: '#/components/schemas/OauthCredentialCreateRequest' AuthMethod: type: object required: diff --git a/openapi.yaml b/openapi.yaml index f8489079..2a8dc65c 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -3745,7 +3745,7 @@ paths: **First credential on an internal account** - If the target internal account does not yet have any authentication credential registered, call this endpoint with the credential details. The response is `201` with the created `AuthMethod`. For `EMAIL_OTP` credentials, this call also triggers a one-time password email to the address on the customer record tied to the internal account; the credential must be activated via `POST /auth/credentials/{id}/verify` before it can sign requests. + If the target internal account does not yet have any authentication credential registered, call this endpoint with the credential details. The response is `201` with the created `AuthMethod`. For `EMAIL_OTP` credentials, this call also triggers a one-time password email to the address on the customer record tied to the internal account; the credential must be activated via `POST /auth/credentials/{id}/verify` before it can sign requests. For `OAUTH` credentials, the supplied `oidcToken` is validated inline against the issuer's `.well-known` OpenID configuration (the token's `iat` must be less than 60 seconds before the request); activation still happens via `POST /auth/credentials/{id}/verify`. **Adding an additional credential** @@ -3782,6 +3782,12 @@ paths: value: type: EMAIL_OTP accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + oauth: + summary: Register an OAuth credential + value: + type: OAUTH + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + oidcToken: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature responses: '201': description: Authentication credential created successfully @@ -13275,13 +13281,35 @@ components: allOf: - $ref: '#/components/schemas/AuthCredentialCreateRequest' - $ref: '#/components/schemas/EmailOtpCredentialCreateRequestFields' + OauthCredentialCreateRequestFields: + type: object + required: + - type + - oidcToken + properties: + type: + type: string + enum: + - OAUTH + description: Discriminator value identifying this as an OAuth credential. + oidcToken: + type: string + description: OIDC ID token issued by the identity provider (e.g. Google, Apple). Grid fetches the issuer's signing key from the `iss` claim's `.well-known` OpenID configuration and verifies the token signature. The token's `iat` claim must be less than 60 seconds before the request timestamp. + example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature + OauthCredentialCreateRequest: + title: OAuth Credential Create Request + allOf: + - $ref: '#/components/schemas/AuthCredentialCreateRequest' + - $ref: '#/components/schemas/OauthCredentialCreateRequestFields' AuthCredentialCreateRequestOneOf: oneOf: - $ref: '#/components/schemas/EmailOtpCredentialCreateRequest' + - $ref: '#/components/schemas/OauthCredentialCreateRequest' discriminator: propertyName: type mapping: EMAIL_OTP: '#/components/schemas/EmailOtpCredentialCreateRequest' + OAUTH: '#/components/schemas/OauthCredentialCreateRequest' AuthMethod: type: object required: diff --git a/openapi/components/schemas/auth/AuthCredentialCreateRequestOneOf.yaml b/openapi/components/schemas/auth/AuthCredentialCreateRequestOneOf.yaml index b0d9bef1..cfa9ea30 100644 --- a/openapi/components/schemas/auth/AuthCredentialCreateRequestOneOf.yaml +++ b/openapi/components/schemas/auth/AuthCredentialCreateRequestOneOf.yaml @@ -1,6 +1,8 @@ oneOf: - $ref: ./EmailOtpCredentialCreateRequest.yaml + - $ref: ./OauthCredentialCreateRequest.yaml discriminator: propertyName: type mapping: EMAIL_OTP: ./EmailOtpCredentialCreateRequest.yaml + OAUTH: ./OauthCredentialCreateRequest.yaml diff --git a/openapi/components/schemas/auth/OauthCredentialCreateRequest.yaml b/openapi/components/schemas/auth/OauthCredentialCreateRequest.yaml new file mode 100644 index 00000000..34834d37 --- /dev/null +++ b/openapi/components/schemas/auth/OauthCredentialCreateRequest.yaml @@ -0,0 +1,4 @@ +title: OAuth Credential Create Request +allOf: + - $ref: ./AuthCredentialCreateRequest.yaml + - $ref: ./OauthCredentialCreateRequestFields.yaml diff --git a/openapi/components/schemas/auth/OauthCredentialCreateRequestFields.yaml b/openapi/components/schemas/auth/OauthCredentialCreateRequestFields.yaml new file mode 100644 index 00000000..3660fc22 --- /dev/null +++ b/openapi/components/schemas/auth/OauthCredentialCreateRequestFields.yaml @@ -0,0 +1,19 @@ +type: object +required: + - type + - oidcToken +properties: + type: + type: string + enum: + - OAUTH + description: Discriminator value identifying this as an OAuth credential. + oidcToken: + type: string + description: >- + OIDC ID token issued by the identity provider (e.g. Google, Apple). + Grid fetches the issuer's signing key from the `iss` claim's + `.well-known` OpenID configuration and verifies the token signature. + The token's `iat` claim must be less than 60 seconds before the + request timestamp. + example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature diff --git a/openapi/paths/auth/auth_credentials.yaml b/openapi/paths/auth/auth_credentials.yaml index 13ffb583..525ebea5 100644 --- a/openapi/paths/auth/auth_credentials.yaml +++ b/openapi/paths/auth/auth_credentials.yaml @@ -13,7 +13,11 @@ post: credentials, this call also triggers a one-time password email to the address on the customer record tied to the internal account; the credential must be activated via `POST /auth/credentials/{id}/verify` - before it can sign requests. + before it can sign requests. For `OAUTH` credentials, the supplied + `oidcToken` is validated inline against the issuer's `.well-known` + OpenID configuration (the token's `iat` must be less than 60 seconds + before the request); activation still happens via + `POST /auth/credentials/{id}/verify`. **Adding an additional credential** @@ -74,6 +78,12 @@ post: value: type: EMAIL_OTP accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + oauth: + summary: Register an OAuth credential + value: + type: OAUTH + accountId: InternalAccount:019542f5-b3e7-1d02-0000-000000000002 + oidcToken: eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMTIyMzM0NDU1IiwiYXVkIjoiMTIzNDU2Ny5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImlhdCI6MTc0NjczNjUwOSwiZXhwIjoxNzQ2NzQwMTA5fQ.signature responses: '201': description: Authentication credential created successfully