Inventory and gap analysis of the 0.2.0 codebase against the full Gitea API and the official gitea-mcp toolset, the B1..B11 + security chunk plan, ADRs 0001-0006 for the full-access rebuild, SDK/OAuth research notes, and the instance swagger pinned for tool generation. The audit-logging doc moves to docs/audit-logging.md.
41 lines
2.0 KiB
Markdown
41 lines
2.0 KiB
Markdown
# ADR 0004: admin-token mode and per-user OAuth mode
|
|
|
|
Status: accepted (2026-10-14)
|
|
|
|
## Context
|
|
|
|
Two authentication models must coexist on one deployment: an admin with full
|
|
access, and other users who log in as themselves. Gitea is an OAuth2 provider
|
|
but has no RFC 8414 metadata and no dynamic client registration; Mistral Le
|
|
Chat requires dynamic client registration and Claude prefers it. Details:
|
|
`docs/research/sdk-oauth-gitea.md` sections 4 and 6.
|
|
|
|
## Decision
|
|
|
|
- **Admin-token mode (default).** `GITEA_TOKEN` is used for every call. stdio
|
|
always runs in this mode. On HTTP, a request carrying the static
|
|
`MCP_BEARER_TOKEN` (constant-time comparison) runs in this mode.
|
|
- **Per-user OAuth mode (HTTP).** The server is an OAuth 2.1 authorization
|
|
server for MCP clients, using the SDK's `OAuthAuthorizationServerProvider`
|
|
with dynamic client registration, and a confidential OAuth2 client of Gitea
|
|
upstream (authorization code with PKCE S256, fixed and validated scope list).
|
|
Our `/oauth/callback` exchanges the Gitea code, then issues our own opaque
|
|
access and refresh tokens mapped to the user's Gitea tokens. Tool calls use
|
|
the user's Gitea access token, so Gitea enforces that user's permissions;
|
|
admin tools fail with Gitea's own 403 for non-admins. Upstream refresh is
|
|
automatic.
|
|
- **Token store.** A JSON file under `DATA_DIR` (default
|
|
`~/.local/state/aegis-gitea-mcp`, `/var/lib/aegis-mcp` in the image), mode
|
|
0600, values encrypted with Fernet using a key derived from
|
|
`TOKEN_ENCRYPTION_KEY`. Our own tokens are stored only as SHA-256 hashes.
|
|
- **Combined verifier.** One `TokenVerifier` accepts the static bearer (admin
|
|
identity) or an issued OAuth token (user identity). The SDK requires passing
|
|
it explicitly on the low-level server.
|
|
- **Identity in results.** Every tool result carries `identity: {login, mode,
|
|
sudo}`.
|
|
|
|
## Consequences
|
|
|
|
The server holds per-user Gitea tokens. That is the most sensitive asset after
|
|
the admin token and is covered by the threat model.
|