From 0b8afec494e0f4744a801b15786c334ef5b19308 Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Sun, 1 Mar 2026 18:25:23 +0100 Subject: [PATCH] feat(helm): add login-flow auth mode to Helm chart (ADR-022) Add Login Flow v2 as a fourth auth mode alongside basic, multi-user-basic, and oauth. This enables multi-user deployments using Nextcloud's native Login Flow v2 without requiring OAuth patches to user_oidc. - Add loginFlow section to values.yaml with token encryption config - Add login-flow env vars, args, volume mounts to deployment.yaml - Add login-flow secret and oauth-storage PVC templates - Add loginFlowSecretName helper, update dataStorageEnabled - Add multi-user-basic and login-flow sections to NOTES.txt - Add version footer and ArtifactHub changelog annotations - Update README with 4 auth modes and docker-compose profiles Co-Authored-By: Claude Opus 4.6 --- README.md | 33 ++++++++++++++----- charts/nextcloud-mcp-server/Chart.yaml | 9 +++++ .../nextcloud-mcp-server/templates/NOTES.txt | 28 ++++++++++++++++ .../templates/_helpers.tpl | 13 ++++++++ .../templates/deployment.yaml | 23 +++++++++++-- .../nextcloud-mcp-server/templates/pvc.yaml | 15 +++++++++ .../templates/secret.yaml | 14 ++++++++ charts/nextcloud-mcp-server/values.yaml | 21 ++++++++++-- 8 files changed, 143 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 5edcf3b..1d4a5a7 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,15 @@ http://127.0.0.1:8000/sse http://127.0.0.1:8000/mcp ``` +**Docker Compose Profiles** (for development/testing): + +```bash +docker compose --profile single-user up -d # Port 8000 +docker compose --profile multi-user-basic up -d # Port 8003 +docker compose --profile oauth up -d # Port 8001 +docker compose --profile login-flow up -d # Port 8004 +``` + **Next Steps:** - Connect your MCP client (Claude Desktop, IDEs, `mcp dev`, etc.) - See [docs/installation.md](docs/installation.md) for other deployment options (local, Kubernetes) @@ -99,25 +108,33 @@ Want to see another Nextcloud app supported? [Open an issue](https://github.com/ ### Authentication Modes -The server supports three authentication modes: +The server supports four authentication modes: -**Single-User Mode (BasicAuth):** +**Single-User (BasicAuth):** - One set of credentials shared by all MCP clients - Simple setup: username + app password in environment variables - All clients access Nextcloud as the same user - Best for: Personal use, development, single-user deployments -**Multi-User Mode (OAuth):** +**Multi-User (BasicAuth Pass-Through):** +- MCP clients send credentials via Authorization header +- Server passes through to Nextcloud (stateless by default) +- Optional offline access for background operations (`ENABLE_MULTI_USER_BASIC_AUTH=true`) +- Best for: Multi-user setups without OAuth infrastructure + +**Multi-User (OAuth):** - Each MCP client authenticates separately with their own Nextcloud account - Per-user scopes and permissions (clients only see tools they're authorized for) - More secure: tokens expire, credentials never shared with server - Best for: Teams, multi-user deployments, production environments with multiple users +- Requires: Patches to the `user_oidc` app (experimental) -**Hybrid Mode (Multi-User BasicAuth + OAuth):** -- MCP clients use BasicAuth (simple, stateless) -- Admin operations use OAuth (webhooks, background sync) -- Best for: Nextcloud deployments with admin-managed webhooks and semantic search -- Requires: `ENABLE_MULTI_USER_BASIC_AUTH=true` + `ENABLE_OFFLINE_ACCESS=true` +**Multi-User (Login Flow v2):** +- Uses Nextcloud's native Login Flow v2 to obtain per-user app passwords +- No OAuth patches required — works with stock Nextcloud +- Each user authenticates via browser, server manages app passwords +- Best for: Multi-user deployments without OAuth infrastructure (`ENABLE_LOGIN_FLOW=true`) +- Experimental: See [ADR-022](docs/ADR-022-deployment-mode-consolidation.md) for details See [docs/authentication.md](docs/authentication.md) for detailed setup instructions. diff --git a/charts/nextcloud-mcp-server/Chart.yaml b/charts/nextcloud-mcp-server/Chart.yaml index a1cdfd2..fbde470 100644 --- a/charts/nextcloud-mcp-server/Chart.yaml +++ b/charts/nextcloud-mcp-server/Chart.yaml @@ -25,6 +25,15 @@ annotations: # Grafana dashboard support grafana_dashboard: "true" grafana_dashboard_folder: "Nextcloud MCP" + artifacthub.io/changes: | + - kind: added + description: Login Flow v2 auth mode for Helm chart (ADR-022) + - kind: added + description: Multi-user BasicAuth guidance in post-install NOTES + - kind: added + description: Version and changelog info in post-install NOTES + - kind: changed + description: Updated appVersion to 0.64.4 dependencies: - name: qdrant version: "1.17.0" diff --git a/charts/nextcloud-mcp-server/templates/NOTES.txt b/charts/nextcloud-mcp-server/templates/NOTES.txt index 8890723..7877506 100644 --- a/charts/nextcloud-mcp-server/templates/NOTES.txt +++ b/charts/nextcloud-mcp-server/templates/NOTES.txt @@ -57,6 +57,28 @@ Your Nextcloud MCP Server has been deployed in {{ .Values.auth.mode }} authentic IMPORTANT: OAuth mode is experimental and requires patches to the user_oidc app. See: https://github.com/cbcoutinho/nextcloud-mcp-server#authentication +{{- else if eq .Values.auth.mode "multi-user-basic" }} + +3. Multi-User BasicAuth Mode (Pass-Through): + - Users provide credentials via Authorization header + - Connected to: {{ .Values.nextcloud.host }} + {{- if .Values.auth.multiUserBasic.enableOfflineAccess }} + - Offline access: Enabled (background operations with app passwords) + - Token storage: {{ .Values.auth.multiUserBasic.tokenStorageDb }} + {{- else }} + - Offline access: Disabled (stateless pass-through) + {{- end }} +{{- else if eq .Values.auth.mode "login-flow" }} + +3. Login Flow v2 Mode (Experimental, ADR-022): + - Server URL: {{ include "nextcloud-mcp-server.mcpServerUrl" . }} + - Connected to: {{ .Values.nextcloud.host }} + - Token storage: {{ .Values.auth.loginFlow.tokenStorageDb }} + + Users authenticate via Nextcloud's native Login Flow v2 — no OAuth patches required. + Each user gets a per-device app password managed by the MCP server. + + IMPORTANT: Login Flow v2 is experimental. See ADR-022 for details. {{- end }} {{- if .Values.documentProcessing.enabled }} @@ -169,6 +191,12 @@ After migrating, remove the deprecated settings: ================================================================================ {{- end }} +Deployed version: + - Chart: {{ .Chart.Version }} + - App: {{ .Chart.AppVersion }} + +Full changelog: https://github.com/cbcoutinho/nextcloud-mcp-server/blob/master/charts/nextcloud-mcp-server/CHANGELOG.md + For more information and documentation: - GitHub: https://github.com/cbcoutinho/nextcloud-mcp-server - Documentation: https://github.com/cbcoutinho/nextcloud-mcp-server#readme diff --git a/charts/nextcloud-mcp-server/templates/_helpers.tpl b/charts/nextcloud-mcp-server/templates/_helpers.tpl index 9d0eb7e..0498057 100644 --- a/charts/nextcloud-mcp-server/templates/_helpers.tpl +++ b/charts/nextcloud-mcp-server/templates/_helpers.tpl @@ -105,6 +105,17 @@ Create the name of the secret to use for OAuth {{- end }} {{- end }} +{{/* +Create the name of the secret to use for Login Flow v2 +*/}} +{{- define "nextcloud-mcp-server.loginFlowSecretName" -}} +{{- if .Values.auth.loginFlow.existingSecret }} +{{- .Values.auth.loginFlow.existingSecret }} +{{- else }} +{{- include "nextcloud-mcp-server.fullname" . }}-login-flow +{{- end }} +{{- end }} + {{/* Create the name of the PVC to use for OAuth storage */}} @@ -147,6 +158,8 @@ Checks new dataStorage.enabled OR legacy persistence configs true {{- else if and (eq .Values.auth.mode "multi-user-basic") .Values.auth.multiUserBasic.enableOfflineAccess .Values.auth.multiUserBasic.persistence.enabled -}} true +{{- else if eq .Values.auth.mode "login-flow" -}} +true {{- else if and (eq .Values.qdrant.mode "persistent") .Values.qdrant.localPersistence.enabled -}} true {{- else -}} diff --git a/charts/nextcloud-mcp-server/templates/deployment.yaml b/charts/nextcloud-mcp-server/templates/deployment.yaml index cc58101..749fe2b 100644 --- a/charts/nextcloud-mcp-server/templates/deployment.yaml +++ b/charts/nextcloud-mcp-server/templates/deployment.yaml @@ -46,8 +46,10 @@ spec: args: - "--transport" - "{{ .Values.mcp.transport }}" - {{- if eq .Values.auth.mode "oauth" }} + {{- if or (eq .Values.auth.mode "oauth") (eq .Values.auth.mode "login-flow") }} - "--oauth" + {{- end }} + {{- if eq .Values.auth.mode "oauth" }} - "--oauth-token-type" - "{{ .Values.auth.oauth.tokenType }}" {{- end }} @@ -134,6 +136,21 @@ spec: name: {{ include "nextcloud-mcp-server.oauthSecretName" . }} key: {{ .Values.auth.oauth.clientSecretKey }} {{- end }} + {{- else if eq .Values.auth.mode "login-flow" }} + # Login Flow v2 mode (ADR-022) + - name: ENABLE_LOGIN_FLOW + value: "true" + - name: NEXTCLOUD_MCP_SERVER_URL + value: {{ include "nextcloud-mcp-server.mcpServerUrl" . | quote }} + - name: NEXTCLOUD_PUBLIC_ISSUER_URL + value: {{ include "nextcloud-mcp-server.publicIssuerUrl" . | quote }} + - name: TOKEN_STORAGE_DB + value: {{ .Values.auth.loginFlow.tokenStorageDb | quote }} + - name: TOKEN_ENCRYPTION_KEY + valueFrom: + secretKeyRef: + name: {{ include "nextcloud-mcp-server.loginFlowSecretName" . }} + key: {{ .Values.auth.loginFlow.tokenEncryptionKeyKey }} {{- end }} {{- if .Values.documentProcessing.enabled }} # Document processing @@ -282,7 +299,7 @@ spec: volumeMounts: - name: tmp mountPath: /tmp - {{- if and (eq .Values.auth.mode "oauth") .Values.auth.oauth.persistence.enabled }} + {{- if or (and (eq .Values.auth.mode "oauth") .Values.auth.oauth.persistence.enabled) (eq .Values.auth.mode "login-flow") }} - name: oauth-storage mountPath: /app/.oauth {{- end }} @@ -294,7 +311,7 @@ spec: volumes: - name: tmp emptyDir: {} - {{- if and (eq .Values.auth.mode "oauth") .Values.auth.oauth.persistence.enabled }} + {{- if or (and (eq .Values.auth.mode "oauth") .Values.auth.oauth.persistence.enabled) (eq .Values.auth.mode "login-flow") }} - name: oauth-storage persistentVolumeClaim: claimName: {{ include "nextcloud-mcp-server.oauthPvcName" . }} diff --git a/charts/nextcloud-mcp-server/templates/pvc.yaml b/charts/nextcloud-mcp-server/templates/pvc.yaml index e07ace0..268b812 100644 --- a/charts/nextcloud-mcp-server/templates/pvc.yaml +++ b/charts/nextcloud-mcp-server/templates/pvc.yaml @@ -16,6 +16,21 @@ spec: storage: {{ .Values.auth.oauth.persistence.size }} {{- end }} --- +{{- if eq .Values.auth.mode "login-flow" }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "nextcloud-mcp-server.fullname" . }}-oauth-storage + labels: + {{- include "nextcloud-mcp-server.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +{{- end }} +--- {{- if and (eq (include "nextcloud-mcp-server.dataStorageEnabled" .) "true") (not .Values.dataStorage.existingClaim) }} {{- $legacyMultiUserBasic := eq (include "nextcloud-mcp-server.legacyMultiUserBasicPersistence" .) "true" }} {{- $legacyQdrant := eq (include "nextcloud-mcp-server.legacyQdrantPersistence" .) "true" }} diff --git a/charts/nextcloud-mcp-server/templates/secret.yaml b/charts/nextcloud-mcp-server/templates/secret.yaml index eed9b81..aeb138b 100644 --- a/charts/nextcloud-mcp-server/templates/secret.yaml +++ b/charts/nextcloud-mcp-server/templates/secret.yaml @@ -45,3 +45,17 @@ data: {{ .Values.auth.oauth.clientSecretKey }}: {{ .Values.auth.oauth.clientSecret | b64enc | quote }} {{- end }} {{- end }} +--- +{{- if eq .Values.auth.mode "login-flow" }} +{{- if not .Values.auth.loginFlow.existingSecret }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "nextcloud-mcp-server.fullname" . }}-login-flow + labels: + {{- include "nextcloud-mcp-server.labels" . | nindent 4 }} +type: Opaque +data: + {{ .Values.auth.loginFlow.tokenEncryptionKeyKey }}: {{ .Values.auth.loginFlow.tokenEncryptionKey | b64enc | quote }} +{{- end }} +{{- end }} diff --git a/charts/nextcloud-mcp-server/values.yaml b/charts/nextcloud-mcp-server/values.yaml index 7b46811..2bea791 100644 --- a/charts/nextcloud-mcp-server/values.yaml +++ b/charts/nextcloud-mcp-server/values.yaml @@ -40,12 +40,13 @@ nextcloud: publicIssuerUrl: "" # Authentication configuration -# Choose one mode: "basic", "multi-user-basic", or "oauth" +# Choose one mode: "basic", "multi-user-basic", "oauth", or "login-flow" auth: - # Authentication mode: "basic", "multi-user-basic", or "oauth" + # Authentication mode: "basic", "multi-user-basic", "oauth", or "login-flow" # basic: Single-user with username/password (recommended for personal use) # multi-user-basic: Multi-user with BasicAuth pass-through (credentials in request headers) # oauth: Uses OAuth2/OIDC (experimental, requires patches) + # login-flow: Multi-user via Nextcloud Login Flow v2 (experimental, ADR-022) mode: basic # Basic authentication settings (single-user mode) @@ -139,6 +140,21 @@ auth: # Use existing PVC existingClaim: "" + # Login Flow v2 settings (experimental, ADR-022) + # Uses Nextcloud's native Login Flow v2 to obtain app passwords per user. + # No OAuth patches required — works with stock Nextcloud. + # See: docs/ADR-022-deployment-mode-consolidation.md + loginFlow: + # Token encryption key (required, ignored if existingSecret is set) + # Generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" + tokenEncryptionKey: "" + # Token storage database path + tokenStorageDb: "/app/data/tokens.db" + # Use existing secret instead of creating one + existingSecret: "" + # Key in the existing secret + tokenEncryptionKeyKey: "token_encryption_key" + # Data Storage Configuration # Persistent volume for /app/data directory # Used for: token databases, qdrant persistent storage, and any app data @@ -147,6 +163,7 @@ dataStorage: # Enable persistent storage for /app/data # Set to true when using: # - Multi-user basic auth with offline access (stores tokens.db) + # - Login flow mode (stores app passwords in tokens.db) # - Qdrant persistent mode (stores vector database) # - Any feature requiring persistent app data # Set to false for basic auth without persistence (uses emptyDir)