forgejo: add "Sign in with GitHub" (OAuth2 source + auto-registration)
All checks were successful
ci/woodpecker/push/default Pipeline was successful

Viktor wanted people to be able to sign up with GitHub, not just the
native form or Authentik SSO.

- Added a GitHub OAuth2 login source via `forgejo admin auth add-oauth
  --provider github` (name "github", matching the callback registered on
  the GitHub OAuth App). Like the existing Authentik source, it lives in
  Forgejo's DB rather than Terraform — there's no clean TF resource for
  login sources. Client id/secret mirrored to Vault secret/viktor
  (forgejo_github_oauth_client_id / _secret) for recovery.
- This commit's TF change: ENABLE_AUTO_REGISTRATION=true in
  [oauth2_client], so a first GitHub sign-in creates the account directly
  ("sign up with GitHub") instead of a link-to-existing detour. The
  GitHub identity is the trust gate for this path; Turnstile + email
  confirmation still gate the native form.

Verified: GitHub recognises the client id, Forgejo's /user/oauth2/github
redirects to GitHub's authorize URL with the correct client id +
callback, and the login page renders the button. Final browser
click-through is the user's to do.

Runbook updated: docs/runbooks/forgejo-open-signups.md (GitHub section +
secret-rotation + DB-loss recreate steps).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-06-19 16:41:49 +00:00
parent fd0c7493c3
commit 4a66377425
3 changed files with 58 additions and 6 deletions

View file

@ -96,7 +96,7 @@
| n8n | Workflow automation | n8n |
| real-estate-crawler | Property crawler | real-estate-crawler |
| tor-proxy | Tor proxy | tor-proxy |
| forgejo | Git forge. Open native self-signup (Turnstile captcha + email confirm) alongside Authentik OAuth; see `docs/runbooks/forgejo-open-signups.md` | forgejo |
| forgejo | Git forge. Open native self-signup (Turnstile captcha + email confirm) + Authentik & GitHub OAuth sign-in; see `docs/runbooks/forgejo-open-signups.md` | forgejo |
| freshrss | RSS reader | freshrss |
| navidrome | Music streaming | navidrome |
| networking-toolbox | Network tools | networking-toolbox |

View file

@ -10,12 +10,14 @@ layers:
2. **Mandatory email confirmation** — a new account stays inactive until the
user clicks an activation link emailed to the address they registered with.
The pre-existing **Authentik OAuth2 login** ("Sign in with …") is unchanged and
still works alongside local accounts. This is additive — opening local signups
did not touch SSO.
Two external login sources also work alongside local accounts: the pre-existing
**Authentik OAuth2 login** (SSO) and **Sign in with GitHub** (see the GitHub
section below). Opening local signups was additive — it did not touch SSO.
Everything is Terraform-managed in `stacks/forgejo/`. There is no dashboard or
manual cluster state.
Most of this is Terraform-managed in `stacks/forgejo/`. The one exception is the
OAuth2 login *sources* (Authentik, GitHub), which live in Forgejo's own DB and
are added via `forgejo admin auth` — there is no clean Terraform resource for
them (their secrets are mirrored to Vault for recovery).
## What is configured (and where)
@ -33,6 +35,7 @@ env vars):
| `service.CF_TURNSTILE_SECRET` | from `forgejo-turnstile` Secret | Server-side verification |
| `service.REGISTER_EMAIL_CONFIRM` | `true` | Account inactive until email is confirmed |
| `mailer.*` | see below | Sends the activation email |
| `oauth2_client.ENABLE_AUTO_REGISTRATION` | `true` | First GitHub (OAuth2) sign-in auto-creates the account |
Captcha guards **registration only**`REQUIRE_CAPTCHA_FOR_LOGIN` is left at the
default `false`, so existing users are not captcha'd on every login.
@ -76,6 +79,42 @@ the `/user/sign_up` HTML afterwards.
- The deployment carries `reloader.stakater.com/auto: "true"`, so a rotation of
either secret rolls the pod automatically.
## GitHub sign-in (OAuth2 source)
People can **sign up / sign in with GitHub** — a second Forgejo OAuth2 source
alongside Authentik.
- **Source** (Forgejo DB, *not* Terraform — added via CLI, same as Authentik):
```
forgejo admin auth add-oauth --name github --provider github --key <client-id> --secret <client-secret>
```
The source **name must stay `github`** — it is part of the callback URL
(`/user/oauth2/github/callback`) registered on the GitHub side, so renaming it
breaks the callback. `forgejo admin auth list` shows it (ID 2).
- **GitHub OAuth App**: a classic OAuth App under the ViktorBarzin GitHub account
(Settings → Developer settings → OAuth Apps). Homepage
`https://forgejo.viktorbarzin.me`, callback
`https://forgejo.viktorbarzin.me/user/oauth2/github/callback`. GitHub has **no
API to create OAuth Apps** — creating it is a browser-only step.
- **Credentials**: Vault `secret/viktor``forgejo_github_oauth_client_id` /
`forgejo_github_oauth_client_secret` (kept for recovery; the live values are in
Forgejo's DB).
- **Auto-registration**: `FORGEJO__oauth2_client__ENABLE_AUTO_REGISTRATION=true`
(`main.tf`) makes a first GitHub login create the account directly. The GitHub
identity is the trust gate for this path — the Turnstile captcha + email
confirmation only apply to the **native** signup form, not OAuth.
**Rotate the GitHub client secret** — generate a new one in the GitHub OAuth App, then:
```
vault kv patch secret/viktor forgejo_github_oauth_client_secret=<new>
POD=$(kubectl -n forgejo get pod -l app=forgejo -o jsonpath='{.items[0].metadata.name}')
kubectl -n forgejo exec "$POD" -- su-exec git forgejo admin auth update-oauth --id 2 --secret <new>
```
(Source id from `forgejo admin auth list`.)
**Recreate after a Forgejo DB loss**: the source is not in Terraform, so after a
from-scratch restore, re-run the `add-oauth` command above with the Vault creds.
## Re-closing / tightening signups
Edit `stacks/forgejo/main.tf` and `scripts/tg apply` (or commit + push — CI

View file

@ -280,6 +280,19 @@ resource "kubernetes_deployment" "forgejo" {
}
}
}
# Auto-create a local account on first GitHub (OAuth2) sign-in, so
# "Sign in with GitHub" is a real sign-up path rather than a
# link-to-existing detour. The GitHub identity is the trust gate for
# this OAuth path (the Turnstile captcha + email confirmation apply to
# the native form, not OAuth). The GitHub OAuth2 source itself is added
# out-of-band via `forgejo admin auth add-oauth` (it lives in Forgejo's
# DB, not Terraform same as the Authentik source); credentials are in
# Vault secret/viktor (forgejo_github_oauth_client_id / _secret). See
# docs/runbooks/forgejo-open-signups.md.
env {
name = "FORGEJO__oauth2_client__ENABLE_AUTO_REGISTRATION"
value = "true"
}
volume_mount {
name = "data"
mount_path = "/data"