Authentication methods
Zynth Auth (Perihelium's authentication layer) supports several ways for a user to prove who they are. They all end the same way — a short-lived access token plus a rotating refresh token (see Tokens & sessions) — so your application integrates once and the user picks whichever method suits them.
At a glance
| Method | What the user does | Best for | Extra factor? |
|---|---|---|---|
| Password | Email + password within an organization | The universal default | Optional TOTP (below) |
| Multi-factor (TOTP) | Password, then a 6-digit code | Stronger workforce security | Yes — the second factor |
| Passkeys (WebAuthn) | Fingerprint / Face ID / security key | Phishing-resistant, password-free | Built in (biometric + device) |
| Social login | "Continue with Google / GitHub" | Frictionless consumer sign-up | Delegated to the provider |
| Magic link | Click a one-time link emailed to them | Passwordless, low-friction | No (mailbox possession) |
| Enterprise SSO (OIDC / SAML) | Sign in through your own IdP (Okta, Entra ID, …) | Workforce / enterprise tenants | Delegated to the IdP (+ your Zynth MFA if enrolled) |
All methods are fail-closed and enumeration-safe: a failure never reveals whether an email, organization, or account exists, and errors are generic by design.
These describe signing in. Confirming a sensitive action is a stricter bar. TOTP and passkeys do double duty: they are also what lets a user re-prove themselves for step-up and payment confirmation. Magic links and federated sign-in deliberately do not count toward that — so a user who can sign in is not necessarily a user who can approve a payment.
Two populations. Workforce users (your staff/admins) and customer/CIAM users behave differently — see Tenants & users. Social login and magic links are especially aimed at the customer population; passwords, MFA, and passkeys serve both.
Password
The baseline. Users sign up (creating an organization and becoming its owner) or log in to an existing organization with email + password + the organization slug. Sign-up proves the mailbox first: the request creates nothing and answers the same for every address, and the emailed one-time link is where the password is chosen.
- Passwords are hashed with Argon2id and must meet a NIST-aligned policy (length + common-password denylist).
- Login is generic on failure (
401 Invalid credentials) — it never says whether the email, password, or organization was wrong. - API:
POST /auth/signup·POST /auth/signup/complete·POST /auth/login.
Multi-factor authentication (TOTP)
When a user enables MFA, password login becomes two steps: after the password, the
response is a short-lived MFA challenge (mfa_required: true) instead of a session, and
the user submits a 6-digit code from their authenticator app (or a one-time recovery code)
to finish.
- Enrolment is verify-before-activate (they must enter a valid code once to turn it on) and yields eight single-use recovery codes.
- The challenge token is single-use with a small attempt budget — a captured challenge can't be replayed.
- Each code is accepted once. A 6-digit code is valid for its 30-second step (plus a step of clock tolerance); once it has been accepted it is spent, so a code read over a shoulder or captured by a proxy cannot be used again inside its own window. A replayed code is refused exactly like a wrong one — the answer never confirms it was right.
- The account carries a failure budget that spans challenges. Ten wrong codes in 15
minutes (the default) and further attempts answer
429with aRetry-After, even across fresh sign-ins; a correct code clears the budget, so an ordinary mistype costs nothing. A separate per-network floor bounds anyone spraying codes at accounts they cannot name. - Every other sign-in method that lands on an MFA-enrolled account still asks for the second factor — social login and magic links do not bypass it. (A user-verified passkey is the one exception: it already carries two factors in one ceremony.)
- Managed by the user under Account security; API:
/mfa/*.
Passkeys (WebAuthn)
Phishing-resistant, password-free sign-in backed by the user's device (Touch ID, Windows Hello, a security key, or a synced platform passkey).
- A user-verified passkey is multi-factor by itself (possession of the device + biometric/PIN), so passkey logins skip the extra TOTP step.
- Sign-in is email-less — the passkey identifies the account; the user only supplies their organization slug.
- Users can register several passkeys (phone, laptop, security key); removing one re-proves the account password when one exists. Passkey-only accounts remove passkeys without a password, but the last remaining sign-in method can never be removed.
- API: Passkeys. Integrate the browser ceremony
with the standard
navigator.credentialsAPI.
Social login (Google · GitHub)
"Continue with Google" / "Continue with GitHub" — an authorization-code flow with PKCE, orchestrated server-side.
- New users are just-in-time provisioned as customers into an organization that has customer self-sign-up enabled; an existing account with a provider-verified matching email is linked automatically (an unverified provider email is refused — no account takeover).
- Does not bypass an enrolled TOTP factor.
- Setup (registering the OAuth apps) is an operator task — see Social login setup. API: Social login.
Magic links
Passwordless email sign-in: the user requests a link, clicks it, and they're in.
- Links are single-use, expire in ~15 minutes, and requesting a new link cancels the previous one (only the newest works).
- The request endpoint always answers
202regardless of whether the address exists (no enumeration), and issuance is silently rate-capped so a mailbox can't be flooded. - Clicking the link opens a page that completes sign-in on a deliberate click, so email scanners that pre-fetch links can't consume them.
- Does not bypass an enrolled TOTP factor. API: Magic-link sign-in.
Turning a method off for your organization
A tenant admin (tenant:manage) can close individual sign-in doors under Settings →
Organization, without waiting on the platform operator:
| Setting | Effect when off |
|---|---|
auth.magic_link_enabled | No new sign-in links are issued — and links already in inboxes stop redeeming. Turning it back on does not revive them. |
auth.social_login_enabled | "Continue with Google / GitHub" is refused for this organization. Existing linked identities remain, and become usable again if you re-enable. |
Both default to on (matching previously shipped behaviour), and both are recorded in the setting's change history — who turned it off, and when. This is a narrowing choice about your own members' doors, so it needs no operator involvement; see Organization settings for the API.
Distinct from requiring SSO (below), which refuses password, magic-link, passkey and social in one policy while keeping an owner break-glass path. Use the toggles to retire one method; use SSO enforcement to mandate the IdP.
How long people stay signed in
Zynth Auth ends every sign-in after a maximum age and, where the platform sets one, after a
period with no activity. A tenant admin (tenant:manage) can choose shorter limits for their
own organization under Settings → Organization:
| Setting | Meaning |
|---|---|
auth.session_absolute_max_age_hours | The longest a sign-in lasts, in hours, before the person must sign in again. 0 uses the platform's limit. |
auth.session_idle_timeout_minutes | Sign people out after this many minutes without activity. 0 uses the platform's limit. Values under 15 behave as 15, the time between a signed-in app's refreshes. |
You can only shorten: the smaller of your limit and the platform's always applies, so a value larger than the platform's has no effect. A change applies to people who are already signed in, on their next request: anyone past the new limit is asked to sign in again (until then such a sign-in can still appear in the person's list of sessions). Every change is recorded in the setting's history (who, what, when), and an expiry says in the event whether the platform's limit or yours ended the sign-in.
Enterprise SSO (OIDC & SAML federation)
For organizations that run their own identity provider, a tenant can federate sign-in so its members authenticate at their IdP (Okta, Microsoft Entra ID, Keycloak, ADFS, Ping, …) and land in Zynth Auth with a normal session. Both protocols are supported and configured per-tenant in Settings → Organization → Enterprise SSO:
- OpenID Connect — authorization-code + PKCE; discovery, JWKS rotation, claim→role mapping. See Enterprise SSO (OIDC).
- SAML 2.0 — SP-initiated (and opt-in IdP-initiated) with signed (and optionally encrypted) assertions, IdP-metadata import, and single logout. See Enterprise SSO (SAML).
Both do just-in-time provisioning (create a member on first sign-in, mapped to a role), and neither bypasses an enrolled Zynth MFA factor. Deprovisioning through the IdP — via SCIM (Directory provisioning) or SAML/OIDC logout — ends the user's sessions immediately across every device.
Requiring SSO. A tenant admin can turn on SSO required (the Enforcement tab): members can then sign in only through the IdP — password, magic-link, passkey, and social sign-in are refused for them. Organization owners keep an audited break-glass sign-in, so a broken IdP can never lock the organization out.
Availability. Enterprise federation is a deployment-level capability your operator enables; if the Enterprise SSO settings report it isn't enabled, ask them to turn it on.
Adding a credential needs a recent sign-in
Adding a way into an account — a passkey, an authenticator app, a linked Google or GitHub
account — requires the user to have signed in within the last five minutes (the default;
your operator can change it). Outside that window the API answers 403 with a body your
client can branch on:
{ "detail": { "code": "recent_auth_required", "reauth_path": "/api/v1/auth/login" } }
Send the user back through sign-in and retry the enrolment. Two details worth knowing:
- Refreshing a token does not renew recency. The window is measured from the moment the session actually authenticated, so rotating a refresh token never manufactures freshness.
- API keys and agent tokens can never satisfy it. A credential is something a human adds to their own account; a machine principal has no authentication instant to measure.
Removing a credential is unaffected — it already re-proves the account password. The asymmetry is deliberate: an attacker holding a stolen session adds persistence, they do not remove it. Every newly added credential also sends the account holder an email, so an enrolment inside the window is still visible to the person who owns the account.
When a member loses their second factor
An administrator with Manage members can reset another member's MFA:
POST /api/v1/members/{user_id}/mfa/reset. It clears the enrolled authenticator, signs
that member out everywhere, and emails them. The member then signs in with their password
and enrols again. It is refused with a 409 for a person who also belongs to another
organization: their authenticator and sessions are theirs, not one organization's, so they
disable it themselves from Account security with an authenticator code or a recovery
code, or sign in once with a recovery code.
The same administrator can remove a member's passkeys when a device is lost:
POST /api/v1/members/{user_id}/passkeys/reset. It removes all of the member's passkeys,
signs them out everywhere, emails them, and answers with how many it removed
({"passkeys_removed": 2}); they sign in with their password and add a passkey again. It is
refused with a 409 for a member who has no password (removing every passkey would leave
them unable to sign in), for a person who also belongs to another organization where they can still sign in (their
passkeys serve that sign-in too; they remove their own; a suspended seat or a deleted organization does not count), and for a member who has no passkeys. It is audited as
member.passkeys_reset, and the burst detection counts it with the MFA reset.
The reset is deliberately narrow, because it removes the strongest control on an account:
- it is audited (
member.mfa_reset,member.passkeys_reset) and repeated use raises a detection; - it cannot be performed by an AI agent or an API key, whatever permissions they hold;
- an administrator cannot reset their own factor through it (use Account security, which asks for a code, or for your password when removing a passkey), and an owner's factor can only be reset by another owner.
Hardening the tokens you get back
Independent of how a user signed in, you can harden the resulting tokens:
- Sender-constrained tokens (DPoP, RFC 9449) — bind tokens to a client-held key so a stolen token is useless without the key. Recommended for API/SDK and machine clients. See Sender-constrained tokens (DPoP).
- Sign out everywhere — a user (or your support team) can instantly revoke every session
on every device via Account security → Sign out of all devices
(
POST /auth/sign-out-all).
Choosing methods
- Consumer / CIAM apps: offer social login and magic links for frictionless onboarding; add passkeys as the phishing-resistant upgrade. ⚠️ If you take payments, enrol passkeys or TOTP too. Social login and magic links are fine for signing in, but neither can satisfy a payment confirmation on its own — a user with only those cannot complete strong customer authentication.
- Workforce / internal apps: password + MFA, or passkeys; enable DPoP on service-to-service and SDK clients.
- Enterprise tenants: federate with enterprise SSO (OIDC or SAML) and, if the tenant requires it, turn on SSO required so the IdP is the only door (owners keep break-glass). Automate joiner/mover/leaver with SCIM.
- You don't have to choose exclusively — enable several and let users pick. Every method returns the same token pair, so the rest of your integration is identical.