fix: Implement proper OAuth resource parameters and PRM-based discovery
This commit completes the OAuth audience validation implementation per RFC 7519, RFC 8707 (Resource Indicators), and RFC 9728 (Protected Resource Metadata). ## Key Changes ### OAuth Resource Parameters (RFC 8707) - Add `resource` parameter to Flow 1 (MCP client auth) with MCP server audience - Add `resource` parameter to Flow 2 (Nextcloud access) with Nextcloud audience - Add `nextcloud_resource_uri` to oauth_context configuration - Fix undefined variable error in starlette_lifespan ### PRM-Based Resource Discovery (RFC 9728) - Update tests to fetch resource identifier from PRM endpoint - Add fallback to hardcoded value if PRM fetch fails - Demonstrate correct OAuth client implementation pattern ### ADR-005 Documentation Updates - Update to reflect simplified RFC 7519 compliant implementation - Document that MCP validates only its own audience (not Nextcloud's) - Add section on OAuth resource parameters and PRM discovery - Update implementation checklist to show completed items - Mark status as "Implemented" with update date ## Implementation Details The solution follows RFC 7519 Section 4.1.3: resource servers validate only their own presence in the audience claim. This simplifies the logic while maintaining security: - MCP server validates MCP audience only - Nextcloud independently validates its own audience - No dual validation required at MCP layer - Token reuse is allowed per RFC 8707 for multi-audience tokens ## Test Results ✅ test_mcp_oauth_server_connection - PASSED ✅ test_deck_board_view_permissions - PASSED ✅ test_prm_endpoint - PASSED All OAuth flows now properly specify target resources, resulting in correct audience validation throughout the system. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -867,6 +867,9 @@ def get_app(transport: str = "sse", enabled_apps: list[str] | None = None):
|
||||
mcp_server_url = os.getenv(
|
||||
"NEXTCLOUD_MCP_SERVER_URL", "http://localhost:8000"
|
||||
)
|
||||
nextcloud_resource_uri = os.getenv(
|
||||
"NEXTCLOUD_RESOURCE_URI", nextcloud_host
|
||||
)
|
||||
discovery_url = os.getenv(
|
||||
"OIDC_DISCOVERY_URL",
|
||||
f"{nextcloud_host}/.well-known/openid-configuration",
|
||||
@@ -884,6 +887,7 @@ def get_app(transport: str = "sse", enabled_apps: list[str] | None = None):
|
||||
"client_secret": client_secret, # From setup_oauth_config (DCR or static)
|
||||
"scopes": scopes,
|
||||
"nextcloud_host": nextcloud_host,
|
||||
"nextcloud_resource_uri": nextcloud_resource_uri,
|
||||
"oauth_provider": oauth_provider,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -252,6 +252,7 @@ async def oauth_authorize(request: Request) -> RedirectResponse | JSONResponse:
|
||||
"scope": scopes,
|
||||
"state": idp_state,
|
||||
"prompt": "consent", # Ensure refresh token
|
||||
"resource": f"{oauth_config['mcp_server_url']}/mcp", # MCP server audience
|
||||
}
|
||||
|
||||
auth_url = f"{authorization_endpoint}?{urlencode(idp_params)}"
|
||||
@@ -359,6 +360,7 @@ async def oauth_authorize_nextcloud(
|
||||
"state": state,
|
||||
"prompt": "consent", # Force consent to show resource access
|
||||
"access_type": "offline", # Request refresh token
|
||||
"resource": oauth_config["nextcloud_resource_uri"], # Nextcloud audience
|
||||
}
|
||||
|
||||
auth_url = f"{authorization_endpoint}?{urlencode(idp_params)}"
|
||||
|
||||
Reference in New Issue
Block a user