docs: add Google OAuth setup guide and update README/CLAUDE.md

- Create docs/GOOGLE_OAUTH.md: complete OAuth setup walkthrough
  (Google Cloud Console, env vars, JWT key generation, troubleshooting)
- README.md: add Google OAuth section with quick-setup steps + link
- CLAUDE.md: add JWT key generation tip + link to full guide
This commit is contained in:
Jacob Magar
2026-03-16 10:59:30 -04:00
parent 440245108a
commit 6f7a58a0f9
3 changed files with 201 additions and 2 deletions

View File

@@ -78,8 +78,15 @@ the MCP server requires Google login before any tool call.
2. Authorized redirect URIs: `<UNRAID_MCP_BASE_URL>/auth/callback`
3. Copy Client ID + Secret to `~/.unraid-mcp/.env`
**Generate a stable JWT signing key:**
```bash
python3 -c "import secrets; print(secrets.token_hex(32))"
```
**Omit `GOOGLE_CLIENT_ID` to run without auth** (default — preserves existing behaviour).
**Full guide:** [`docs/GOOGLE_OAUTH.md`](docs/GOOGLE_OAUTH.md)
## Architecture
### Core Components

View File

@@ -14,7 +14,7 @@
- 🔄 **Real-time Data**: WebSocket subscriptions for live metrics, logs, array state, and more
- 📊 **Health Monitoring**: Comprehensive system diagnostics and status
- 🐳 **Docker Ready**: Full containerization support with Docker Compose
- 🔒 **Secure**: Proper SSL/TLS configuration and API key management
- 🔒 **Secure**: Optional Google OAuth 2.0 authentication + SSL/TLS + API key management
- 📝 **Rich Logging**: Structured logging with rotation and multiple levels
---
@@ -25,6 +25,7 @@
- [Quick Start](#-quick-start)
- [Installation](#-installation)
- [Configuration](#-configuration)
- [Google OAuth](#-google-oauth-optional)
- [Available Tools & Resources](#-available-tools--resources)
- [Development](#-development)
- [Architecture](#-architecture)
@@ -229,7 +230,7 @@ UNRAID_VERIFY_SSL=true # true, false, or path to CA bundle
# Subscription Configuration
UNRAID_AUTO_START_SUBSCRIPTIONS=true # Auto-start WebSocket subscriptions on startup (default: true)
UNRAID_MAX_RECONNECT_ATTEMPTS=5 # Max WebSocket reconnection attempts (default: 5)
UNRAID_MAX_RECONNECT_ATTEMPTS=10 # Max WebSocket reconnection attempts (default: 10)
# Optional: Log Stream Configuration
# UNRAID_AUTOSTART_LOG_PATH=/var/log/syslog # Path for log streaming resource (unraid://logs/stream)
@@ -245,6 +246,32 @@ UNRAID_MAX_RECONNECT_ATTEMPTS=5 # Max WebSocket reconnection attempts (def
---
## 🔐 Google OAuth (Optional)
Protect the HTTP server with Google OAuth 2.0 — clients must complete a Google login before any tool call is executed.
Add these to `~/.unraid-mcp/.env`:
```bash
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-your-secret
UNRAID_MCP_BASE_URL=http://10.1.0.2:6970 # public URL of this server
UNRAID_MCP_JWT_SIGNING_KEY=<64-char-hex> # prevents token invalidation on restart
```
**Quick setup:**
1. [Google Cloud Console](https://console.cloud.google.com/) → Credentials → OAuth 2.0 Client ID (Web application)
2. Authorized redirect URI: `<UNRAID_MCP_BASE_URL>/auth/callback`
3. Copy Client ID + Secret into `~/.unraid-mcp/.env`
4. Generate a signing key: `python3 -c "import secrets; print(secrets.token_hex(32))"`
5. Restart the server
Omit `GOOGLE_CLIENT_ID` to run without authentication (default behavior).
**Full guide:** [`docs/GOOGLE_OAUTH.md`](docs/GOOGLE_OAUTH.md)
---
## 🛠️ Available Tools & Resources
The single `unraid` tool uses `action` (domain) + `subaction` (operation) routing to expose all operations via one MCP tool, minimizing context window usage. Destructive actions require `confirm=True`.

165
docs/GOOGLE_OAUTH.md Normal file
View File

@@ -0,0 +1,165 @@
# Google OAuth Setup Guide
This document explains how to protect the Unraid MCP HTTP server with Google OAuth 2.0 authentication using FastMCP's built-in `GoogleProvider`.
---
## Overview
By default the MCP server is **open** — any client on the network can call tools. Setting three environment variables enables Google OAuth 2.1 authentication: clients must complete a Google login flow before the server will execute any tool.
OAuth state (issued tokens, refresh tokens) is persisted to an encrypted file store at `~/.local/share/fastmcp/oauth-proxy/`, so sessions survive server restarts when `UNRAID_MCP_JWT_SIGNING_KEY` is set.
> **Transport requirement**: OAuth only works with HTTP transports (`streamable-http` or `sse`). It has no effect on `stdio` — the server logs a warning if you configure both.
---
## Prerequisites
- Google account with access to [Google Cloud Console](https://console.cloud.google.com/)
- MCP server reachable at a known URL from your browser (LAN IP, Tailscale IP, or public domain)
- `UNRAID_MCP_TRANSPORT=streamable-http` (the default)
---
## Step 1: Create a Google OAuth Client
1. Open [Google Cloud Console](https://console.cloud.google.com/) → **APIs & Services****Credentials**
2. Click **Create Credentials****OAuth 2.0 Client ID**
3. Application type: **Web application**
4. Name: anything (e.g. `Unraid MCP`)
5. **Authorized redirect URIs** — add exactly:
```
http://<your-server-ip>:6970/auth/callback
```
Replace `<your-server-ip>` with the IP/hostname your browser uses to reach the MCP server (e.g. `10.1.0.2`, `100.x.x.x` for Tailscale, or a domain name).
6. Click **Create** — copy the **Client ID** and **Client Secret**
---
## Step 2: Configure Environment Variables
Add these to `~/.unraid-mcp/.env` (the canonical credential file for all runtimes):
```bash
# Google OAuth (optional — enables authentication)
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-your-client-secret
# Public base URL of this MCP server (must match the redirect URI above)
UNRAID_MCP_BASE_URL=http://10.1.0.2:6970
# Stable JWT signing key — prevents token invalidation on server restart
# Generate one: python3 -c "import secrets; print(secrets.token_hex(32))"
UNRAID_MCP_JWT_SIGNING_KEY=your-64-char-hex-string
```
**All four variables at once** (copy-paste template):
```bash
cat >> ~/.unraid-mcp/.env <<'EOF'
# Google OAuth
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
UNRAID_MCP_BASE_URL=http://10.1.0.2:6970
UNRAID_MCP_JWT_SIGNING_KEY=
EOF
```
Then fill in the blanks.
---
## Step 3: Generate a Stable JWT Signing Key
Without `UNRAID_MCP_JWT_SIGNING_KEY`, FastMCP derives a key on startup. Any server restart invalidates all existing tokens and forces every client to re-authenticate.
Generate a stable key once:
```bash
python3 -c "import secrets; print(secrets.token_hex(32))"
```
Paste the output into `UNRAID_MCP_JWT_SIGNING_KEY`. This value never needs to change unless you intentionally want to invalidate all sessions.
---
## Step 4: Restart the Server
```bash
# Docker Compose
docker compose restart unraid-mcp
# Direct / uv
uv run unraid-mcp-server
```
On startup you should see:
```
INFO [SERVER] Google OAuth enabled — base_url=http://10.1.0.2:6970, redirect_uri=http://10.1.0.2:6970/auth/callback
```
---
## How Authentication Works
1. An MCP client connects to `http://<server>:6970/mcp`
2. The server responds with a `401 Unauthorized` and an OAuth authorization URL
3. The client opens the URL in a browser; the user logs in with Google
4. Google redirects to `<UNRAID_MCP_BASE_URL>/auth/callback` with an authorization code
5. FastMCP exchanges the code for tokens, issues a signed JWT, and returns it to the client
6. The client includes the JWT in subsequent requests — the server validates it without hitting Google again
7. Tokens persist to `~/.local/share/fastmcp/oauth-proxy/` — sessions survive server restarts
---
## Environment Variable Reference
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `GOOGLE_CLIENT_ID` | For OAuth | `""` | OAuth 2.0 Client ID from Google Cloud Console |
| `GOOGLE_CLIENT_SECRET` | For OAuth | `""` | OAuth 2.0 Client Secret from Google Cloud Console |
| `UNRAID_MCP_BASE_URL` | For OAuth | `""` | Public base URL of this server — must match the authorized redirect URI |
| `UNRAID_MCP_JWT_SIGNING_KEY` | Recommended | auto-derived | Stable 32+ char secret for JWT signing — prevents token invalidation on restart |
OAuth is activated only when **all three** of `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `UNRAID_MCP_BASE_URL` are non-empty. Omit any one to run without authentication.
---
## Disabling OAuth
Remove (or empty) `GOOGLE_CLIENT_ID` from `~/.unraid-mcp/.env` and restart. The server reverts to unauthenticated mode and logs:
```
WARNING [SERVER] No authentication configured — MCP server is open to all clients on the network.
```
---
## Troubleshooting
**`redirect_uri_mismatch` from Google**
The redirect URI in Google Cloud Console must exactly match `<UNRAID_MCP_BASE_URL>/auth/callback` — same scheme, host, port, and path. Trailing slashes matter.
**Tokens invalidated after restart**
Set `UNRAID_MCP_JWT_SIGNING_KEY` to a stable secret (see Step 3). Without it, FastMCP generates a new key on every start.
**`stdio` transport warning**
OAuth requires an HTTP transport. Set `UNRAID_MCP_TRANSPORT=streamable-http` (the default) or `sse`.
**Client cannot reach the callback URL**
`UNRAID_MCP_BASE_URL` must be the address your browser uses to reach the server — not `localhost` or `0.0.0.0`. Use the LAN IP, Tailscale IP, or a domain name.
**OAuth configured but server not starting**
Check `logs/unraid-mcp.log` or `docker compose logs unraid-mcp` for startup errors.
---
## Security Notes
- OAuth protects the MCP HTTP interface — the Unraid GraphQL API itself still uses `UNRAID_API_KEY`
- OAuth state files at `~/.local/share/fastmcp/oauth-proxy/` should be on a private filesystem; do not expose them
- Restrict Google OAuth to specific accounts via the Google Cloud Console **OAuth consent screen** → **Test users** if you don't want to publish the app
- `UNRAID_MCP_JWT_SIGNING_KEY` is a credential — store it in `~/.unraid-mcp/.env` (mode 600), never in source control