Skip to content

feat(auth): surface features.sso in public /auth/config#2330

Merged
os-zhuang merged 2 commits into
mainfrom
fix/login-sso-button-server-gated
Jun 25, 2026
Merged

feat(auth): surface features.sso in public /auth/config#2330
os-zhuang merged 2 commits into
mainfrom
fix/login-sso-button-server-gated

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Problem

The console login page renders a "Sign in with SSO" button unconditionally. On a deployment where enterprise SSO isn't wired (default for self-hosted / os dev), the /sign-in/sso route isn't mounted, so the button is a dead end that only reveals "No SSO provider is configured for this email domain." at click time.

Root cause on the server side: getPublicConfig() (the /auth/config payload the SPA reads) advertised every other capability flag — oidcProvider, twoFactor, multiOrgEnabled, deviceAuthorization, … — but omitted enterprise SSO, even though the manager already computes whether @better-auth/sso is wired (OS_SSO_ENABLED / plugins.sso). The login UI had no signal to gate on.

Fix

Two layers, so the advertised capability matches reality at both granularities:

  1. Coarse (sync, cheap)getPublicConfig().features.sso now reports whether the plugin is wired, resolved with the EXACT logic that decides whether it's mounted in buildPlugins() (factored into a private isSsoWired()). This honours the invariant the existing comment already demands: "the /auth/config response MUST match what's actually wired, otherwise the frontend will render UI for endpoints that 404."

  2. Precise (async, at the route) — the /auth/config handler refines that flag to usable via the new AuthManager.isSsoUsable(): wired AND at least one sys_sso_provider row exists. This also hides the button when SSO is enabled but no IdP is configured yet. It only queries when wired, and fails open to the wired flag on any introspection error (no data engine, query throws) so the login config never 500s. Applied in both the plugin-auth route and the hono adapter /config path (the latter guarded as a no-op against an older auth service).

Per-email domain→IdP matching still happens at /sign-in/sso; isSsoUsable() answers the coarser "is there any point showing the button at all".

Tests

auth-manager.test.ts:

  • features.sso (coarse): default-off, on-via-plugins.sso, OS_SSO_ENABLED env override.
  • isSsoUsable() (precise): not-wired (skips query), zero-providers → false, ≥1 provider → true, no-engine fail-open, query-throws fail-open.

95 plugin-auth tests green, hono suite green (67), tsc --noEmit clean on index.ts (3 pre-existing hono.test.ts context-typing errors are unrelated). Browser end-to-end verification of the live /auth/config + login page done alongside the objectui PR.

Pairs with

Client-side consumer that hides the button: objectstack-ai/objectui#2006

🤖 Generated with Claude Code

getPublicConfig() advertised every other auth capability flag (oidcProvider,
twoFactor, multiOrgEnabled, …) but omitted enterprise SSO, even though the
manager already computes whether @better-auth/sso is wired (OS_SSO_ENABLED /
plugins.sso). The login UI therefore had nothing to gate on and rendered the
"Sign in with SSO" button unconditionally; on a deployment without SSO wired,
clicking it only then surfaced "No SSO provider is configured for this email
domain."

features.sso is now resolved with the EXACT logic that decides whether the
plugin is mounted in buildPlugins(), so the advertised capability can never
disagree with the actual /sign-in/sso route. Tests cover default-off, config-on,
and OS_SSO_ENABLED env override.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 25, 2026 4:11pm

Request Review

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): packages/adapters, @objectstack/plugin-auth.

10 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/implementation-status.mdx (via @objectstack/plugin-auth)
  • content/docs/concepts/packages.mdx (via @objectstack/plugin-auth)
  • content/docs/getting-started/cli.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/auth-sso.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/authentication.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/kernel-services.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/packages.mdx (via packages/adapters, @objectstack/plugin-auth)
  • content/docs/guides/plugins.mdx (via @objectstack/plugin-auth)
  • content/docs/guides/production-readiness.mdx (via @objectstack/plugin-auth)
  • content/docs/releases/v9.mdx (via @objectstack/plugin-auth)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Follow-up to the coarse features.sso flag: getPublicConfig() still returns the
"wired" capability (cheap, sync), but the /auth/config route now refines it to
"usable" via the new AuthManager.isSsoUsable() — wired AND >=1 sys_sso_provider
row exists. This hides the "Sign in with SSO" button not only when SSO is off
but also when it's enabled yet no IdP is configured yet (previously the button
showed and errored for everyone at click time).

isSsoUsable() only queries sys_sso_provider when wired and fails OPEN to the
wired flag on any introspection failure (no data engine, query throws), so the
login config endpoint never 500s. The coarse computation is factored into a
private isSsoWired() shared by getPublicConfig() and isSsoUsable(). Both the
plugin-auth route and the hono adapter /config path apply the refinement (the
latter guarded so it's a no-op against an older auth service). 5 new unit tests
cover not-wired, zero-providers, >=1 provider, no-engine fail-open, and
query-throws fail-open.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added size/m and removed size/s labels Jun 25, 2026
@os-zhuang
os-zhuang merged commit 1e8a813 into main Jun 25, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the fix/login-sso-button-server-gated branch June 25, 2026 16:20
os-zhuang added a commit that referenced this pull request Jul 6, 2026
* chore: bump objectui to c27bd3264f39

feat(studio): per-object API / Hooks / Actions tabs + Data-pillar polish (#2330)

objectui@c27bd3264f394bec5f5f71326118bfb115fbe884

* chore(cli): stop default-loading @objectstack/studio app package

os dev / os start / os serve no longer register the Studio one-app
package by default. The console ships a dedicated Studio surface at
/_console/studio/<pkg>/<pillar>, so Studio no longer needs to exist as
a navigable app tile in "Your apps".

Setup and Account app packages are unchanged. The @objectstack/studio
package itself is kept (still registerable explicitly; ADR-0048
app-split test continues to cover it).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: add changeset for dropping default studio app load

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
os-zhuang added a commit that referenced this pull request Jul 6, 2026
* chore: bump objectui to c27bd3264f39

feat(studio): per-object API / Hooks / Actions tabs + Data-pillar polish (#2330)

objectui@c27bd3264f394bec5f5f71326118bfb115fbe884

* docs: reframe getting-started around the AI build/verify loop

Center the docs on the actual ObjectStack workflow: an AI agent (Claude Code)
authors typed metadata, a validation gate catches silent-at-runtime mistakes,
the human verifies in the visual Console, and the built app is itself AI-operable
over MCP.

- New flagship tutorial "Build with Claude Code" — the whole loop on a
  live-built Support Desk app, grounded in real `os` CLI output.
- New concept page "How AI Development Works" — why the loop is fast and safe
  (skills+AGENTS.md, os validate, contract-first, draft-gating, worktree).
- Repurpose the old hand-authoring Quick Start into "Anatomy of an ObjectStack
  App" (read-to-verify); keep the /quick-start slug so inbound links survive.
- Front-load the "your app is an MCP server" story: env-var value-prop, a
  dedicated api/index section + request-path diagram, deployment pointer.
- Reframe by real development layering (Data / Automation / Interface / Access /
  AI) instead of the ObjectQL/ObjectOS/ObjectUI protocol trinity.
- Add Mermaid diagrams (build loop, AI architecture, protocol stack, MCP request
  path) using the site's existing Mermaid support.
- Docs-site chrome: drop the meaningless "Documentation" self-link, fix the UI
  Engine icon (deprecated `Layout` -> `LayoutDashboard`), hide the
  single-language switcher.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant