Skip to content

feat(system-config): serve the public system config unauthenticated - #136

Merged
Bccorb merged 2 commits into
mainfrom
feat/public-system-config-endpoint
Jul 31, 2026
Merged

feat(system-config): serve the public system config unauthenticated#136
Bccorb merged 2 commits into
mainfrom
feat/public-system-config-endpoint

Conversation

@Bccorb

@Bccorb Bccorb commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Second of three PRs adding the ability to skip passkey registration.

Draft: blocked on @seamless-auth/types 0.5.0 (fells-code/seamless-auth-types#23). This branch imports PublicSystemConfigResponseSchema, which is not published yet, so CI will fail on install until that lands. The dependency pin bump from ^0.4.0 is 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 own SYSTEM_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 public GET /oauth/providers, which already exposes provider config to signed-out callers.

Two deliberate choices:

  • Reads through 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.
  • loginMethods only. 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 security block on the new path, against bearerAuth on its /system-config/roles neighbour, 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:check all clean. openapi.json and src/generated/api.ts regenerated; tests/unit/openapi/generatedContract.spec.ts caught the stale contract before I did.

Three integration tests added: the configured list is returned, a config with no login_methods falls back to the defaults rather than an empty list, and the response body carries no key but loginMethods.

Next

seamless-auth-react: consume this, gate a skip control on the passkey registration screen, and drop the hardcoded fallback.

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.
@Bccorb
Bccorb marked this pull request as ready for review July 31, 2026 02:53
@Bccorb
Bccorb merged commit ecf4946 into main Jul 31, 2026
2 checks passed
@Bccorb
Bccorb deleted the feat/public-system-config-endpoint branch July 31, 2026 03:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant