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 <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-03-01 18:25:23 +01:00
parent bd69e68dd5
commit 0b8afec494
8 changed files with 143 additions and 13 deletions
+25 -8
View File
@@ -55,6 +55,15 @@ http://127.0.0.1:8000/sse
http://127.0.0.1:8000/mcp 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:** **Next Steps:**
- Connect your MCP client (Claude Desktop, IDEs, `mcp dev`, etc.) - Connect your MCP client (Claude Desktop, IDEs, `mcp dev`, etc.)
- See [docs/installation.md](docs/installation.md) for other deployment options (local, Kubernetes) - 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 ### 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 - One set of credentials shared by all MCP clients
- Simple setup: username + app password in environment variables - Simple setup: username + app password in environment variables
- All clients access Nextcloud as the same user - All clients access Nextcloud as the same user
- Best for: Personal use, development, single-user deployments - 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 - Each MCP client authenticates separately with their own Nextcloud account
- Per-user scopes and permissions (clients only see tools they're authorized for) - Per-user scopes and permissions (clients only see tools they're authorized for)
- More secure: tokens expire, credentials never shared with server - More secure: tokens expire, credentials never shared with server
- Best for: Teams, multi-user deployments, production environments with multiple users - 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):** **Multi-User (Login Flow v2):**
- MCP clients use BasicAuth (simple, stateless) - Uses Nextcloud's native Login Flow v2 to obtain per-user app passwords
- Admin operations use OAuth (webhooks, background sync) - No OAuth patches required — works with stock Nextcloud
- Best for: Nextcloud deployments with admin-managed webhooks and semantic search - Each user authenticates via browser, server manages app passwords
- Requires: `ENABLE_MULTI_USER_BASIC_AUTH=true` + `ENABLE_OFFLINE_ACCESS=true` - 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. See [docs/authentication.md](docs/authentication.md) for detailed setup instructions.
+9
View File
@@ -25,6 +25,15 @@ annotations:
# Grafana dashboard support # Grafana dashboard support
grafana_dashboard: "true" grafana_dashboard: "true"
grafana_dashboard_folder: "Nextcloud MCP" 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: dependencies:
- name: qdrant - name: qdrant
version: "1.17.0" version: "1.17.0"
@@ -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. IMPORTANT: OAuth mode is experimental and requires patches to the user_oidc app.
See: https://github.com/cbcoutinho/nextcloud-mcp-server#authentication 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 }} {{- end }}
{{- if .Values.documentProcessing.enabled }} {{- if .Values.documentProcessing.enabled }}
@@ -169,6 +191,12 @@ After migrating, remove the deprecated settings:
================================================================================ ================================================================================
{{- end }} {{- 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: For more information and documentation:
- GitHub: https://github.com/cbcoutinho/nextcloud-mcp-server - GitHub: https://github.com/cbcoutinho/nextcloud-mcp-server
- Documentation: https://github.com/cbcoutinho/nextcloud-mcp-server#readme - Documentation: https://github.com/cbcoutinho/nextcloud-mcp-server#readme
@@ -105,6 +105,17 @@ Create the name of the secret to use for OAuth
{{- end }} {{- end }}
{{- 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 Create the name of the PVC to use for OAuth storage
*/}} */}}
@@ -147,6 +158,8 @@ Checks new dataStorage.enabled OR legacy persistence configs
true true
{{- else if and (eq .Values.auth.mode "multi-user-basic") .Values.auth.multiUserBasic.enableOfflineAccess .Values.auth.multiUserBasic.persistence.enabled -}} {{- else if and (eq .Values.auth.mode "multi-user-basic") .Values.auth.multiUserBasic.enableOfflineAccess .Values.auth.multiUserBasic.persistence.enabled -}}
true true
{{- else if eq .Values.auth.mode "login-flow" -}}
true
{{- else if and (eq .Values.qdrant.mode "persistent") .Values.qdrant.localPersistence.enabled -}} {{- else if and (eq .Values.qdrant.mode "persistent") .Values.qdrant.localPersistence.enabled -}}
true true
{{- else -}} {{- else -}}
@@ -46,8 +46,10 @@ spec:
args: args:
- "--transport" - "--transport"
- "{{ .Values.mcp.transport }}" - "{{ .Values.mcp.transport }}"
{{- if eq .Values.auth.mode "oauth" }} {{- if or (eq .Values.auth.mode "oauth") (eq .Values.auth.mode "login-flow") }}
- "--oauth" - "--oauth"
{{- end }}
{{- if eq .Values.auth.mode "oauth" }}
- "--oauth-token-type" - "--oauth-token-type"
- "{{ .Values.auth.oauth.tokenType }}" - "{{ .Values.auth.oauth.tokenType }}"
{{- end }} {{- end }}
@@ -134,6 +136,21 @@ spec:
name: {{ include "nextcloud-mcp-server.oauthSecretName" . }} name: {{ include "nextcloud-mcp-server.oauthSecretName" . }}
key: {{ .Values.auth.oauth.clientSecretKey }} key: {{ .Values.auth.oauth.clientSecretKey }}
{{- end }} {{- 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 }} {{- end }}
{{- if .Values.documentProcessing.enabled }} {{- if .Values.documentProcessing.enabled }}
# Document processing # Document processing
@@ -282,7 +299,7 @@ spec:
volumeMounts: volumeMounts:
- name: tmp - name: tmp
mountPath: /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 - name: oauth-storage
mountPath: /app/.oauth mountPath: /app/.oauth
{{- end }} {{- end }}
@@ -294,7 +311,7 @@ spec:
volumes: volumes:
- name: tmp - name: tmp
emptyDir: {} 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 - name: oauth-storage
persistentVolumeClaim: persistentVolumeClaim:
claimName: {{ include "nextcloud-mcp-server.oauthPvcName" . }} claimName: {{ include "nextcloud-mcp-server.oauthPvcName" . }}
@@ -16,6 +16,21 @@ spec:
storage: {{ .Values.auth.oauth.persistence.size }} storage: {{ .Values.auth.oauth.persistence.size }}
{{- end }} {{- 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) }} {{- if and (eq (include "nextcloud-mcp-server.dataStorageEnabled" .) "true") (not .Values.dataStorage.existingClaim) }}
{{- $legacyMultiUserBasic := eq (include "nextcloud-mcp-server.legacyMultiUserBasicPersistence" .) "true" }} {{- $legacyMultiUserBasic := eq (include "nextcloud-mcp-server.legacyMultiUserBasicPersistence" .) "true" }}
{{- $legacyQdrant := eq (include "nextcloud-mcp-server.legacyQdrantPersistence" .) "true" }} {{- $legacyQdrant := eq (include "nextcloud-mcp-server.legacyQdrantPersistence" .) "true" }}
@@ -45,3 +45,17 @@ data:
{{ .Values.auth.oauth.clientSecretKey }}: {{ .Values.auth.oauth.clientSecret | b64enc | quote }} {{ .Values.auth.oauth.clientSecretKey }}: {{ .Values.auth.oauth.clientSecret | b64enc | quote }}
{{- end }} {{- end }}
{{- 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 }}
+19 -2
View File
@@ -40,12 +40,13 @@ nextcloud:
publicIssuerUrl: "" publicIssuerUrl: ""
# Authentication configuration # Authentication configuration
# Choose one mode: "basic", "multi-user-basic", or "oauth" # Choose one mode: "basic", "multi-user-basic", "oauth", or "login-flow"
auth: 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) # basic: Single-user with username/password (recommended for personal use)
# multi-user-basic: Multi-user with BasicAuth pass-through (credentials in request headers) # multi-user-basic: Multi-user with BasicAuth pass-through (credentials in request headers)
# oauth: Uses OAuth2/OIDC (experimental, requires patches) # oauth: Uses OAuth2/OIDC (experimental, requires patches)
# login-flow: Multi-user via Nextcloud Login Flow v2 (experimental, ADR-022)
mode: basic mode: basic
# Basic authentication settings (single-user mode) # Basic authentication settings (single-user mode)
@@ -139,6 +140,21 @@ auth:
# Use existing PVC # Use existing PVC
existingClaim: "" 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 # Data Storage Configuration
# Persistent volume for /app/data directory # Persistent volume for /app/data directory
# Used for: token databases, qdrant persistent storage, and any app data # Used for: token databases, qdrant persistent storage, and any app data
@@ -147,6 +163,7 @@ dataStorage:
# Enable persistent storage for /app/data # Enable persistent storage for /app/data
# Set to true when using: # Set to true when using:
# - Multi-user basic auth with offline access (stores tokens.db) # - 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) # - Qdrant persistent mode (stores vector database)
# - Any feature requiring persistent app data # - Any feature requiring persistent app data
# Set to false for basic auth without persistence (uses emptyDir) # Set to false for basic auth without persistence (uses emptyDir)