switching clears the token & responses
No token
7

Rendering the right sign-in form

Signing in

The mirror of scenario 1, for the other half of the front door: before showing any sign-in UI the client asks which LOGIN methods are live, so it offers a password form, a one-time-code form, or both. A different question from scenario 1 — that policy governs how an account may be CREATED, this one how an account that already EXISTS may authenticate.

  1. GET /v1/auth/login-policy → { allowedMethods } — render the matching form(s)
  2. Callable alongside GET /v1/auth/device (scenario 8) when the page also asks “do I recognise this browser?”
GET/v1/auth/login-policypublicGet active login policy

Read-only and lightly cached (max-age=30) — safe on page load. There is NO combinationMode: combining channels is a sign-up concept, and requiring two channels at every login is MFA, which is per-tenant (scenario 25) rather than part of this policy. allowedMethods is NEVER empty — the platform cannot be sealed shut — but it may exclude the method a given user depends on, so render defensively: an absent method answers 403 login_method_disabled. A method missing here closes EVERY door that uses it: dropping phone stops phone + password (scenario 10) AND the one-time-code login (scenario 14), including a code that was already sent. It does NOT gate sign-up (scenario 1), refresh (scenario 18), switch-tenant (scenario 20), or the MFA second factor — a policy change never signs a live session out and never strips an enrolled factor. The write path lives on the operator profile (scenario 40).

ℹ︎Field guide — what each value means & where it comes fromno inputs

No request body, path params, or query params — and no bearer token: this is the unauthenticated read the sign-in screen makes before rendering. Returns the active LOGIN policy. This is NOT GET /v1/auth/registration-policy under another name: that one answers “what may a new account be created with?”, this one “what may an account that already exists sign in with?”. A sign-in screen reading the registration policy is answering the wrong question and will offer a method login refuses, or hide one it accepts.

GET https://api.kerja.team/v1/auth/login-policy
↩︎Response guide — what comes back & what each value means2 fields

200 OK with the { data } envelope and Cache-Control: public, max-age=30 — lightly cacheable, safe to call on page load, and safe to call alongside GET /v1/auth/device. No secrets and no user data: this is the same fact the endpoint already publishes to anyone who asks, which is why the 403 login_method_disabled it predicts can be raised before a credential is even examined without leaking anything.

dataobjectalways
The active global LoginPolicy. Note what is NOT here: there is no combinationMode. Combining channels is a sign-up concept, and requiring two channels at every login is MFA, which is per-tenant (isMfaRequired) rather than part of this policy.
data.allowedMethods[]arrayalways
Non-empty subset of ["email","phone"] — render only these forms. Defaults to BOTH when the policy row is missing, deliberately more permissive than the registration policy's fallback: a missing row here would otherwise lock out every account whose only credential is the excluded channel.
  • emailOffer the email + password form (scenario 9).
  • phoneOffer the phone forms — phone + password (scenario 10) and the one-time-code login (scenario 14). Both close together when this is absent, including codes already sent.