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 | | n8n | Workflow automation | n8n |
| real-estate-crawler | Property crawler | real-estate-crawler | | real-estate-crawler | Property crawler | real-estate-crawler |
| tor-proxy | Tor proxy | tor-proxy | | 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 | | freshrss | RSS reader | freshrss |
| navidrome | Music streaming | navidrome | | navidrome | Music streaming | navidrome |
| networking-toolbox | Network tools | networking-toolbox | | networking-toolbox | Network tools | networking-toolbox |

View file

@ -10,12 +10,14 @@ layers:
2. **Mandatory email confirmation** — a new account stays inactive until the 2. **Mandatory email confirmation** — a new account stays inactive until the
user clicks an activation link emailed to the address they registered with. user clicks an activation link emailed to the address they registered with.
The pre-existing **Authentik OAuth2 login** ("Sign in with …") is unchanged and Two external login sources also work alongside local accounts: the pre-existing
still works alongside local accounts. This is additive — opening local signups **Authentik OAuth2 login** (SSO) and **Sign in with GitHub** (see the GitHub
did not touch SSO. section below). Opening local signups was additive — it did not touch SSO.
Everything is Terraform-managed in `stacks/forgejo/`. There is no dashboard or Most of this is Terraform-managed in `stacks/forgejo/`. The one exception is the
manual cluster state. 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) ## What is configured (and where)
@ -33,6 +35,7 @@ env vars):
| `service.CF_TURNSTILE_SECRET` | from `forgejo-turnstile` Secret | Server-side verification | | `service.CF_TURNSTILE_SECRET` | from `forgejo-turnstile` Secret | Server-side verification |
| `service.REGISTER_EMAIL_CONFIRM` | `true` | Account inactive until email is confirmed | | `service.REGISTER_EMAIL_CONFIRM` | `true` | Account inactive until email is confirmed |
| `mailer.*` | see below | Sends the activation email | | `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 Captcha guards **registration only**`REQUIRE_CAPTCHA_FOR_LOGIN` is left at the
default `false`, so existing users are not captcha'd on every login. 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 - The deployment carries `reloader.stakater.com/auto: "true"`, so a rotation of
either secret rolls the pod automatically. 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 ## Re-closing / tightening signups
Edit `stacks/forgejo/main.tf` and `scripts/tg apply` (or commit + push — CI 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 { volume_mount {
name = "data" name = "data"
mount_path = "/data" mount_path = "/data"