Files
unraid-mcp/docs/AUTHENTICATION.md
Jacob Magar 2b777be927 fix(security): path traversal, timing-safe auth, stale credential bindings
Security:
- Remove /mnt/ from _ALLOWED_LOG_PREFIXES to prevent Unraid share exposure
- Add early .. detection for disk/logs and live/log_tail path validation
- Add /boot/ prefix restriction for flash_backup source_path
- Use hmac.compare_digest for timing-safe API key verification in server.py
- Gate include_traceback on DEBUG log level (no tracebacks in production)

Correctness:
- Re-raise CredentialsNotConfiguredError in health check instead of swallowing
- Fix ups_device query (remove non-existent nominalPower/currentPower fields)

Best practices (BP-01, BP-05, BP-06):
- Add # noqa: ASYNC109 to timeout params in _handle_live and unraid()
- Fix start_array* → start_array in docstring (not in ARRAY_DESTRUCTIVE)
- Remove from __future__ import annotations from snapshot.py
- Replace import-time UNRAID_API_KEY/URL bindings with _settings.ATTR pattern
  in manager.py, snapshot.py, utils.py, diagnostics.py — fixes stale binding
  after apply_runtime_config() post-elicitation (BP-05)

CI/CD:
- Add .github/workflows/ci.yml (5-job pipeline: lint, typecheck, test, version-sync, audit)
- Add fail_under = 80 to [tool.coverage.report]
- Add version sync check to scripts/validate-marketplace.sh

Documentation:
- Sync plugin.json version 1.1.1 → 1.1.2 with pyproject.toml
- Update CLAUDE.md: 3 tools, system domain count 18, scripts comment fix
- Update README.md: 3 tools, security notes
- Update docs/AUTHENTICATION.md: H1 title fix
- Add UNRAID_CREDENTIALS_DIR to .env.example

Bump: 1.1.1 → 1.1.2

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-23 11:37:05 -04:00

7.2 KiB

Authentication Setup Guide

This document covers both Google OAuth 2.0 and API key bearer token authentication for the Unraid MCP HTTP server. It explains how to protect the server using FastMCP's built-in GoogleProvider for OAuth, or a static bearer token for headless/machine access.


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
  • 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 ConsoleAPIs & ServicesCredentials
  2. Click Create CredentialsOAuth 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):

# 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):

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:

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

# 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.


API Key Authentication (Alternative / Combined)

For machine-to-machine access (scripts, CI, other agents) without a browser-based OAuth flow, set UNRAID_MCP_API_KEY:

# In ~/.unraid-mcp/.env
UNRAID_MCP_API_KEY=your-secret-token

Clients present it as a standard bearer token:

Authorization: Bearer your-secret-token

Combining with Google OAuth: set both GOOGLE_CLIENT_ID and UNRAID_MCP_API_KEY. The server activates MultiAuth and accepts either method — Google OAuth for interactive clients, API key for headless clients.

Reusing the Unraid API key: you can set UNRAID_MCP_API_KEY to the same value as UNRAID_API_KEY for simplicity. The two vars are kept separate so each concern has its own name.

Standalone API key (no Google OAuth): set only UNRAID_MCP_API_KEY. The server validates bearer tokens directly with no OAuth redirect flow.


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 screenTest 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