Add OAuth2/OIDC per-user Gitea authentication
Some checks failed
docker / lint (push) Has been cancelled
docker / test (push) Has been cancelled
docker / docker-build (push) Has been cancelled
lint / lint (push) Has been cancelled
test / test (push) Has been cancelled

Introduce a GiteaOAuthValidator for JWT and userinfo validation and
fallbacks, add /oauth/token proxy, and thread per-user tokens through
the
request context and automation paths. Update config and .env.example for
OAuth-first mode, add OpenAPI, extensive unit/integration tests,
GitHub/Gitea CI workflows, docs, and lint/test enforcement (>=80% cov).
This commit is contained in:
2026-02-25 16:54:01 +01:00
parent a00b6a0ba2
commit 59e1ea53a8
31 changed files with 2575 additions and 660 deletions

182
README.md
View File

@@ -1,85 +1,149 @@
# AegisGitea-MCP
Security-first, policy-driven MCP gateway for Gitea.
Security-first MCP server for self-hosted Gitea with per-user OAuth2/OIDC authentication.
AegisGitea-MCP exposes controlled read and optional write capabilities to AI agents through MCP-compatible endpoints, with strict validation, policy enforcement, tamper-evident audit logging, and secure-by-default runtime controls.
AegisGitea-MCP exposes MCP tools over HTTP/SSE and validates each user token against Gitea so tool access follows each user's actual repository permissions.
## Highlights
## Securing MCP with Gitea OAuth
- Security-first defaults (localhost bind, write mode disabled, no stack traces in production errors).
- YAML policy engine with global/per-repository tool allow/deny and optional path restrictions.
- Expanded read tools for repositories, commits, diffs, issues, PRs, labels, tags, and releases.
- Strict write mode (opt-in + policy enforcement, with whitelist by default).
- Tamper-evident audit logging with hash-chain integrity validation.
- Secret detection/sanitization for outbound payloads.
- Structured JSON logging + Prometheus metrics.
- Hardened Docker runtime (non-root, no-new-privileges, capability drop, read-only where practical).
### 1) Create a Gitea OAuth2 application
## Quick Start
1. Open `https://git.hiddenden.cafe/user/settings/applications` (or admin application settings).
2. Create an OAuth2 app.
3. Set redirect URI to the ChatGPT callback URL shown after creating a New App.
4. Save the app and keep:
- `Client ID`
- `Client Secret`
### 1. Install dependencies
Required scopes:
- `read:repository`
- `write:repository` (only needed when using write tools)
```bash
make install-dev
```
### 2. Configure environment
### 2) Configure this MCP server
```bash
cp .env.example .env
```
Set at minimum:
- `GITEA_URL`
- `GITEA_TOKEN`
- `MCP_API_KEYS`
Set OAuth-first values:
### 3. Run locally
```bash
make run
```env
GITEA_URL=https://git.hiddenden.cafe
OAUTH_MODE=true
GITEA_OAUTH_CLIENT_ID=<your-client-id>
GITEA_OAUTH_CLIENT_SECRET=<your-client-secret>
OAUTH_EXPECTED_AUDIENCE=<optional; defaults to client id>
```
Server defaults to `127.0.0.1:8080`.
### 3) Configure ChatGPT New App
## Core Commands
In ChatGPT New App:
- `make test`: run pytest with coverage.
- `make lint`: run Ruff + mypy.
- `make format`: run Black + Ruff autofix.
- `make docker-up`: start hardened prod-profile container.
- `make docker-down`: stop containers.
- `make validate-audit`: validate audit hash chain integrity.
- MCP server URL: `https://<your-mcp-domain>/mcp/sse`
- Authentication: OAuth
- OAuth client ID: Gitea OAuth app client ID
- OAuth client secret: Gitea OAuth app client secret
## Security Model
After creation, copy the ChatGPT callback URL and add it to the Gitea OAuth app redirect URIs.
- Authentication: API keys (`Authorization: Bearer <key>`).
- Authorization: policy engine (`policy.yaml`) evaluated before tool execution.
- Rate limiting: per-IP and per-token.
- Output controls: bounded response size and optional secret masking/blocking.
- Write controls: `WRITE_MODE=false` by default; when enabled, use whitelist or opt into `WRITE_ALLOW_ALL_TOKEN_REPOS=true`.
### 4) OAuth-protected MCP behavior
The server publishes protected-resource metadata:
- `GET /.well-known/oauth-protected-resource`
Example response:
```json
{
"resource": "https://git.hiddenden.cafe",
"authorization_servers": ["https://git.hiddenden.cafe"],
"bearer_methods_supported": ["header"],
"scopes_supported": ["read:repository", "write:repository"],
"resource_documentation": "https://hiddenden.cafe/docs/mcp-gitea"
}
```
If a tool call is missing/invalid auth, MCP endpoints return `401` with:
```http
WWW-Authenticate: Bearer resource_metadata="https://<mcp-host>/.well-known/oauth-protected-resource", scope="read:repository"
```
## Architecture
```text
ChatGPT App
-> Authorization Code Flow
-> Gitea OAuth2/OIDC (issuer: https://git.hiddenden.cafe)
-> Access token
-> MCP Server (/mcp/sse, /mcp/tool/call)
-> OIDC discovery + JWKS cache
-> Scope enforcement (read:repository / write:repository)
-> Per-request Gitea API calls with Authorization: Bearer <user token>
```
## Example curl
Protected resource metadata:
```bash
curl -s https://<mcp-host>/.well-known/oauth-protected-resource | jq
```
Expected 401 challenge when missing token:
```bash
curl -i https://<mcp-host>/mcp/tool/call \
-H "Content-Type: application/json" \
-d '{"tool":"list_repositories","arguments":{}}'
```
Authenticated tool call:
```bash
curl -s https://<mcp-host>/mcp/tool/call \
-H "Authorization: Bearer <user_access_token>" \
-H "Content-Type: application/json" \
-d '{"tool":"list_repositories","arguments":{}}'
```
## Threat model
- Shared bot tokens are dangerous:
- one leaked token can expose all repositories reachable by that bot account.
- blast radius is repository-wide and cross-user.
- Token-in-URL is insecure:
- URLs leak via logs, proxies, browser history, and referers.
- bearer tokens must be sent in `Authorization` headers only.
- Per-user OAuth reduces lateral access:
- each call runs as the signed-in user.
- users only see repositories they already have permission for in Gitea.
## CI/CD
Gitea workflows were added under `.gitea/workflows/`:
- `lint.yml`: Ruff + formatting + mypy.
- `test.yml`: lint + pytest + enforced coverage (`>=80%`).
- `docker.yml`: lint+test gated Docker build, SHA tag, `latest` tag on `main`.
## Docker hardening
`docker/Dockerfile` uses a multi-stage build, non-root runtime user, production env flags, minimal runtime dependencies, and a healthcheck.
## Commands
- `make test`
- `make lint`
- `make format`
- `make docker-build`
- `make docker-up`
## Documentation
All detailed docs are under `docs/`:
- `docs/api-reference.md`
- `docs/policy.md`
- `docs/security.md`
- `docs/audit.md`
- `docs/write-mode.md`
- `docs/configuration.md`
- `docs/deployment.md`
- `docs/observability.md`
- `docs/automation.md`
- `docs/governance.md`
- `docs/roadmap.md`
- `docs/todo.md`
## Conduct and Governance
- Contributor/maintainer conduct: `CODE_OF_CONDUCT.md`
- AI agent behavioral contract: `AGENTS.md`
## License
MIT (see `LICENSE`).
- `docs/write-mode.md`