Skip to content

feat(auth): auto-hide OAuth buttons when secrets absent; add ALLOW_REGISTRATION toggle - #349

Merged
arozumenko merged 3 commits into
mainfrom
feat/auth-oauth-visibility-and-registration-toggle
May 22, 2026
Merged

feat(auth): auto-hide OAuth buttons when secrets absent; add ALLOW_REGISTRATION toggle#349
arozumenko merged 3 commits into
mainfrom
feat/auth-oauth-visibility-and-registration-toggle

Conversation

@arozumenko

Copy link
Copy Markdown
Owner

Summary

  • OAuth visibility: GitHub and Google sign-in buttons are now shown/hidden automatically based on whether GITHUB_CLIENT_ID/GITHUB_CLIENT_SECRET and GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET are set. A new GET /api/auth/config endpoint exposes this to the client at runtime, replacing the unreliable manual NEXT_PUBLIC_SHOW_GITHUB/NEXT_PUBLIC_SHOW_GOOGLE flags (removed).
  • Registration toggle: ALLOW_REGISTRATION=false in .env disables self-service account creation — blocks Better-Auth's /sign-up/email and the custom /register route (both return HTTP 403), and hides the "Create an account" toggle in the login UI. Default (var absent) keeps registration open.

Test plan

  • With GITHUB_CLIENT_ID/SECRET unset, GitHub button absent from login page; set both → button appears
  • Same for Google
  • ALLOW_REGISTRATION=false → "Create an account" link hidden, POST /api/auth/sign-up/email returns 403, POST /api/auth/register returns 403
  • ALLOW_REGISTRATION=true (or unset) → registration works as before

🤖 Generated with Claude Code

…GISTRATION toggle

OAuth provider buttons (GitHub, Google) are now derived from server-side
env vars at runtime via /api/auth/config, removing the error-prone manual
NEXT_PUBLIC_SHOW_* flags. Setting ALLOW_REGISTRATION=false blocks sign-up
at both the API layer (Better-Auth /sign-up/email and custom /register)
and hides the register toggle in the UI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 22, 2026 14:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds runtime-driven auth UI configuration and a server-enforced registration toggle, removing brittle build-time flags and ensuring OAuth/registration availability reflects actual server secrets/config.

Changes:

  • Add GET /api/auth/config to expose enabled OAuth providers + registration availability to the client.
  • Update login UI to fetch auth config at runtime and conditionally render OAuth buttons + registration toggle.
  • Enforce ALLOW_REGISTRATION=false server-side for both Better-Auth email sign-up and the custom /api/auth/register route.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
web/src/app/login/page.tsx Fetches /api/auth/config and conditionally renders OAuth + registration UI based on runtime config
web/src/app/api/auth/register/route.ts Blocks custom registration endpoint when ALLOW_REGISTRATION=false
web/src/app/api/auth/config/route.ts New endpoint returning provider enablement + registration flag
web/src/app/api/auth/[...all]/route.ts Wraps Better-Auth POST handler to block /sign-up/email when registration disabled
web/.env.example Removes NEXT_PUBLIC_SHOW_* flags, documents “blank secrets hide button”, adds ALLOW_REGISTRATION
.env.example Documents “blank secrets hide button” and adds ALLOW_REGISTRATION
Comments suppressed due to low confidence (1)

web/src/app/api/auth/[...all]/route.ts:1

  • The registration block relies on url.pathname.endsWith('/sign-up/email'). Requests with a trailing slash (e.g., /sign-up/email/) won’t match and could bypass the intended 403. Normalize the path (e.g., strip trailing slashes) and/or compare against the exact expected pathname so the block is reliably enforced.

Comment thread web/src/app/login/page.tsx Outdated
Comment on lines +31 to +35
const [authConfig, setAuthConfig] = useState<{
github: boolean;
google: boolean;
registrationEnabled: boolean;
} | null>(null);
Comment thread web/src/app/login/page.tsx Outdated
)}

{(showGithub || showGoogle) && (
{(authConfig?.github || authConfig?.google) && (
</Divider>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5, width: '100%' }}>
{showGithub && (
{authConfig.github && (
</Button>
)}
{showGoogle && (
{authConfig.google && (
Comment thread web/src/app/login/page.tsx Outdated
Comment on lines +44 to +50
fetch('/api/auth/config')
.then((r) => r.json())
.then((cfg) => {
setAuthConfig(cfg);
if (!cfg.registrationEnabled) setMode('login');
})
.catch(() => setAuthConfig({ github: false, google: false, registrationEnabled: true }));
…cache header, optimistic UI, tests

- Forward Next.js context param in catch-all POST (Better-Auth ignores it, but satisfies type contract)
- Use exact pathname equality for sign-up block (prevents false matches on similar paths)
- Add Cache-Control: public, max-age=300 to /api/auth/config (values are static per deployment)
- Optimistic initial authConfig state so register toggle renders without layout shift
- Integration tests for /api/auth/config and registration block in [...all]/route (12 cases)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comment on lines +46 to +52
fetch('/api/auth/config')
.then((r) => r.json())
.then((cfg) => {
setAuthConfig(cfg);
if (!cfg.registrationEnabled) setMode('login');
})
.catch(() => {/* keep optimistic defaults on network error */});
Comment on lines +11 to +14
const url = new URL(request.url);
if (process.env.ALLOW_REGISTRATION === 'false' && url.pathname === '/api/auth/sign-up/email') {
return NextResponse.json({ error: 'Registration is disabled' }, { status: 403 });
}
Comment thread .env.example Outdated
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_ID= # Leave blank to hide "Sign in with Google"
GOOGLE_CLIENT_SECRET=
ALLOW_REGISTRATION=true # Set to false to disable self-service account creation
…nt placement

- Check r.ok before r.json() in config fetch; coerce fields to booleans so a
  non-2xx JSON error body can't corrupt authConfig state
- Add mounted flag to config useEffect to prevent state update after unmount
- Use message field (not error) in 403 response so handleRegister surfaces
  the real reason to the user via data.message
- Move inline comments to their own lines in .env.example — trailing inline
  comments on assignment lines are treated as part of the value by some
  env parsers (dotenv, Docker Compose), causing non-empty CLIENT_ID values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@arozumenko
arozumenko merged commit 45cac3c into main May 22, 2026
5 checks passed
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.

2 participants