Sign in / sign up with a Sign in with Apple credential
const url = 'https://api.tovarifinancial.com/auth/apple';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"identityToken":"example","authorizationCode":"example","nonce":"example","fullName":{"givenName":"example","familyName":"example"}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.tovarifinancial.com/auth/apple \ --header 'Content-Type: application/json' \ --data '{ "identityToken": "example", "authorizationCode": "example", "nonce": "example", "fullName": { "givenName": "example", "familyName": "example" } }'The Apple bridge, the counterpart of POST /auth/google (KAN-3109 documents it; the handler is src/handlers/auth/apple-auth.ts). Identity resolves by the Apple sub first and the verified email second, because Apple sends the email on the FIRST authorization only. When neither resolves and the email is a Hide-My-Email private-relay address, NOTHING is provisioned: the 200 carries { confirmationRequired, confirmationToken, expiresIn } instead (KAN-2823), and only a second POST carrying { confirmationToken } — that key alone, within expiresIn seconds — creates the account (201). A non-relay address with no match provisions immediately (201). X-Idempotency-Key is accepted and logged but NOT persisted, exactly as on POST /auth/google: the new-user path mints a fresh org id per request, so a stored replay could never be found again; duplicate deliveries converge structurally instead (one Apple binding per sub, one Cognito user per address) and the loser answers 409. Also reachable, and deliberately not published as a response code because this operation sits outside the tenant gate: 403 SIGNUP_RESTRICTED when a deployment restricts sign-ups to an allowlist and this NEW account is not on it — an existing account signs in regardless. The 503 is SERVICE_UNAVAILABLE for an Apple, Cognito or key-service outage and MISCONFIGURED when a deployment value the request needs is missing — a code no retry can clear until the deployment is fixed.
Request Bodyrequired
Section titled “Request Bodyrequired”Identity variant — an Apple credential from the native or web Sign in with Apple ceremony.
object
The Sign in with Apple identity token (JWT).
The single-use Sign in with Apple authorization code.
The raw nonce the client supplied to the Apple ceremony. Optional; when the token carries a nonce claim it must correspond to this value.
Apple sends the name on the FIRST authorization only. Untrusted: used solely to name a brand-new account, never to change an existing one.
object
Confirm variant (KAN-2823) — the user chose to create a new account for a Hide-My-Email address. This key and NO other.
object
The confirmationToken a previous POST /auth/apple returned on its confirmation-required 200.
Examplegenerated
{ "identityToken": "example", "authorizationCode": "example", "nonce": "example", "fullName": { "givenName": "example", "familyName": "example" }}Responses
Section titled “Responses”Existing user authenticated (tokens issued), OR a second factor is required (challenge returned, no tokens), OR a Hide-My-Email sign-up needs the user to confirm (confirmation token returned, nothing written)
object
Existing user authenticated — tokens issued.
object
Access/ID token lifetime in seconds.
object
object
TRUE when the outstanding policies are a first acceptance rather than an update.
object
Derived from users.email_verified, the sole source of truth for the gate.
object
Seconds the resend button stays disabled after an interactive send.
The session user display-language state (KAN-3773).
object
The per-environment Spanish rollout flag. False ⇒ effectiveLocale is always ‘en’.
The explicit choice; null = use device language.
This request’s X-Client-Locale, else the last-seen device language, else null.
The language to display: ‘en’ when the flag is off, else preference ?? detected ?? ‘en’.
Always false on a 200 — a brand-new account answers 201.
TRUE when this request replaced the account password to complete the Apple link.
A second factor is required. email is server-resolved and carried because POST /auth/mfa needs it and an Apple client cannot know it (omitted after the first authorization, hidden behind Hide My Email). A challenge implies an EXISTING user, so there is no isNewUser.
object
object
The Cognito challenge name, e.g. SOFTWARE_TOKEN_MFA.
KAN-2823. A verified Hide-My-Email address matched no account, so the user is ASKED rather than given a second household: nothing has been written and no Cognito user exists. Either confirm with a second POST carrying only { confirmationToken }, or sign in with email and password instead.
object
Opaque, signed and single-use. Send it back ALONE as { confirmationToken } to create the account; never store it.
Seconds the confirmation token stays valid (APPLE_CONFIRMATION_TTL_SECONDS in src/handlers/auth/apple-signup-confirmation.ts).
Example
{ "data": { "verification": { "status": "verified" }, "language": { "preference": "en", "detected": "en", "effectiveLocale": "en" } }}New account created from the Apple credential (or from a confirmed Hide-My-Email sign-up); tokens issued
object
object
Access/ID token lifetime in seconds.
object
object
TRUE when the outstanding policies are a first acceptance rather than an update.
object
Derived from users.email_verified, the sole source of truth for the gate.
object
Seconds the resend button stays disabled after an interactive send.
The session user display-language state (KAN-3773).
object
The per-environment Spanish rollout flag. False ⇒ effectiveLocale is always ‘en’.
The explicit choice; null = use device language.
This request’s X-Client-Locale, else the last-seen device language, else null.
The language to display: ‘en’ when the flag is off, else preference ?? detected ?? ‘en’.
Always true on a 201.
Example
{ "data": { "verification": { "status": "verified" }, "language": { "preference": "en", "detected": "en", "effectiveLocale": "en" } }}VALIDATION_ERROR — no body, a body that is not a JSON object, a missing identityToken or authorizationCode (named in fieldErrors), a confirm body that is not exactly one non-empty string confirmationToken, or an Apple email that is not a valid address. A Cognito rejection of the new account’s creation parameters also answers 400 with its own code.
object
Machine-readable error code (AUTH_ERROR_CODES).
Optional field-level validation errors, keyed by field name.
object
Example
{ "errorCode": "INVALID_EMAIL_FORMAT"}AUTHENTICATION_FAILED — the Apple credential (or confirmation token) was rejected, carries no usable identity, or resolves to no active account; one fixed message whichever check failed. APPLE_CONFIRMATION_EXPIRED — a validly signed confirmation token that has expired, or whose sign-up already completed or now matches an existing account; one fixed message for all three, and the client starts again from email and password. An identity-provider refusal while creating a new account can also surface on this status with that provider’s mapped code.
object
Machine-readable error code (AUTH_ERROR_CODES).
Optional field-level validation errors, keyed by field name.
object
Example
{ "errorCode": "INVALID_EMAIL_FORMAT"}Conflict — errorCode is one of: IDENTITY_CONFLICT (the verified email belongs to an account already bound to a different Apple ID); USER_ALREADY_EXISTS (an account exists for this email that this sign-in cannot create, link or heal, including a concurrent sign-up of the same Apple ID that won the race); UNCLAIMED_INVITE_ACCOUNT (the email resolves to an account created by accepting a household invite that has never verified its email — a fixed-message refusal, carrying no details, that writes nothing and tells the user to sign in with the password set when the invitation was accepted and verify their email first).
object
Machine-readable error code (AUTH_ERROR_CODES).
Optional field-level validation errors, keyed by field name.
object
Example
{ "errorCode": "INVALID_EMAIL_FORMAT"}TOO_MANY_REQUESTS — the identity provider is shedding load while creating a new account. Retry after a short back-off.
object
Machine-readable error code (AUTH_ERROR_CODES).
Optional field-level validation errors, keyed by field name.
object
Example
{ "errorCode": "INVALID_EMAIL_FORMAT"}Internal error (no internal detail leaked)
object
Machine-readable error code (AUTH_ERROR_CODES).
Optional field-level validation errors, keyed by field name.
object
Example
{ "errorCode": "INVALID_EMAIL_FORMAT"}Service temporarily unavailable — errorCode is SERVICE_UNAVAILABLE. A TRANSIENT, RETRYABLE condition rather than a defect in the request: a query that exceeded its time budget, a lost or refused database connection, a saturated connection pool, or an outage at an upstream provider the request depends on (the authentication provider, or the bank data provider on a bank-connection operation). The identical request may succeed on retry — back off briefly and, on a write, resend the same X-Idempotency-Key where the operation accepts one.
object
Machine-readable error code (AUTH_ERROR_CODES).
Optional field-level validation errors, keyed by field name.
object
Example
{ "errorCode": "INVALID_EMAIL_FORMAT"}