Skip to main content
PeridotID authentication is cookie-based. After a successful Google sign-in the API sets two HttpOnly cookies on your domain.

The two cookies

Both are HttpOnly; SameSite=Lax. In production the Secure flag is also set, so they are only sent over HTTPS.

Sign-in flow

  1. Call POST /v1/auth/login (or use the SDK) to get the Google authorization URL.
  2. Navigate the browser to that URL. The user consents on Google.
  3. Google redirects to GET /v1/auth/google/callback?code=.... The API exchanges the code and resolves the credential.
  4. Returning credential → session cookies are issued and the browser lands on CLIENT_SUCCESS_URL with a logged-in session. New credential → the PID picker (see below).
On a first-time sign-in the API mints a single-use claim ticket (pid_claim cookie, 10 min) and redirects to CLIENT_SUCCESS_URL?claim=1. The claim screen shows who you’re continuing as — pick a handle, POST /v1/auth/claim, and the identity (<handle>@pid), profile, and credential are created in one transaction. Nothing exists until you claim: abandoning restarts cleanly on the next login, and an expired ticket answers 410. On every later sign-in the same Google credential is found and you land back on the same identity — the PID never changes, is never reused, and cannot be reassigned, no matter which providers you link or unlink later. Choose the handle carefully: it is permanent.
Note: GET /v1/auth/google and /v1/auth/google/callback are browser-navigation only. They respond with 302 redirects that a fetch()/Swagger “Send” call cannot follow (the cross-origin redirect to Google is CORS-blocked, showing Failed to fetch). Always open the login URL in a browser tab — never call these two endpoints from JavaScript.

Login credentials

Every way to log in — Google, email OTP, passkeys (Apple coming soon) — is a credential. Credentials belong to an identity:
  • The pair (provider, providerUserId) is unique — it is the source of truth.
  • A non-null email is unique across accounts: one email = one PID. If a new login presents an email already associated with another account, it is rejected with 409 (Email sudah terhubung dengan akun lain) — accounts are never merged, moved, or minted twice (see Errors).
  • The identity (PID) is the only source of truth. Credentials are just the keys that prove ownership of a PID.
List and unlink credentials via the SDK:
An identity must always keep at least one credential — unlinking the last one returns 400. This prevents a user from locking themselves out. Unlinking a credential never touches the identity or its wallet (if any) — the wallet belongs to the PID, not to a provider.

Email OTP login

POST /v1/auth/email/start sends a 6-digit code to the address (Brevo sender, 10-minute TTL, 60-second resend cooldown, 5 requests/hour per address), then POST /v1/auth/email/verify consumes it (single-use, locked after 5 wrong attempts). Returning addresses land straight on a session; new addresses get a claim ticket and continue through the same PID picker as Google. The code itself is never stored — only its SHA-256 hash — and both endpoints answer generically so they never reveal whether an address has an account.

Refreshing tokens

The access token expires after 15 minutes. Instead of asking the user to log in again, call POST /v1/auth/refresh. It:
  1. Verifies the current pid_refresh cookie.
  2. Revokes it (rotation) and issues a new access + refresh pair as cookies.
Because each refresh token can be used exactly once, a stolen token that is replayed after rotation is rejected.
If this returns 401, the refresh token is invalid, expired, or already rotated — the user must sign in again.

Logging out

POST /v1/auth/logout revokes the current refresh token and clears both cookies. It is idempotent — safe to call when already logged out.

Security notes

  • Access tokens are short-lived; the real secret is the rotating refresh token.
  • The refresh cookie’s Path=/v1/auth means it is only sent to auth endpoints, reducing the window for cross-route exfiltration.
  • Auth endpoints are rate limited (see Errors).