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:
co-authored by
Claude Opus 4.7
parent
074d20998c
commit
35c115ead6
@@ -126,6 +126,8 @@ Astrolabe MCP Server Nextcloud OIDC
|
|||||||
- Tokens are validated by the MCP server using Nextcloud OIDC JWKS
|
- Tokens are validated by the MCP server using Nextcloud OIDC JWKS
|
||||||
- Authorization check: `token.sub == requested_resource_owner`
|
- 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
|
### 3. Login Flow v2
|
||||||
|
|||||||
+16
-9
@@ -24,7 +24,8 @@ cp env.sample .env # Full reference with all options
|
|||||||
Then choose your deployment mode:
|
Then choose your deployment mode:
|
||||||
|
|
||||||
- [Single-User BasicAuth](#single-user-basicauth-mode) - Simplest for personal instances
|
- [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
|
- [Deployment Mode Selection](#deployment-mode-selection) - Explicit mode declaration
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -627,27 +628,33 @@ uv run nextcloud-mcp-server --no-oauth \
|
|||||||
|
|
||||||
### For Development
|
### For Development
|
||||||
|
|
||||||
- Use BasicAuth for quick setup and testing
|
- Use Single-User BasicAuth for the fastest local setup (one user, one app password)
|
||||||
- Or use OAuth with auto-registration (dynamic client registration)
|
|
||||||
- Store `.env` file in your project directory
|
- Store `.env` file in your project directory
|
||||||
- Add `.env` to `.gitignore`
|
- Add `.env` to `.gitignore`
|
||||||
|
|
||||||
### For Production
|
### For Production
|
||||||
|
|
||||||
- **Always use OAuth2/OIDC** with pre-configured clients
|
Pick the mode that matches your deployment topology — there is no single "always" answer:
|
||||||
- Store OAuth client credentials securely
|
|
||||||
|
- **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.)
|
- Use environment variables from your deployment platform (Docker secrets, Kubernetes ConfigMaps, etc.)
|
||||||
- Never commit credentials to version control
|
- Never commit credentials to version control
|
||||||
- SQLite database permissions are handled automatically by the server
|
- SQLite database permissions are handled automatically by the server
|
||||||
|
|
||||||
### For Docker
|
### 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
|
```bash
|
||||||
docker run -v $(pwd)/.oauth:/app/.oauth --env-file .env \
|
docker run -v $(pwd)/data:/app/data --env-file .env \
|
||||||
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest
|
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.)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
# Keycloak Multi-Client Token Validation
|
# 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
|
## Executive Summary
|
||||||
|
|
||||||
**Question**: Can Nextcloud's `user_oidc` app (configured with client A) validate bearer tokens from client B in the same Keycloak realm?
|
**Question**: Can Nextcloud's `user_oidc` app (configured with client A) validate bearer tokens from client B in the same Keycloak realm?
|
||||||
|
|||||||
@@ -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.
|
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
|
## See Also
|
||||||
|
|
||||||
- [ADR-022: Deployment Mode Consolidation](ADR-022-deployment-mode-consolidation.md) — design rationale
|
- [ADR-022: Deployment Mode Consolidation](ADR-022-deployment-mode-consolidation.md) — design rationale
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
# OAuth Impersonation Investigation Findings
|
# 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
|
**Date**: 2025-11-02
|
||||||
**Last Updated**: 2025-11-02 (Token Exchange Resolution)
|
**Last Updated**: 2025-11-02 (Token Exchange Resolution)
|
||||||
**Status**: Implementation Complete - Token Exchange Working
|
**Status**: Implementation Complete - Token Exchange Working
|
||||||
|
|||||||
+11
-13
@@ -17,15 +17,17 @@ Before running the server:
|
|||||||
Start the server using Docker:
|
Start the server using Docker:
|
||||||
|
|
||||||
```bash
|
```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 \
|
docker run -p 127.0.0.1:8000:8000 --env-file .env --rm \
|
||||||
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest --oauth
|
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 \
|
docker run -p 127.0.0.1:8000:8000 --env-file .env --rm \
|
||||||
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest
|
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.
|
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
|
### 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
|
```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 \
|
docker run -p 127.0.0.1:8000:8000 --env-file .env --rm \
|
||||||
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest --oauth
|
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 \
|
docker run -p 127.0.0.1:8080:8000 --env-file .env --rm \
|
||||||
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest --oauth
|
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest --oauth
|
||||||
|
|
||||||
# OAuth with pre-configured client
|
# OAuth issuer with specific apps only
|
||||||
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
|
|
||||||
docker run -p 127.0.0.1:8000:8000 --env-file .env --rm \
|
docker run -p 127.0.0.1:8000:8000 --env-file .env --rm \
|
||||||
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest --oauth \
|
ghcr.io/cbcoutinho/nextcloud-mcp-server:latest --oauth \
|
||||||
--enable-app notes --enable-app calendar
|
--enable-app notes --enable-app calendar
|
||||||
```
|
```
|
||||||
|
|
||||||
#### BasicAuth Mode (Legacy)
|
#### BasicAuth Mode
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# BasicAuth (requires NEXTCLOUD_USERNAME/PASSWORD in .env)
|
# BasicAuth (requires NEXTCLOUD_USERNAME/PASSWORD in .env)
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ graph TB
|
|||||||
|
|
||||||
## How It Works: Background Synchronization
|
## 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
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
@@ -478,7 +478,7 @@ except Exception as e:
|
|||||||
|
|
||||||
**BasicAuth:**
|
**BasicAuth:**
|
||||||
- Set `NEXTCLOUD_USERNAME` and `NEXTCLOUD_PASSWORD`
|
- 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)
|
- Credentials stored in `.env` file (secure server access required)
|
||||||
|
|
||||||
**OAuth:**
|
**OAuth:**
|
||||||
@@ -497,7 +497,7 @@ except Exception as e:
|
|||||||
|
|
||||||
**In-Memory Mode:**
|
**In-Memory Mode:**
|
||||||
```bash
|
```bash
|
||||||
VECTOR_SYNC_ENABLED=true
|
ENABLE_SEMANTIC_SEARCH=true
|
||||||
# QDRANT_LOCATION not set → defaults to :memory:
|
# QDRANT_LOCATION not set → defaults to :memory:
|
||||||
```
|
```
|
||||||
- Fastest startup
|
- Fastest startup
|
||||||
@@ -506,7 +506,7 @@ VECTOR_SYNC_ENABLED=true
|
|||||||
|
|
||||||
**Persistent Local Mode:**
|
**Persistent Local Mode:**
|
||||||
```bash
|
```bash
|
||||||
VECTOR_SYNC_ENABLED=true
|
ENABLE_SEMANTIC_SEARCH=true
|
||||||
QDRANT_LOCATION=/var/lib/qdrant
|
QDRANT_LOCATION=/var/lib/qdrant
|
||||||
```
|
```
|
||||||
- Vectors survive restarts
|
- Vectors survive restarts
|
||||||
@@ -515,7 +515,7 @@ QDRANT_LOCATION=/var/lib/qdrant
|
|||||||
|
|
||||||
**Network Mode (Recommended for Production):**
|
**Network Mode (Recommended for Production):**
|
||||||
```bash
|
```bash
|
||||||
VECTOR_SYNC_ENABLED=true
|
ENABLE_SEMANTIC_SEARCH=true
|
||||||
QDRANT_URL=http://qdrant:6333
|
QDRANT_URL=http://qdrant:6333
|
||||||
QDRANT_API_KEY=secret # optional
|
QDRANT_API_KEY=secret # optional
|
||||||
```
|
```
|
||||||
@@ -563,12 +563,12 @@ OLLAMA_EMBEDDING_MODEL=nomic-embed-text # 768-dimensional vectors
|
|||||||
|
|
||||||
**Enable Semantic Search:**
|
**Enable Semantic Search:**
|
||||||
```bash
|
```bash
|
||||||
VECTOR_SYNC_ENABLED=true # Default: false (opt-in)
|
ENABLE_SEMANTIC_SEARCH=true # Default: false (opt-in)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Qdrant Vector Database:**
|
**Qdrant Vector Database:**
|
||||||
```bash
|
```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:
|
# QDRANT_LOCATION not set → uses :memory:
|
||||||
|
|
||||||
# Persistent local mode
|
# Persistent local mode
|
||||||
@@ -617,7 +617,7 @@ VECTOR_SYNC_INTERVAL=3600 # Scan interval in seconds (default: 1 hour)
|
|||||||
|
|
||||||
## Operational Behavior
|
## Operational Behavior
|
||||||
|
|
||||||
### What Happens When VECTOR_SYNC_ENABLED=true
|
### What Happens When ENABLE_SEMANTIC_SEARCH=true
|
||||||
|
|
||||||
**Immediate (Server Startup):**
|
**Immediate (Server Startup):**
|
||||||
1. MCP server connects to Qdrant (creates collection if needed)
|
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:**
|
**Diagnosis Flow:**
|
||||||
1. Check sync status: `nc_get_vector_sync_status`
|
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
|
- `status: error` → Check scanner logs for failures
|
||||||
2. Check queue size:
|
2. Check queue size:
|
||||||
- `pending_documents > 0` → Processing in progress, wait
|
- `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`
|
- Model not found → Pull model: `ollama pull nomic-embed-text`
|
||||||
|
|
||||||
**Common Causes:**
|
**Common Causes:**
|
||||||
- Sync disabled (default): Enable `VECTOR_SYNC_ENABLED=true`
|
- Sync disabled (default): Enable `ENABLE_SEMANTIC_SEARCH=true`
|
||||||
- Ollama not running: Start Ollama service
|
- Ollama not running: Start Ollama service
|
||||||
- Qdrant not accessible: Check network/URL
|
- Qdrant not accessible: Check network/URL
|
||||||
- First scan in progress: Wait up to 1 hour + processing time
|
- First scan in progress: Wait up to 1 hour + processing time
|
||||||
|
|||||||
Reference in New Issue
Block a user