131 lines
5.0 KiB
YAML
131 lines
5.0 KiB
YAML
# Authelia configuration for the BookStack MCP server.
|
|
#
|
|
# Merge these blocks into your existing configuration.yml. Tested against
|
|
# Authelia 4.39+, where ID Token claims changed (see the claims_policies note).
|
|
#
|
|
# Generate the client id and secret first:
|
|
#
|
|
# docker run --rm authelia/authelia:latest \
|
|
# authelia crypto rand --length 72 --charset rfc3986
|
|
#
|
|
# docker run --rm authelia/authelia:latest \
|
|
# authelia crypto hash generate pbkdf2 --variant sha512 \
|
|
# --random --random.length 72 --random.charset rfc3986
|
|
#
|
|
# The second command prints BOTH a "Random Password" and a "Digest".
|
|
# Digest -> client_secret below
|
|
# Random Password -> OIDC_CLIENT_SECRET in /etc/bookstack-mcp/env
|
|
# Do not put the digest in the MCP server's env file.
|
|
|
|
identity_providers:
|
|
oidc:
|
|
# -----------------------------------------------------------------------
|
|
# 1. Claims policy
|
|
#
|
|
# Authelia 4.39 stopped putting non-standard claims in the ID Token by
|
|
# default. The MCP server reads identity from the ID Token (because
|
|
# Authelia issues opaque access tokens), so groups and preferred_username
|
|
# have to be put back explicitly. Without this, MCP_REQUIRED_GROUPS will
|
|
# never match and every tool call gets denied.
|
|
# -----------------------------------------------------------------------
|
|
claims_policies:
|
|
mcp_claims:
|
|
id_token:
|
|
- 'groups'
|
|
- 'preferred_username'
|
|
- 'email'
|
|
- 'email_verified'
|
|
- 'name'
|
|
|
|
# -----------------------------------------------------------------------
|
|
# 2. Lifespan
|
|
#
|
|
# Authelia's default refresh_token lifespan is 90m. A claude.ai connector
|
|
# that sits idle longer than that has to be re-authorised by hand, which
|
|
# gets old fast. A long refresh token with a short access token is the
|
|
# right shape for a background integration.
|
|
# -----------------------------------------------------------------------
|
|
lifespans:
|
|
custom:
|
|
mcp:
|
|
access_token: '1h'
|
|
id_token: '1h'
|
|
refresh_token: '30d'
|
|
|
|
clients:
|
|
- client_id: 'REPLACE_WITH_GENERATED_ID'
|
|
client_name: 'BookStack MCP'
|
|
client_secret: 'REPLACE_WITH_PBKDF2_DIGEST'
|
|
public: false
|
|
|
|
# one_factor if you don't want to be prompted for 2FA on reconnect.
|
|
authorization_policy: 'two_factor'
|
|
|
|
claims_policy: 'mcp_claims'
|
|
lifespan: 'mcp'
|
|
|
|
# Claude re-authorises on token refresh. Explicit consent every time is
|
|
# painful; pre-configured remembers the grant for the given duration.
|
|
consent_mode: 'pre-configured'
|
|
pre_configured_consent_duration: '1 month'
|
|
|
|
require_pkce: true
|
|
pkce_challenge_method: 'S256'
|
|
|
|
redirect_uris:
|
|
- 'https://mcp.example.com/auth/callback'
|
|
|
|
# RFC 8707 resource indicators. The MCP server sends a 'resource'
|
|
# parameter identifying itself; Authelia validates it against this
|
|
# whitelist and returns 'invalid_target' if it isn't listed.
|
|
# Alternative: set OIDC_FORWARD_RESOURCE=false on the MCP server to
|
|
# stop sending it at all.
|
|
audience:
|
|
- 'https://mcp.example.com'
|
|
- 'https://mcp.example.com/mcp'
|
|
requested_audience_mode: 'implicit'
|
|
|
|
# offline_access is what gets you a refresh token. Without it the
|
|
# connector dies when the access token expires.
|
|
scopes:
|
|
- 'openid'
|
|
- 'profile'
|
|
- 'email'
|
|
- 'groups'
|
|
- 'offline_access'
|
|
|
|
grant_types:
|
|
- 'authorization_code'
|
|
- 'refresh_token'
|
|
|
|
response_types:
|
|
- 'code'
|
|
|
|
# Authelia's default and what the spec requires. Leave it alone unless
|
|
# you see 'invalid_client' at the token endpoint, in which case try
|
|
# 'client_secret_post'.
|
|
token_endpoint_auth_method: 'client_secret_basic'
|
|
|
|
# Both are already the default; stated explicitly because they matter.
|
|
# 'none' means access tokens stay opaque rather than being JWTs, which
|
|
# is exactly why the MCP server needs OIDC_VERIFY_ID_TOKEN=true.
|
|
access_token_signed_response_alg: 'none'
|
|
userinfo_signed_response_alg: 'none'
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. Access control bypass <-- the one everybody misses
|
|
#
|
|
# If mcp.example.com is behind Authelia's forward-auth on your reverse proxy,
|
|
# Claude will be served an HTML login page instead of the MCP endpoint and the
|
|
# connector will fail with a parse error or a redirect loop. Claude cannot do
|
|
# cookie-based forward auth; the MCP server runs its own OAuth flow against
|
|
# Authelia instead. So the host must bypass forward-auth entirely.
|
|
#
|
|
# This is not a security hole: the MCP server refuses every unauthenticated
|
|
# request itself, and Authelia is still the thing deciding who gets a token.
|
|
# ---------------------------------------------------------------------------
|
|
access_control:
|
|
rules:
|
|
- domain: 'mcp.example.com'
|
|
policy: 'bypass'
|