feat(system-config): serve the public system config unauthenticated - #136
Merged
Conversation
The bundled sign-in screens render before anyone has a session, so they cannot read the system configuration. Today they fall back to a hardcoded list of login methods, which can advertise a method this instance has turned off, and which is a second source of truth for something this server already knows. GET /system-config/public returns the configured loginMethods and nothing else, alongside the existing public GET /oauth/providers. It also unblocks offering a skip on passkey registration: that is only safe when another login method is enabled, and a client that cannot see the method list cannot make that call. The handler reads through getLoginPolicy rather than the raw config, so a tainted or partially written config answers with the defaults. A 500 here would take the sign-in screen down with it, since the client has nothing else to render from. Requires @seamless-auth/types 0.5.0 for PublicSystemConfigResponseSchema. The dependency pin is bumped separately once that release lands. Verified with npm run typecheck, npm run test:run (963 passing), npm run lint, and npm run format:check, against a local build of the types package. openapi.json and src/generated/api.ts regenerated; the contract test enforces it.
The release carrying PublicSystemConfigResponseSchema, which the public system-config route declares as its response. Verified against the published package with npm run typecheck, npm run test:run (963 passing), npm run lint, and npm run format:check.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Second of three PRs adding the ability to skip passkey registration.
Draft: blocked on
@seamless-auth/types0.5.0 (fells-code/seamless-auth-types#23). This branch importsPublicSystemConfigResponseSchema, which is not published yet, so CI will fail on install until that lands. The dependency pin bump from^0.4.0is the last commit here before it comes out of draft. Verified locally against a local build of the types package.Why
The bundled sign-in screens render before anyone has a session, so they cannot read the system configuration. The React SDK falls back to a hardcoded
DEFAULT_LOGIN_METHODS = ['passkey', 'magic_link', 'phone_otp'], which does not match this repo's ownSYSTEM_CONFIG_DEFAULTS.login_methods = ['passkey', 'magic_link']. A client can advertise a method the instance has turned off.It also unblocks the actual goal: registration currently forces a passkey with no way past the screen. Offering a skip is only safe when another login method is enabled, otherwise a user who declines can never sign back in, and a client cannot check that today.
What
GET /system-config/public, unauthenticated, returning{ loginMethods }and nothing else. It sits alongside the existing publicGET /oauth/providers, which already exposes provider config to signed-out callers.Two deliberate choices:
getLoginPolicy, not the raw config. A tainted or partially written config answers with the defaults instead of failing. A 500 here would take the sign-in screen down with it, since the client has nothing else to render from.loginMethodsonly. Every other key stays behind the admin routes. Both the route comment and the schema doc comment say so, so this does not quietly become a general config dump.The generated OpenAPI shows no
securityblock on the new path, againstbearerAuthon its/system-config/rolesneighbour, so the contract records it as public.Verification
npm run typecheck,npm run test:run(963 passing, 1 skipped),npm run lint,npm run format:checkall clean.openapi.jsonandsrc/generated/api.tsregenerated;tests/unit/openapi/generatedContract.spec.tscaught the stale contract before I did.Three integration tests added: the configured list is returned, a config with no
login_methodsfalls back to the defaults rather than an empty list, and the response body carries no key butloginMethods.Next
seamless-auth-react: consume this, gate a skip control on the passkey registration screen, and drop the hardcoded fallback.