docs: address remaining Login Flow v2 review feedback

Round-2 cleanup of PR #743 review comments not covered by d153e96:

- configuration.md: fix broken `#multi-user-oauth-modes` anchor; replace
  with the two real anchors (Multi-User BasicAuth, Login Flow v2). Rewrite
  the stale "always use OAuth2/OIDC with pre-configured clients" Best
  Practices section to reflect the post-pivot mode matrix, and update the
  Docker volume example to mount the encrypted app-password store
  (`TOKEN_STORAGE_DB`) rather than obsolete `.oauth` client storage.
- semantic-search-architecture.md: rename remaining body references from
  the deprecated `VECTOR_SYNC_ENABLED` to `ENABLE_SEMANTIC_SEARCH` so the
  doc matches configuration.md / troubleshooting.md.
- running.md: relabel "OAuth Mode (Recommended)" as
  "Login Flow v2 / OAuth issuer mode (--oauth)", drop the misleading
  "(Legacy)" suffix from BasicAuth, drop the
  `NEXTCLOUD_OIDC_CLIENT_ID/SECRET` example (tied to the retired
  direct-OAuth-to-Nextcloud flow), and add a note explaining what
  `--oauth` actually enables post-pivot.
- keycloak-multi-client-validation.md, oauth-impersonation-findings.md:
  add a deprecation banner pointing at ADR-022 / Login Flow v2. Files
  retained because ADR-002 and CLAUDE.md still cite them.
- auth-flows.md: clarify under the Astrolabe → MCP diagram that the
  Nextcloud-OIDC JWKS path applies to Multi-User BasicAuth; under
  Login Flow v2 the MCP server validates tokens against its own JWKS.
- login-flow-v2.md: clarify the sticky-session note — affinity must key
  on the OAuth bearer token (or user-bound cookie), not source IP, since
  MCP clients may not maintain stable IPs across the provisioning flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-04-30 01:57:16 +02:00
co-authored by Claude Opus 4.7
parent 074d20998c
commit 35c115ead6
7 changed files with 47 additions and 32 deletions
+2
View File
@@ -126,6 +126,8 @@ Astrolabe MCP Server Nextcloud OIDC
- Tokens are validated by the MCP server using Nextcloud OIDC JWKS
- Authorization check: `token.sub == requested_resource_owner`
> **Note:** The diagram and JWKS source above apply to **Multi-User BasicAuth**, where Nextcloud is the IdP. Under [Login Flow v2](#3-login-flow-v2) the MCP server is its own OAuth issuer and validates Bearer tokens against its **own** JWKS (or via local introspection of opaque tokens) — Nextcloud's JWKS is not involved on the MCP-client → MCP-server leg. See the Login Flow v2 section below.
---
### 3. Login Flow v2
+16 -9
View File
@@ -24,7 +24,8 @@ cp env.sample .env # Full reference with all options
Then choose your deployment mode:
- [Single-User BasicAuth](#single-user-basicauth-mode) - Simplest for personal instances
- [Multi-User OAuth](#multi-user-oauth-modes) - Recommended for production
- [Multi-User BasicAuth](#multi-user-basicauth-mode) - Internal deployments with credential pass-through
- [Login Flow v2](#login-flow-v2-mode) - Recommended for hosted / OAuth-based MCP clients
- [Deployment Mode Selection](#deployment-mode-selection) - Explicit mode declaration
---
@@ -627,27 +628,33 @@ uv run nextcloud-mcp-server --no-oauth \
### For Development
- Use BasicAuth for quick setup and testing
- Or use OAuth with auto-registration (dynamic client registration)
- Use Single-User BasicAuth for the fastest local setup (one user, one app password)
- Store `.env` file in your project directory
- Add `.env` to `.gitignore`
### For Production
- **Always use OAuth2/OIDC** with pre-configured clients
- Store OAuth client credentials securely
Pick the mode that matches your deployment topology — there is no single "always" answer:
- **Multi-user / hosted** — use [Login Flow v2](login-flow-v2.md). MCP clients authenticate via OAuth 2.1 + DCR (no pre-configured client to manage); per-user Nextcloud access is stored as encrypted app passwords.
- **Internal multi-user** — Multi-User BasicAuth pass-through (clients send `Authorization: Basic` headers) is fully supported when users manage their own Nextcloud credentials.
- **Personal / self-hosted** — Single-User BasicAuth with a Nextcloud app password is the simplest production setup.
In all modes:
- Use environment variables from your deployment platform (Docker secrets, Kubernetes ConfigMaps, etc.)
- Never commit credentials to version control
- SQLite database permissions are handled automatically by the server
### For Docker
- Mount OAuth client storage as a volume for persistence:
- Under Login Flow v2, mount the encrypted app-password store as a volume so per-user provisioning survives container restarts:
```bash
docker run -v $(pwd)/.oauth:/app/.oauth --env-file .env \
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest
docker run -v $(pwd)/data:/app/data --env-file .env \
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest --oauth
```
- Use Docker secrets for sensitive values in production
(`TOKEN_STORAGE_DB=/app/data/tokens.db` in `.env`.)
- Use Docker secrets for sensitive values in production (`TOKEN_ENCRYPTION_KEY`, `NEXTCLOUD_PASSWORD`, etc.)
---
+3
View File
@@ -1,5 +1,8 @@
# Keycloak Multi-Client Token Validation
> [!WARNING]
> **Deprecated — historical reference only.** This document describes the direct-OAuth-to-Nextcloud architecture that was retired in favor of Login Flow v2. See [ADR-022](ADR-022-deployment-mode-consolidation.md) and [Login Flow v2](login-flow-v2.md) for the current approach. Retained because [ADR-002](ADR-002-vector-sync-authentication.md) and `CLAUDE.md` still cite this investigation for context.
## Executive Summary
**Question**: Can Nextcloud's `user_oidc` app (configured with client A) validate bearer tokens from client B in the same Keycloak realm?
+2
View File
@@ -242,6 +242,8 @@ The user revoked it from **Settings → Security → Devices & Sessions**. Delet
The provisioning session store is in-memory; `ENABLE_LOGIN_FLOW=true` assumes a single worker. Running with `uvicorn --workers N` will cause provisioning sessions to randomly fail. For higher concurrency, scale horizontally (multiple containers behind a sticky-session load balancer) rather than within a single process.
> **Sticky-session keying:** route on the user's OAuth bearer token (or a session cookie tied to the user) — **not** on source IP. MCP clients may not maintain stable IPs between the request that initiates provisioning and the polling request that completes it, so IP-based affinity will silently break the flow. A stable per-user identifier from the `Authorization` header is the right key.
## See Also
- [ADR-022: Deployment Mode Consolidation](ADR-022-deployment-mode-consolidation.md) — design rationale
+3
View File
@@ -1,5 +1,8 @@
# OAuth Impersonation Investigation Findings
> [!WARNING]
> **Deprecated — historical reference only.** This document describes the direct-OAuth-to-Nextcloud / token-exchange architecture that was retired in favor of Login Flow v2. See [ADR-022](ADR-022-deployment-mode-consolidation.md) and [Login Flow v2](login-flow-v2.md) for the current approach. Retained because [ADR-002](ADR-002-vector-sync-authentication.md) still cites this investigation for context.
**Date**: 2025-11-02
**Last Updated**: 2025-11-02 (Token Exchange Resolution)
**Status**: Implementation Complete - Token Exchange Working
+11 -13
View File
@@ -17,15 +17,17 @@ Before running the server:
Start the server using Docker:
```bash
# OAuth mode (recommended)
# Login Flow v2 / OAuth issuer mode (--oauth, recommended for multi-user)
docker run -p 127.0.0.1:8000:8000 --env-file .env --rm \
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest --oauth
# BasicAuth mode
# BasicAuth mode (single-user or multi-user pass-through)
docker run -p 127.0.0.1:8000:8000 --env-file .env --rm \
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest
```
> **Note:** The `--oauth` flag turns on the OAuth-issuer layer used by [Login Flow v2](login-flow-v2.md), the recommended multi-user mode. It does **not** forward client OAuth tokens to Nextcloud — Nextcloud is always reached via per-user app passwords (Login Flow v2) or Basic Auth credentials.
The server will start on `http://127.0.0.1:8000` by default.
---
@@ -34,30 +36,26 @@ The server will start on `http://127.0.0.1:8000` by default.
### Basic Docker Run
#### OAuth Mode (Recommended)
#### Login Flow v2 / OAuth issuer mode (`--oauth`)
Recommended for multi-user deployments. The MCP server acts as the OAuth issuer for MCP clients; per-user Nextcloud access is obtained via [Login Flow v2](login-flow-v2.md) and stored as encrypted app passwords.
```bash
# OAuth with auto-registration
# OAuth issuer with DCR (Dynamic Client Registration)
docker run -p 127.0.0.1:8000:8000 --env-file .env --rm \
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest --oauth
# OAuth with custom port
# OAuth issuer on a custom port
docker run -p 127.0.0.1:8080:8000 --env-file .env --rm \
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest --oauth
# OAuth with pre-configured client
docker run -p 127.0.0.1:8000:8000 --env-file .env --rm \
-e NEXTCLOUD_OIDC_CLIENT_ID=abc123 \
-e NEXTCLOUD_OIDC_CLIENT_SECRET=xyz789 \
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest --oauth
# OAuth with specific apps only
# OAuth issuer with specific apps only
docker run -p 127.0.0.1:8000:8000 --env-file .env --rm \
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest --oauth \
--enable-app notes --enable-app calendar
```
#### BasicAuth Mode (Legacy)
#### BasicAuth Mode
```bash
# BasicAuth (requires NEXTCLOUD_USERNAME/PASSWORD in .env)
+10 -10
View File
@@ -86,7 +86,7 @@ graph TB
## How It Works: Background Synchronization
Background synchronization runs automatically when `VECTOR_SYNC_ENABLED=true`, discovering changes and indexing documents without user intervention.
Background synchronization runs automatically when `ENABLE_SEMANTIC_SEARCH=true`, discovering changes and indexing documents without user intervention.
```mermaid
sequenceDiagram
@@ -478,7 +478,7 @@ except Exception as e:
**BasicAuth:**
- Set `NEXTCLOUD_USERNAME` and `NEXTCLOUD_PASSWORD`
- Background sync works immediately when `VECTOR_SYNC_ENABLED=true`
- Background sync works immediately when `ENABLE_SEMANTIC_SEARCH=true`
- Credentials stored in `.env` file (secure server access required)
**OAuth:**
@@ -497,7 +497,7 @@ except Exception as e:
**In-Memory Mode:**
```bash
VECTOR_SYNC_ENABLED=true
ENABLE_SEMANTIC_SEARCH=true
# QDRANT_LOCATION not set → defaults to :memory:
```
- Fastest startup
@@ -506,7 +506,7 @@ VECTOR_SYNC_ENABLED=true
**Persistent Local Mode:**
```bash
VECTOR_SYNC_ENABLED=true
ENABLE_SEMANTIC_SEARCH=true
QDRANT_LOCATION=/var/lib/qdrant
```
- Vectors survive restarts
@@ -515,7 +515,7 @@ QDRANT_LOCATION=/var/lib/qdrant
**Network Mode (Recommended for Production):**
```bash
VECTOR_SYNC_ENABLED=true
ENABLE_SEMANTIC_SEARCH=true
QDRANT_URL=http://qdrant:6333
QDRANT_API_KEY=secret # optional
```
@@ -563,12 +563,12 @@ OLLAMA_EMBEDDING_MODEL=nomic-embed-text # 768-dimensional vectors
**Enable Semantic Search:**
```bash
VECTOR_SYNC_ENABLED=true # Default: false (opt-in)
ENABLE_SEMANTIC_SEARCH=true # Default: false (opt-in)
```
**Qdrant Vector Database:**
```bash
# In-memory mode (default if VECTOR_SYNC_ENABLED=true)
# In-memory mode (default if ENABLE_SEMANTIC_SEARCH=true)
# QDRANT_LOCATION not set → uses :memory:
# Persistent local mode
@@ -617,7 +617,7 @@ VECTOR_SYNC_INTERVAL=3600 # Scan interval in seconds (default: 1 hour)
## Operational Behavior
### What Happens When VECTOR_SYNC_ENABLED=true
### What Happens When ENABLE_SEMANTIC_SEARCH=true
**Immediate (Server Startup):**
1. MCP server connects to Qdrant (creates collection if needed)
@@ -752,7 +752,7 @@ For detailed observability setup, see [docs/observability.md](observability.md).
**Diagnosis Flow:**
1. Check sync status: `nc_get_vector_sync_status`
- `sync_enabled: false` → Enable with `VECTOR_SYNC_ENABLED=true`
- `sync_enabled: false` → Enable with `ENABLE_SEMANTIC_SEARCH=true`
- `status: error` → Check scanner logs for failures
2. Check queue size:
- `pending_documents > 0` → Processing in progress, wait
@@ -765,7 +765,7 @@ For detailed observability setup, see [docs/observability.md](observability.md).
- Model not found → Pull model: `ollama pull nomic-embed-text`
**Common Causes:**
- Sync disabled (default): Enable `VECTOR_SYNC_ENABLED=true`
- Sync disabled (default): Enable `ENABLE_SEMANTIC_SEARCH=true`
- Ollama not running: Start Ollama service
- Qdrant not accessible: Check network/URL
- First scan in progress: Wait up to 1 hour + processing time