fix(auth): address PR #758 round-2 review

- oauth_login_callback's integrated-mode token-exchange branch now reuses
  the shared discovery cache via get_oidc_discovery (round-2 finding 1).
- AS proxy flow now generates an OIDC nonce in oauth_authorize, stores it
  on ASProxySession, forwards it to the IdP, and passes it as
  expected_nonce to verify_id_token in _oauth_callback_as_proxy
  (round-2 finding 2).
- Consolidate the two parallel discovery caches: oauth_routes' local
  _discovery_cache and _get_cached_discovery are removed; all callers
  now go through token_utils.get_oidc_discovery, which acquires the
  follow_redirects=True knob it needs for Nextcloud installs without
  pretty URLs (round-2 finding 3).
- Demote per-user INFO logs in oauth_tools.py (check_logged_in,
  get_provisioning_status) to DEBUG; the elicitation auth URL is no
  longer logged because it contains a sensitive state token
  (round-2 finding 4).

Also pin nonce binding behaviour with a new unit test that asserts
_oauth_callback_as_proxy forwards session.nonce to verify_id_token, and
update test mocks to track the cache consolidation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-05-02 21:56:08 +02:00
co-authored by Claude Opus 4.7
parent 4c84d82984
commit c33d52ea91
8 changed files with 210 additions and 114 deletions
+9 -8
View File
@@ -1,12 +1,12 @@
"""Unit tests for OIDC discovery fetch in oauth_routes."""
"""Unit tests for the shared OIDC discovery fetch in token_utils."""
from unittest.mock import patch
import httpx
import pytest
from nextcloud_mcp_server.auth import oauth_routes
from nextcloud_mcp_server.auth.oauth_routes import _get_cached_discovery
from nextcloud_mcp_server.auth import token_utils
from nextcloud_mcp_server.auth.token_utils import get_oidc_discovery
pytestmark = pytest.mark.unit
@@ -14,9 +14,9 @@ pytestmark = pytest.mark.unit
@pytest.fixture(autouse=True)
def _clear_discovery_cache():
"""Reset the in-memory discovery cache between tests."""
oauth_routes._discovery_cache.clear()
token_utils._discovery_cache.clear()
yield
oauth_routes._discovery_cache.clear()
token_utils._discovery_cache.clear()
async def test_discovery_follows_redirect_to_index_php():
@@ -26,7 +26,8 @@ async def test_discovery_follows_redirect_to_index_php():
redirect ``/.well-known/openid-configuration`` to
``/index.php/.well-known/openid-configuration``. Without follow_redirects
the OAuth authorize handler raises HTTPStatusError and returns 500
(see oauth_routes._get_cached_discovery).
(PR #758 round-2 nit 3 consolidated the discovery cache; see
``token_utils.get_oidc_discovery``).
"""
pretty_url = "https://nx.example.com/.well-known/openid-configuration"
@@ -51,10 +52,10 @@ async def test_discovery_follows_redirect_to_index_php():
return httpx.AsyncClient(**kwargs)
with patch(
"nextcloud_mcp_server.auth.oauth_routes.nextcloud_httpx_client",
"nextcloud_mcp_server.auth.token_utils.nextcloud_httpx_client",
side_effect=fake_client,
) as factory:
result = await _get_cached_discovery(pretty_url)
result = await get_oidc_discovery(pretty_url)
assert result == discovery_doc
factory.assert_called_once()