Skip to content

Auto-promote first OAuth login to admin when no admin exists#531

Merged
compscidr merged 2 commits intomainfrom
fix/wizard-when-no-admin-user
Apr 30, 2026
Merged

Auto-promote first OAuth login to admin when no admin exists#531
compscidr merged 2 commits intomainfrom
fix/wizard-when-no-admin-user

Conversation

@compscidr
Copy link
Copy Markdown
Collaborator

Summary

Closes the second half of #529.

When `.env` is pre-populated by configuration management on a fresh install, `rootHandler` only checks `.env` (not the DB) when deciding whether to show the install wizard, so the wizard's UI flow is skipped entirely. The wizard is the only existing path that creates an `admin_users` row, so the site comes up with no admin user. After #530 made `IsAdmin` strict, the symptom for the operator becomes: `/login` and `/logout` work, the OAuth dance succeeds, the BlogUser is created — but no admin link, no admin pages, no way to administer the site.

This PR adds `Auth.EnsureAdmin(blogUserID)` and calls it from `LoginPostHandler` after a successful OAuth login. It promotes the logging-in user to admin if and only if no `admin_users` row exists yet — same trust model as the wizard's `updateAdminUser` (whoever first completes OAuth against the server's `client_secret` becomes the admin), but reachable from the regular login path so an operator who pre-populates `.env` can finish setup just by clicking "Sign in with GitHub".

The wizard's own flow is unchanged: it still runs when `.env` is missing or incomplete, still ends in `updateAdminUser`. `EnsureAdmin` is idempotent — once an admin exists, subsequent logins are pure authentications and no promotion happens.

Tests

`auth/auth_test.go` covers:

  • `EnsureAdmin` creates a row when none exists and points to the supplied `BlogUserID`.
  • `EnsureAdmin` is a no-op when an admin already exists (the existing admin is preserved; a second caller doesn't get promoted).

```
ok goblog/admin 0.029s
ok goblog/auth 0.007s
ok goblog/blog 0.028s
```

Note on the trust model

Anyone with a GitHub account can complete OAuth against the public site's `/login`, so "first to log in becomes admin" is a race that the operator has to win on first deploy. This is unchanged from the existing wizard flow, which has the same race. A stricter model (e.g. configuring an expected GitHub login in `.env` and only promoting that user) is a reasonable follow-up but out of scope here.

🤖 Generated with Claude Code

When .env is pre-populated by configuration management on a fresh
install, the install wizard's UI flow is skipped (rootHandler only
checks .env, not the DB) and the wizard's updateAdminUser is never
called. The site comes up with no admin_users row, so even after the
operator successfully logs in via /login they remain unable to
administer the site.

Add EnsureAdmin to Auth and call it from LoginPostHandler. It promotes
the logging-in user to admin if and only if no admin_users row exists.
Same trust model as the wizard (whoever first completes OAuth against
the server's client_secret becomes the admin), but reachable from the
regular login path so automated deploys work end-to-end.

Refs #529

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 30, 2026 16:30
compscidr added a commit to compscidr/iac that referenced this pull request Apr 30, 2026
Includes goblogplatform/goblog#531 (auto-promote first OAuth login to
admin), so a fresh deploy with pre-populated .env can finish setup
just by signing in with GitHub — no manual wizard step needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

❌ Patch coverage is 75.00000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
auth/auth.go 75.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses fresh-install deployments where a pre-populated .env causes the install wizard UI to be skipped, leaving the DB without an admin_users row and therefore no way to administer the site after OAuth login.

Changes:

  • Add Auth.EnsureAdmin(blogUserID) to auto-promote the first successful OAuth login to admin when no admin exists yet.
  • Call EnsureAdmin from LoginPostHandler after a successful OAuth login.
  • Add unit tests covering EnsureAdmin create/no-op behavior.

Reviewed changes

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

File Description
auth/auth.go Adds EnsureAdmin and invokes it during OAuth login to create the initial admin user when missing.
auth/auth_test.go Adds tests ensuring EnsureAdmin creates the first admin row and is a no-op once one exists.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread auth/auth.go Outdated
Comment thread auth/auth.go
Address PR feedback:
- Surface DB lookup errors other than gorm.ErrRecordNotFound rather
  than treating any error as "no admin exists" and proceeding to
  create one.
- Wrap the check-and-create in a transaction so two concurrent first
  logins can't both observe an empty admin_users table and each
  create a row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@compscidr compscidr merged commit 02355f7 into main Apr 30, 2026
1 check passed
@compscidr compscidr deleted the fix/wizard-when-no-admin-user branch April 30, 2026 16:42
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