You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The admin login page always renders passkey as the primary method, configured auth providers under "OR CONTINUE WITH", and a magic-link fallback. A site that standardizes on one identity provider — one SSO button and nothing else — can't express that: LoginPage.tsx hardcodes the layout, and the public /_emdash/api/auth/mode endpoint returns authMode plus the provider list with no per-method visibility.
The only exclusive mode today is the transparent external-auth descriptor (the Cloudflare Access style), which replaces the login page entirely. That's the wrong tool when you still want EmDash's own login page, session handling, and user provisioning — just with a single method on it.
Concretely: we run a production EmDash site (blog.hello.coop) where every admin signs in with an OIDC provider via authProviders. Passkey and magic-link on the login screen are dead UI at best; at worst they're a side door that bypasses the provider's account lifecycle (we also drive account state through the provider — suspend/delete should mean the user can't fall back to a magic link).
Proposal
emdash({authProviders: [myProvider({})],loginMethods: {passkey: false,magicLink: false},// default: both true});
auth/mode.ts / /_emdash/api/auth/mode return the enabled methods alongside providers, so the login page (and anything else) can render from data instead of assumptions.
LoginPage.tsx: when passkey is disabled and exactly one provider is configured, promote that provider's button to the primary position; drop the divider and the magic-link row. (Strings go through Lingui as usual.)
Enforcement, not just cosmetics: the passkey and magic-link API routes return 404/403 when disabled. Hiding a button while leaving the route open isn't a policy.
Lockout valve:?method=passkey on the login URL as a documented break-glass override — with providers-only login, a provider outage or a misconfigured client would otherwise lock every admin out. (Open to a different shape here: an env var, or honoring the flag only for existing passkey-enrolled users.)
Defaults unchanged: with no loginMethods configured, the page and routes behave exactly as today.
Why in core rather than a fork or CSS
The layout and the route gating both live in core (packages/admin/src/components/LoginPage.tsx, packages/core/src/auth/mode.ts, the passkey/magic-link routes). A provider package can add a login method but can't subtract the built-ins, and hiding them with CSS leaves the routes live. This is config for a policy decision that only the site owner can make — same altitude as authProviders itself.
Status
Design only at this point — we're running the provider setup above in production and will prototype loginMethods in our patched checkout next. Happy to build the PR (implementation + tests + docs + changeset) if this direction works for you, and to adjust naming (loginMethods vs login: { methods }), the single-provider promotion heuristic, and the break-glass mechanism to taste.
Related: this is the same production use case behind #2836 (pluggable token verifiers) — filed separately since the two are independent.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
The problem
The admin login page always renders passkey as the primary method, configured auth providers under "OR CONTINUE WITH", and a magic-link fallback. A site that standardizes on one identity provider — one SSO button and nothing else — can't express that:
LoginPage.tsxhardcodes the layout, and the public/_emdash/api/auth/modeendpoint returnsauthModeplus the provider list with no per-method visibility.The only exclusive mode today is the transparent external-auth descriptor (the Cloudflare Access style), which replaces the login page entirely. That's the wrong tool when you still want EmDash's own login page, session handling, and user provisioning — just with a single method on it.
Concretely: we run a production EmDash site (blog.hello.coop) where every admin signs in with an OIDC provider via
authProviders. Passkey and magic-link on the login screen are dead UI at best; at worst they're a side door that bypasses the provider's account lifecycle (we also drive account state through the provider — suspend/delete should mean the user can't fall back to a magic link).Proposal
auth/mode.ts//_emdash/api/auth/modereturn the enabled methods alongsideproviders, so the login page (and anything else) can render from data instead of assumptions.LoginPage.tsx: when passkey is disabled and exactly one provider is configured, promote that provider's button to the primary position; drop the divider and the magic-link row. (Strings go through Lingui as usual.)?method=passkeyon the login URL as a documented break-glass override — with providers-only login, a provider outage or a misconfigured client would otherwise lock every admin out. (Open to a different shape here: an env var, or honoring the flag only for existing passkey-enrolled users.)Defaults unchanged: with no
loginMethodsconfigured, the page and routes behave exactly as today.Why in core rather than a fork or CSS
The layout and the route gating both live in core (
packages/admin/src/components/LoginPage.tsx,packages/core/src/auth/mode.ts, the passkey/magic-link routes). A provider package can add a login method but can't subtract the built-ins, and hiding them with CSS leaves the routes live. This is config for a policy decision that only the site owner can make — same altitude asauthProvidersitself.Status
Design only at this point — we're running the provider setup above in production and will prototype
loginMethodsin our patched checkout next. Happy to build the PR (implementation + tests + docs + changeset) if this direction works for you, and to adjust naming (loginMethodsvslogin: { methods }), the single-provider promotion heuristic, and the break-glass mechanism to taste.Related: this is the same production use case behind #2836 (pluggable token verifiers) — filed separately since the two are independent.
All reactions