Compare commits

...

17 Commits

Author SHA1 Message Date
brandon 92f2d74637 feat: auto-derive oidc.discovery_url from NEXTCLOUD_HOST
Bump version / Bump version and create changelog for monorepo components (push) Failing after 8s
When OIDC_DISCOVERY_URL is not explicitly set, the status endpoint now
auto-derives the discovery URL from NEXTCLOUD_HOST using the standard
well-known path. This allows Astrolabe to discover OIDC endpoints
without requiring explicit OIDC configuration.

The oidc block is now included in the status response regardless of
auth mode when a discovery URL is available (explicit or derived),
enabling smoother auth mode transitions.

Closes #1
2026-03-29 12:56:50 -06:00
github-actions[bot] 656acc2c1f bump: version 0.58.2 → 0.58.3 2026-03-16 17:38:05 +00:00
Chris Coutinho c726e25e8b Merge pull request #625 from cbcoutinho/renovate/astral-sh-setup-uv-7.x
chore(deps): update astral-sh/setup-uv action to v7.6.0
2026-03-16 18:37:45 +01:00
renovate-bot-cbcoutinho[bot] 355bd1bad3 chore(deps): update astral-sh/setup-uv action to v7.6.0 2026-03-16 17:22:55 +00:00
github-actions[bot] 989d3f2857 bump: version 0.58.1 → 0.58.2 2026-03-14 15:56:15 +00:00
Chris Coutinho 92d5cd4e26 Merge pull request #613 from cbcoutinho/renovate/anthropics-claude-code-action-1.x
chore(deps): update anthropics/claude-code-action action to v1.0.72
2026-03-14 16:55:59 +01:00
renovate-bot-cbcoutinho[bot] 5823286907 chore(deps): update anthropics/claude-code-action action to v1.0.72 2026-03-14 05:23:04 +00:00
github-actions[bot] 7fb6613bc2 bump: version 0.58.0 → 0.58.1 2026-03-03 11:33:21 +00:00
Chris Coutinho cd6f0ffa63 Merge pull request #606 from cbcoutinho/renovate/node-24.x
chore(deps): update dependency node to v24
2026-03-03 12:26:33 +01:00
Chris Coutinho 5d98858bb6 Merge pull request #603 from cbcoutinho/renovate/docker.io-library-nextcloud-33.0.0
chore(deps): update docker.io/library/nextcloud:33.0.0 docker digest to d53f6cb
2026-03-03 12:26:07 +01:00
Chris Coutinho af7c752cc1 Merge pull request #607 from cbcoutinho/renovate/migrate-config
chore(config): migrate Renovate config
2026-03-03 12:25:36 +01:00
Chris Coutinho 2526390ce8 Merge pull request #604 from cbcoutinho/renovate/docker.io-library-nextcloud-31.x
chore(deps): update docker.io/library/nextcloud docker tag to v31.0.14
2026-03-03 12:24:50 +01:00
renovate-bot-cbcoutinho[bot] 0b5571f3d7 chore(config): migrate config renovate.json 2026-03-03 11:18:14 +00:00
renovate-bot-cbcoutinho[bot] 059f37d093 chore(deps): update dependency node to v24 2026-03-03 11:18:05 +00:00
renovate-bot-cbcoutinho[bot] 28ad0aefbf chore(deps): update docker.io/library/nextcloud docker tag to v31.0.14 2026-03-03 11:17:49 +00:00
renovate-bot-cbcoutinho[bot] 6ce9599757 chore(deps): update docker.io/library/nextcloud:33.0.0 docker digest to d53f6cb 2026-03-03 11:17:25 +00:00
github-actions[bot] 1cdf148899 bump: version 0.57.94 → 0.58.0 2026-03-03 08:42:10 +00:00
11 changed files with 199 additions and 30 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@64c7a0ef71df67b14cb4471f4d9c8565c61042bf # v1.0.66
uses: anthropics/claude-code-action@cd77b50d2b0808657f8e6774085c8bf54484351c # v1.0.72
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
allowed_bots: "renovate-bot-cbcoutinho"
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@64c7a0ef71df67b14cb4471f4d9c8565c61042bf # v1.0.66
uses: anthropics/claude-code-action@cd77b50d2b0808657f8e6774085c8bf54484351c # v1.0.72
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
+1 -1
View File
@@ -42,7 +42,7 @@ jobs:
VECTOR_SYNC_SCAN_INTERVAL: "5"
- name: Install the latest version of uv
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: Wait for Nextcloud to be ready
run: |
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install uv
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: Install Python 3.11
run: uv python install 3.11
- name: Build
+6 -6
View File
@@ -11,7 +11,7 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install the latest version of uv
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: Check format
run: uv run --frozen ruff format --diff
- name: Linting
@@ -25,7 +25,7 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install the latest version of uv
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: Run unit tests
run: uv run pytest -v -m unit -o "addopts=-p no:asyncio"
@@ -48,14 +48,14 @@ jobs:
# Version-specific image pins — Renovate updates these via customManagers
# renovate: datasource=docker depName=docker.io/library/nextcloud
- nextcloud_version: "31"
nextcloud_image: "docker.io/library/nextcloud:31.0.8@sha256:92bc503ea0c19789f402b0469ecfb8df1ffea81e2bf90a45bba39063a626aa00"
nextcloud_image: "docker.io/library/nextcloud:31.0.14@sha256:9bf3fae91aad4dca3eff02c1f71df8d5c6705a349065fb537aa5c5ef578f1013"
# renovate: datasource=docker depName=docker.io/library/nextcloud
- nextcloud_version: "32"
nextcloud_image: "docker.io/library/nextcloud:32.0.6@sha256:5c4e09f72f096cd68379a8ae69f71e61d13da5a07430fc4a17c702a14e6a4267"
# renovate: datasource=docker depName=docker.io/library/nextcloud
# Disabled until all upstream apps support NC 33
# - nextcloud_version: "33"
# nextcloud_image: "docker.io/library/nextcloud:33.0.0@sha256:ff2cbaab14c85e587b5541e3aff4216a8a484e06424ebae661581937c0c8da0c"
# nextcloud_image: "docker.io/library/nextcloud:33.0.0@sha256:d53f6cb35b0712aa890a5e4a8ca21043d6fcd390f38c55b710816dd7cbc2edc0"
# Mode-specific properties
- mode: single-user
@@ -112,7 +112,7 @@ jobs:
if: matrix.mode != 'single-user'
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 22
node-version: 24
- name: Build Astrolabe app
if: matrix.mode != 'single-user'
@@ -134,7 +134,7 @@ jobs:
NEXTCLOUD_IMAGE: ${{ matrix.nextcloud_image }}
- name: Install the latest version of uv
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
- name: Install Playwright
if: matrix.needs-playwright
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.commitizen]
name = "cz_conventional_commits"
version = "0.57.94"
version = "0.58.3"
tag_format = "nextcloud-mcp-server-$version"
version_scheme = "semver"
update_changelog_on_bump = true
+35
View File
@@ -14,6 +14,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Configurable resource limits
- Grafana dashboard annotations
## nextcloud-mcp-server-0.58.3 (2026-03-16)
## nextcloud-mcp-server-0.58.2 (2026-03-14)
## nextcloud-mcp-server-0.58.1 (2026-03-03)
## nextcloud-mcp-server-0.58.0 (2026-03-03)
### Feat
- **auth**: implement OAuth AS proxy to fix audience mismatch (ADR-023)
- **ci**: add Nextcloud version matrix (NC 31, 32, 33)
- **helm**: add login-flow auth mode to Helm chart (ADR-022)
- add Docker Compose profiles and Login Flow v2 service
### Fix
- replace assert with proper guard and invalidate scope cache after provisioning
- disable NC rate limiting in dev/CI and add token endpoint diagnostics
- address review feedback — security, caching, CI 429 retry
- skip keycloak hook when profile inactive and update stale PRM test
- address remaining PR #589 review findings
- address PR #589 review findings
- address PR review issues for Login Flow v2
- address PR #589 review feedback (round 2)
- **ci**: remove dev OIDC mount to fix HTTP 500 in single-user/multi-user-basic
- **ci**: fix health check timeout and per-profile MCP server URL routing
- **ci**: fix PHP gating, add multi-user-basic matrix entry, upload debug artifacts
- address PR #589 review feedback for Login Flow v2
- **ci**: fix integration test collection and skip Playwright in CI
- **test**: fix 17 pre-existing unit test failures and add astrolabe CI build
- **ci**: keep third_party mount, always build submodules in CI
- **ci**: revert accidental third_party mount, use compose override for OIDC
- **ci**: don't block integration matrix on unit-test failures
## nextcloud-mcp-server-0.57.94 (2026-03-03)
### Fix
+1 -1
View File
@@ -2,7 +2,7 @@ apiVersion: v2
name: nextcloud-mcp-server
description: A Helm chart for Nextcloud MCP Server - enables AI assistants to interact with Nextcloud
type: application
version: 0.57.94
version: 0.58.3
appVersion: "0.65.0"
keywords:
- nextcloud
+17 -15
View File
@@ -235,24 +235,26 @@ async def get_server_status(request: Request) -> JSONResponse:
if mode == AuthMode.MULTI_USER_BASIC:
response_data["supports_app_passwords"] = settings.enable_offline_access
# Include OIDC configuration if OAuth is available
# This includes OAuth mode AND hybrid mode (multi_user_basic + offline_access)
# Astrolabe needs OIDC config to discover IdP for OAuth flow in hybrid mode
oauth_provisioning_available = auth_mode == "oauth" or (
mode == AuthMode.MULTI_USER_BASIC and settings.enable_offline_access
)
if oauth_provisioning_available:
# Provide IdP discovery information for NC PHP app
oidc_config = {}
# Include OIDC configuration for client discovery (e.g. Astrolabe PHP app).
# Always attempt to provide oidc.discovery_url so clients can discover the
# IdP regardless of the current auth mode. This enables smoother transitions
# between auth modes and lets Astrolabe pre-discover OIDC endpoints.
oidc_config: dict[str, str] = {}
if settings.oidc_discovery_url:
oidc_config["discovery_url"] = settings.oidc_discovery_url
if settings.oidc_discovery_url:
# Explicit OIDC_DISCOVERY_URL takes precedence
oidc_config["discovery_url"] = settings.oidc_discovery_url
elif settings.nextcloud_host:
# Auto-derive from NEXTCLOUD_HOST — Nextcloud exposes OIDC discovery
# at the standard well-known path when user_oidc is enabled
host = settings.nextcloud_host.rstrip("/")
oidc_config["discovery_url"] = f"{host}/.well-known/openid-configuration"
if settings.oidc_issuer:
oidc_config["issuer"] = settings.oidc_issuer
if settings.oidc_issuer:
oidc_config["issuer"] = settings.oidc_issuer
if oidc_config:
response_data["oidc"] = oidc_config
if oidc_config:
response_data["oidc"] = oidc_config
return JSONResponse(response_data)
+6 -2
View File
@@ -7,14 +7,18 @@
"dependencyDashboard": true,
"packageRules": [
{
"matchPackageNames": ["pillow"],
"matchPackageNames": [
"pillow"
],
"allowedVersions": "<12.0.0"
}
],
"customManagers": [
{
"customType": "regex",
"fileMatch": ["^\\.github/workflows/test\\.yml$"],
"managerFilePatterns": [
"/^\\.github/workflows/test\\.yml$/"
],
"matchStrings": [
"nextcloud_image:\\s*\"(?<depName>[^:]+):(?<currentValue>[^@]+)@(?<currentDigest>sha256:[a-f0-9]+)\""
],
+129 -1
View File
@@ -37,6 +37,7 @@ def create_mock_settings(
oidc_issuer: str | None = None,
vector_sync_enabled: bool = False,
nextcloud_url: str = "http://localhost",
nextcloud_host: str | None = "http://localhost",
enable_token_exchange: bool = False,
mcp_client_id: str | None = None,
mcp_client_secret: str | None = None,
@@ -49,6 +50,7 @@ def create_mock_settings(
settings.oidc_issuer = oidc_issuer
settings.vector_sync_enabled = vector_sync_enabled
settings.nextcloud_url = nextcloud_url
settings.nextcloud_host = nextcloud_host
settings.enable_token_exchange = enable_token_exchange
settings.mcp_client_id = mcp_client_id
settings.mcp_client_secret = mcp_client_secret
@@ -133,6 +135,7 @@ class TestStatusEndpointOidcConfig:
enable_offline_access=False, # Key difference: no offline access
oidc_discovery_url="http://keycloak/.well-known/openid-configuration",
oidc_issuer="http://keycloak/realms/test",
nextcloud_host=None,
)
with (
@@ -196,12 +199,13 @@ class TestStatusEndpointOidcConfig:
)
def test_single_user_basic_no_oidc(self):
"""Test that single-user BasicAuth mode doesn't return OIDC config."""
"""Test that single-user BasicAuth mode doesn't return OIDC config when no host."""
mock_settings = create_mock_settings(
enable_multi_user_basic=False,
enable_offline_access=False,
oidc_discovery_url="http://keycloak/.well-known/openid-configuration",
oidc_issuer="http://keycloak/realms/test",
nextcloud_host=None,
)
with (
@@ -344,3 +348,127 @@ class TestStatusEndpointBasicResponse:
data = response.json()
assert data["vector_sync_enabled"] is True
class TestStatusEndpointOidcAutoDerivation:
"""Tests for OIDC discovery_url auto-derivation from NEXTCLOUD_HOST."""
def test_derives_discovery_url_from_nextcloud_host(self):
"""Test that discovery_url is auto-derived from nextcloud_url when not explicit."""
mock_settings = create_mock_settings(
oidc_discovery_url=None,
oidc_issuer=None,
)
mock_settings.nextcloud_host = "https://cloud.example.com"
with (
patch(
"nextcloud_mcp_server.api.management.get_settings",
return_value=mock_settings,
),
patch(
"nextcloud_mcp_server.api.management.detect_auth_mode",
return_value=AuthMode.SINGLE_USER_BASIC,
),
):
app = create_test_app()
client = TestClient(app)
response = client.get("/api/v1/status")
assert response.status_code == 200
data = response.json()
assert "oidc" in data
assert (
data["oidc"]["discovery_url"]
== "https://cloud.example.com/.well-known/openid-configuration"
)
def test_derives_discovery_url_strips_trailing_slash(self):
"""Test that trailing slash on nextcloud_host is stripped."""
mock_settings = create_mock_settings(
oidc_discovery_url=None,
oidc_issuer=None,
)
mock_settings.nextcloud_host = "https://cloud.example.com/"
with (
patch(
"nextcloud_mcp_server.api.management.get_settings",
return_value=mock_settings,
),
patch(
"nextcloud_mcp_server.api.management.detect_auth_mode",
return_value=AuthMode.SINGLE_USER_BASIC,
),
):
app = create_test_app()
client = TestClient(app)
response = client.get("/api/v1/status")
assert response.status_code == 200
data = response.json()
assert "oidc" in data
assert (
data["oidc"]["discovery_url"]
== "https://cloud.example.com/.well-known/openid-configuration"
)
def test_explicit_discovery_url_takes_precedence(self):
"""Test that explicit OIDC_DISCOVERY_URL overrides auto-derivation."""
mock_settings = create_mock_settings(
oidc_discovery_url="https://keycloak.example.com/.well-known/openid-configuration",
oidc_issuer=None,
)
mock_settings.nextcloud_host = "https://cloud.example.com"
with (
patch(
"nextcloud_mcp_server.api.management.get_settings",
return_value=mock_settings,
),
patch(
"nextcloud_mcp_server.api.management.detect_auth_mode",
return_value=AuthMode.SINGLE_USER_BASIC,
),
):
app = create_test_app()
client = TestClient(app)
response = client.get("/api/v1/status")
assert response.status_code == 200
data = response.json()
assert "oidc" in data
assert (
data["oidc"]["discovery_url"]
== "https://keycloak.example.com/.well-known/openid-configuration"
)
def test_no_oidc_when_no_host_and_no_discovery_url(self):
"""Test that oidc block is absent when neither host nor discovery_url is set."""
mock_settings = create_mock_settings(
oidc_discovery_url=None,
oidc_issuer=None,
)
mock_settings.nextcloud_host = None
with (
patch(
"nextcloud_mcp_server.api.management.get_settings",
return_value=mock_settings,
),
patch(
"nextcloud_mcp_server.api.management.detect_auth_mode",
return_value=AuthMode.SINGLE_USER_BASIC,
),
):
app = create_test_app()
client = TestClient(app)
response = client.get("/api/v1/status")
assert response.status_code == 200
data = response.json()
assert "oidc" not in data