Rendering the right sign-in form
Signing inThe 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.
- GET /v1/auth/login-policy → { allowedMethods } — render the matching form(s)
- Callable alongside GET /v1/auth/device (scenario 8) when the page also asks “do I recognise this browser?”
/v1/auth/login-policypublicGet active login policyRead-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-policyResponse 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.
dataobjectalwaysdata.allowedMethods[]arrayalwaysemail— Offer the email + password form (scenario 9).phone— Offer 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.