Skip to content

Auth

Every path below is scoped to one auth collection: /collections/{idOrName}/<action>. Superuser login is the same auth-with-password endpoint with idOrName=_superusers — there is no separate admin-login path. See Concepts → Authentication for the token model these endpoints share.

Method & path Auth Summary
POST .../auth-with-password none Log in with identity + password
POST .../auth-refresh bearer Refresh a session token
GET .../auth-methods none Which auth methods are enabled
POST .../request-verification none Email a verification link
POST .../confirm-verification none Consume it
POST .../request-password-reset none Email a reset link
POST .../confirm-password-reset none Consume it, set a new password
POST .../request-email-change bearer Email a confirmation to a pending new address
POST .../confirm-email-change none Consume it (acting record comes from the token)
POST .../request-otp none Email a one-time login code
POST .../auth-with-otp none Complete OTP login
POST .../request-magic-link none Email a single-use sign-in link
POST .../auth-with-magic-link none Complete magic-link login
POST .../impersonate/{id} bearer (superuser) Mint a non-refreshable token for another record
POST .../auth-signout bearer Revoke the session backing the current token
GET .../sessions bearer List the caller’s own sessions (or another record’s, as superuser)
DELETE .../sessions/{id} bearer Revoke one session by id
POST .../sessions/revoke-others bearer Revoke every session but the current one
POST .../sessions/revoke-all bearer Revoke every session, including the current one
POST .../stop-impersonating cookie Restore the impersonator’s session (cookie mode only)
POST .../ban/{id} bearer (superuser) Ban a record, revoking its sessions immediately
DELETE .../ban/{id} bearer (superuser) Lift a ban
GET .../oauth2/{provider}/start none Begin the server-driven OAuth2 redirect flow
GET .../oauth2/{provider}/callback none, GET/POST Complete it, landing the session in a cookie
POST .../totp/setup bearer Start (or restart) TOTP setup, returns the otpauth:// URI + secret
POST .../totp/confirm bearer Confirm setup, returns 10 backup codes shown once
POST .../totp/disable bearer Disable TOTP with a code or the account password
POST .../totp/backup-codes/regenerate bearer Invalidate existing backup codes, issue 10 new ones
POST .../auth-with-totp none Complete a pending MFA challenge with a TOTP/backup code
GET .../records/{id}/external-auths bearer (owner/superuser) List a record’s linked OAuth2 providers
DELETE .../records/{id}/external-auths/{provider} bearer (owner/superuser) Unlink one
{ "identity": "alice@example.com", "password": "hunter2", "identityField": "email", "mfaId": "..." }

identity is matched against passwordAuth.identityFields (email by default); identityField pins the lookup to one field instead of trying each in order. mfaId completes a pending MFA challenge from a previous call. Success: 200 { "token", "record" }. A pending MFA challenge is 401 { "mfaId": "..." } — with no status/message/data envelope, distinct from every other error shape on this page.

request-verification, request-password-reset, request-otp, and request-magic-link all answer 204 (or, for OTP, 200 { "otpId" }; for magic-link, 200 {}) regardless of whether the address exists — this is deliberate account-enumeration resistance, not a bug to work around. See Email flows, OTP, and Magic link.

Requires authOptions.magicLink.enabled (default false) — disabled answers 403. POST .../request-magic-link body: { "email": "...", "redirectUrl": "..." } (redirectUrl optional, honoured only when same-origin as settings.meta.appURL). POST .../auth-with-magic-link body: { "token": "...", "mfaId": "..." } → the normal 200 { "token", "record" }, or 400 for a missing, reused, or expired token. See Magic link and Email → Recipes for the full flow.

{ "token": "...", "password": "current-password" }

Note this one takes the record’s current password as a second factor, not the new email — the acting record comes entirely from the token’s claims, not from an Authorization header, which is why this endpoint is unauthenticated at the transport level while still being scoped to one specific record.

Superuser-only. Body: { "duration": 3600 } (optional; defaults to the collection’s own authToken.duration). Returns a normal { "token", "record" } pair, but the token is flagged non-refreshable — calling auth-refresh on it returns the same token unchanged rather than failing. See Impersonation.

Bearer-authenticated. Revokes the _sessions row backing the current token and, in cookie mode, clears the session cookie. Returns 204 regardless of prior state.

{ "items": [{ "id": "...", "kind": "password", "fingerprint": "...", "ip": "...", "userAgent": "...", "current": true, "created": "...", "lastSeenAt": "...", "expiresAt": "..." }] }

GET .../sessions lists the caller’s own sessions; pass ?recordId= to look at another record’s sessions, which requires superuser. kind is one of password, otp, oauth2, impersonation, or refresh. Requires SESSION_TRACKING=true (the default) — with it off, this always returns an empty list and revocation is a no-op.

DELETE .../sessions/{id} revokes one session by id: 204 whether or not the id existed, and requires either being the session’s own owner or a superuser (403 otherwise). POST .../sessions/revoke-others revokes every session but the current one and returns 200 { "revoked": <count> }. POST .../sessions/revoke-all revokes every session, including the current one — 204, clearing the session cookie in cookie mode.

Cookie mode only. Reads the cb_session_prev cookie stashed by impersonate/{id}, re-verifies it still signs correctly, and restores it as the active session cookie, clearing cb_session_prev. Returns 200 { "record" }. 400 if there’s no impersonation session to stop (missing, unverifiable, or already-consumed cookie). Bearer-mode impersonation restoration has no server-side counterpart — the client just discards the impersonated token and keeps using the original.

Superuser-only. POST .../ban/{id} body: { "reason": "...", "expiresAt": "..." } (both optional). Upserts a _bans row, rotates the target’s signing key, and revokes every one of its live sessions — a ban locks out live sessions immediately, not just future logins. Returns 200 { "recordRef", "reason", "expiresAt" }. DELETE .../ban/{id} removes the ban, if any, and returns 204. A banned record’s next auth-with-password/auth-with-otp/auth-with-oauth2/auth-refresh call fails with 403 "This account is banned.".

oauth2/{provider}/start / oauth2/{provider}/callback

Section titled “oauth2/{provider}/start / oauth2/{provider}/callback”

Cookie mode only — the server drives the whole provider round trip. GET .../oauth2/{provider}/start?redirect=&createData= requires redirect to be same-origin with settings.meta.appURL or an exact member of CORS_ALLOW_ORIGINS (a "*" entry never qualifies); an untrusted redirect answers 400 JSON — the one failure this flow answers directly instead of by redirecting, since redirecting to an untrusted target is exactly the open redirect this guards against. Every later failure instead redirects to the now-trusted redirect with ?cb_error=<code>: cookie_sessions_disabled, app_url_not_configured, oauth2_disabled, or provider_not_enabled.

GET .../oauth2/{provider}/callback is the provider’s own redirect target — never called directly — and on success redirects to redirect with the session already set in an httpOnly cookie. A pending MFA challenge redirects with ?cb_mfa=<mfaId> instead of setting the cookie. The exact URL to register with the provider is {appURL}/api/collections/{collection}/oauth2/{provider}/callback. The callback also accepts POST (Apple’s response_mode=form_post sends code/state/user as form fields instead of query params — see Sign in with Apple); every other provider only ever uses GET.

totp/setup / totp/confirm / totp/disable / totp/backup-codes/regenerate

Section titled “totp/setup / totp/confirm / totp/disable / totp/backup-codes/regenerate”

Bearer-authenticated, act on the caller’s own record — there’s no {id} in any of these paths. POST .../totp/setup (no body) → 200 { "secret", "uri" }; calling it again before confirming replaces the pending secret, and calling it once already confirmed answers 400. POST .../totp/confirm body { "code": "123456" } → 200 { "backupCodes": [...10 strings] }, shown once and never retrievable again. POST .../totp/disable body { "code": "..." } or { "password": "..." } → 204. POST .../totp/backup-codes/regenerate (no body) → 200 { "backupCodes": [...10 new strings] }, invalidating every previous one. See TOTP 2FA.

{ "mfaId": "...", "code": "123456" }

Unauthenticated at the transport level — the pending _mfas session mfaId names already identifies the record. code is either a live TOTP code or one of the record’s backup codes (consumed on use, and never accepted twice). Success: the normal 200 { "token", "record" }; a wrong/reused code is 400.

[{ "id": "...", "created": "...", "updated": "...", "recordId": "...", "collectionId": "...", "provider": "google", "providerId": "..." }]

GET lists every provider linked to id — a bare array, not the usual { "items": [...] } list envelope. Both this and DELETE .../external-auths/{provider} require being id’s own owner or a superuser. The DELETE additionally refuses (400) when unlinking that provider would leave the record with no password, no other linked provider, and both OTP and magic-link login disabled — see Linked accounts.