| 1 | # Forge authentication (v0.3) |
| 2 | |
| 3 | North star: [FORGE-ADR-0006](../design/FORGE-ADR-0006-auth-vpn-and-plugins.md) · [FORGE-ADR-0010](../design/FORGE-ADR-0010-auth-login-and-credential-store.md) · OAuth: [FORGE-ADR-0011](../design/FORGE-ADR-0011-oauth-sign-in-providers.md) |
| 4 | |
| 5 | ## Pilot behind VPN |
| 6 | |
| 7 | `FORGE_REQUIRE_AUTH=false` — API и Lens без Bearer. Git — SSH keys only. |
| 8 | |
| 9 | ## OAuth Sign in (recommended) |
| 10 | |
| 11 | Configure GitHub OAuth on the server: |
| 12 | |
| 13 | ```bash |
| 14 | FORGE_GITHUB_CLIENT_ID=… |
| 15 | FORGE_GITHUB_CLIENT_SECRET=… |
| 16 | ``` |
| 17 | |
| 18 | Then from workstation or CIDE: |
| 19 | |
| 20 | ```bash |
| 21 | forge auth login --host http://127.0.0.1:8770 |
| 22 | # Opens browser → Sign in with GitHub → token in ~/.forge/credentials.json |
| 23 | ``` |
| 24 | |
| 25 | Cascade IDE: `forge_lens.connect` tries OAuth first, falls back to device login if OAuth is not configured. |
| 26 | |
| 27 | ## Device login (fallback / bootstrap) |
| 28 | |
| 29 | ```bash |
| 30 | forge auth login --device --host http://127.0.0.1:8770 |
| 31 | # Terminal 2 — one-time bootstrap on server |
| 32 | FORGE_BOOTSTRAP_TOKEN=… forge auth approve ABCD-EFGH --host http://127.0.0.1:8770 |
| 33 | ``` |
| 34 | |
| 35 | Credentials land in `~/.forge/credentials.json`. **AgentForge.Mcp** reads them automatically when `FORGE_API_TOKEN` is unset. |
| 36 | |
| 37 | ## Commands |
| 38 | |
| 39 | | Command | Purpose | |
| 40 | |---------|---------| |
| 41 | | `forge auth login` | OAuth if configured, else device flow | |
| 42 | | `forge auth login --oauth github` | Force OAuth | |
| 43 | | `forge auth login --device` | Force device flow | |
| 44 | | `forge auth approve <code>` | Approve pending CLI (bootstrap) | |
| 45 | | `forge auth status` | Show saved login for host | |
| 46 | | `forge auth logout` | Remove host entry | |
| 47 | | `forge auth token` | Print token (scripts only) | |
| 48 | |
| 49 | ## Overrides |
| 50 | |
| 51 | | Variable | When | |
| 52 | |----------|------| |
| 53 | | `FORGE_BASE_URL` | Default host for CLI / MCP | |
| 54 | | `FORGE_API_TOKEN` | Wins over credential file (CI/debug) | |
| 55 | | `FORGE_BOOTSTRAP_TOKEN` | Approve device / create first API token | |
| 56 | |
| 57 | ## Bootstrap (server setup) |
| 58 | |
| 59 | ```bash |
| 60 | curl -X POST http://127.0.0.1:8770/api/v1/admin/tokens \ |
| 61 | -H "Authorization: Bearer $FORGE_BOOTSTRAP_TOKEN" \ |
| 62 | -H "Content-Type: application/json" \ |
| 63 | -d '{"name":"ops","scopes":"read,write,accept_merge"}' |
| 64 | ``` |
| 65 | |
| 66 | Prefer `forge auth login` for workstations instead of copying `fg_…` into MCP env. |
| 67 | |
| 68 | **Org catalog admin (no PAT):** after login, users with `org_members.role = admin` (alias `owner`) and `write` scope can create repo groups, org-scoped repos, and add members via MCP/API. Bootstrap still required for org create, bulk import, and `/admin/*`. See [FORGE-ADR-0034](../design/FORGE-ADR-0034-org-catalog-admin-auth-v1.md). |
| 69 | |
| 70 | ## Public AMS (internet-facing showcase) |
| 71 | |
| 72 | When the host is reachable by public IP (`FORGE_REQUIRE_AUTH=true`), treat it as **partially open**: public repos and `/view` are intentional; everything else must be locked down. |
| 73 | |
| 74 | ### Required `.env` on VPS |
| 75 | |
| 76 | | Variable | Value | Why | |
| 77 | |----------|-------|-----| |
| 78 | | `FORGE_REQUIRE_AUTH` | `true` | Bearer on MCP invoke and most write API | |
| 79 | | `FORGE_OAUTH_SIGNIN_POLICY` | `invite_only` (showcase default) | OAuth only for identities already in `FORGE_DEFAULT_ORG_SLUG` members (import or admin add) | |
| 80 | | `FORGE_ORG_AUTO_MEMBER_ON_LOGIN` | `false` | Do not auto-add every GitHub login to the org | |
| 81 | | `FORGE_GIT_HOOK_SECRET` | random 32+ bytes (`openssl rand -hex 32`) | Blocks anonymous `POST /api/v1/git/push` | |
| 82 | | `FORGE_REQUIRE_GIT_HOOK_SECRET` | `true` | Explicit; also implied when `FORGE_REQUIRE_AUTH=true` | |
| 83 | | `FORGE_BOOTSTRAP_TOKEN` | strong secret, server-only | Import, device approve, admin | |
| 84 | | `FORGE_GITHUB_CLIENT_ID/SECRET` | OAuth app | Human sign-in | |
| 85 | | `FORGE_GITHUB_IMPORT_TOKEN` | PAT with org repo scope | Private org import | |
| 86 | |
| 87 | `scripts/deploy-vps-lean.ps1 -Showcase` sets auth flags, **`FORGE_OAUTH_SIGNIN_POLICY=invite_only`**, **`FORGE_ORG_AUTO_MEMBER_ON_LOGIN=false`**, and **generates `FORGE_GIT_HOOK_SECRET` if missing** (does not overwrite an existing value). |
| 88 | |
| 89 | ### OAuth sign-in policy (`instance-user-management` plugin) |
| 90 | |
| 91 | | `FORGE_OAUTH_SIGNIN_POLICY` | Behavior | |
| 92 | |-----------------------------|----------| |
| 93 | | `open` | Any GitHub user may sign in (local dev default when `FORGE_REQUIRE_AUTH=false`) | |
| 94 | | `invite_only` | Identity must already be org member of `FORGE_DEFAULT_ORG_SLUG` (seed via `org-import-github` or `org-user-management`) | |
| 95 | | `github_org` | Live GitHub org check — set `FORGE_OAUTH_GITHUB_ORG=AI-Guiders` (requests `read:org` scope) | |
| 96 | |
| 97 | Org member CRUD: **`org-user-management`** plugin (`POST/GET/PATCH/DELETE /api/v1/orgs/{slug}/members`). |
| 98 | |
| 99 | ### Instance admin (`instance-user-management` plugin) |
| 100 | |
| 101 | | Role | Scope | Typical powers | |
| 102 | |------|-------|----------------| |
| 103 | | **Instance admin** | whole forge host | create orgs, bulk import, `/admin/*`, manage instance admins; catalog ops in **any** org | |
| 104 | | **Org admin** | one org | repo groups, org members, org-scoped repos | |
| 105 | | **Org member** | one org | read/write repos per token scopes | |
| 106 | |
| 107 | Bootstrap (`FORGE_BOOTSTRAP_TOKEN`) — break-glass / deploy only. Day-to-day host ops → promote instance admins via `POST /api/v1/instance/admins` (bootstrap or existing instance admin). Instance admins bypass `invite_only` OAuth gate. |
| 108 | |
| 109 | ### Post-deploy smoke (no token) |
| 110 | |
| 111 | ```bash |
| 112 | curl -sf http://HOST:8770/health # 200 |
| 113 | curl -sf http://HOST:8770/view/orgs/ai-guiders # 200 |
| 114 | curl -s -o /dev/null -w '%{http_code}\n' -X POST http://HOST:8770/api/v1/git/push \ |
| 115 | -H 'Content-Type: application/json' -d '{"repo":"x","commit":"0","branch":"main"}' # 401 or 503, not 200 |
| 116 | curl -s -o /dev/null -w '%{http_code}\n' http://HOST:8770/api/v1/admin/plugins/status # 401 when auth on |
| 117 | curl -s -o /dev/null -w '%{http_code}\n' -X POST http://HOST:8770/api/v1/mcp/invoke # 401 |
| 118 | ``` |
| 119 | |
| 120 | ### Still public by design |
| 121 | |
| 122 | - `/view/orgs/{slug}` — public repo catalog |
| 123 | - `/api/v1/orgs` — org slugs and repo counts (private repo **names** hidden) |
| 124 | - `/api/v1/capabilities` — MCP tool catalog (invoke still needs token) |
| 125 | |
| 126 | ### Recommended hardening (ops) |
| 127 | |
| 128 | - TLS reverse proxy (Caddy/nginx) in front of `:8770` |
| 129 | - Firewall: expose `2222` (git-ssh) only if needed; never without `FORGE_GIT_AUTHORIZED_KEYS` |
| 130 | - Rotate `FORGE_BOOTSTRAP_TOKEN` after initial setup; keep off workstations |
| 131 | |
| 132 | See also [git-transport.md](git-transport.md) and [FORGE-ADR-0024](../design/FORGE-ADR-0024-orgs-visibility-and-repo-catalog.md). |
| 133 | |