Summary
authorization_server_metadata() advertises a revocation_endpoint that is hardcoded to the issuer root ({issuer}/oauth/revoke), while authorization_endpoint, token_endpoint and registration_endpoint all accept caller-supplied overrides. When a deployment mounts the delegated-credential OAuth adapter under a Connection Hub bundle path — as the Connection Hub migration intends — the three overridable endpoints point at the bundle path and the revocation endpoint points at a route that does not exist.
Where
app/ai-app/src/kdcube-ai-app/kdcube_ai_app/apps/chat/sdk/solutions/connections/delegated_credentials/oauth/metadata.py:46
"authorization_endpoint": authorization_endpoint or f"{issuer}/oauth/authorize",
"token_endpoint": token_endpoint or f"{issuer}/oauth/token",
"registration_endpoint": registration_endpoint or f"{issuer}/oauth/register",
# no override parameter:
"revocation_endpoint": f"{issuer}/oauth/revoke",
revocation_endpoint is the only one of the four with no corresponding keyword argument in the signature, so a deployment has no way to point it at the path where the adapter is actually mounted.
Reproduction (live, platform release 2026.07.28.210)
$ curl -s https://yey.boats/.well-known/oauth-authorization-server | jq '{authorization_endpoint, token_endpoint, revocation_endpoint}'
{
"authorization_endpoint": "https://yey.boats/api/integrations/bundles/home/demo/connection-hub@1-0/public/oauth/authorize",
"token_endpoint": "https://yey.boats/api/integrations/bundles/home/demo/connection-hub@1-0/public/oauth/token",
"revocation_endpoint": "https://yey.boats/oauth/revoke"
}
$ curl -s -o /dev/null -w '%{http_code}\n' https://yey.boats/oauth/revoke
404
$ curl -s https://yey.boats/oauth/revoke
{"error":"oauth_route_not_found","path":"revoke"}
Authorize/token/register resolve under the bundle path; revoke does not. Root /oauth/* routing was intentionally dropped once clients moved to the Connection Hub endpoints, so the advertised root path is dead.
Impact
RFC 7009 revocation is unreachable for any deployment that mounts the adapter under a bundle path. The docstring at that line states the intent — "a disconnecting client revokes its token here, which also retires its Connection Hub card (no orphan)" — so a client disconnecting a connector cannot retire its grant, and the Connection Hub card is orphaned. Tokens stay valid until natural expiry (access 1h, refresh 180d per oauth/store.py:26).
This is metadata-only; it does not affect the authorization-code or refresh flows.
Suggested fix
Give revocation_endpoint the same override treatment as its three siblings:
def authorization_server_metadata(
issuer: str,
*,
authorization_endpoint: str | None = None,
token_endpoint: str | None = None,
registration_endpoint: str | None = None,
revocation_endpoint: str | None = None,
...
) -> Dict[str, Any]:
...
"revocation_endpoint": revocation_endpoint or f"{issuer}/oauth/revoke",
and pass the bundle-mounted path from the Connection Hub call site alongside the other three. A test in oauth/tests/test_discovery.py asserting all four endpoints honor their overrides would keep them from drifting apart again.
Summary
authorization_server_metadata()advertises arevocation_endpointthat is hardcoded to the issuer root ({issuer}/oauth/revoke), whileauthorization_endpoint,token_endpointandregistration_endpointall accept caller-supplied overrides. When a deployment mounts the delegated-credential OAuth adapter under a Connection Hub bundle path — as the Connection Hub migration intends — the three overridable endpoints point at the bundle path and the revocation endpoint points at a route that does not exist.Where
app/ai-app/src/kdcube-ai-app/kdcube_ai_app/apps/chat/sdk/solutions/connections/delegated_credentials/oauth/metadata.py:46revocation_endpointis the only one of the four with no corresponding keyword argument in the signature, so a deployment has no way to point it at the path where the adapter is actually mounted.Reproduction (live, platform release 2026.07.28.210)
Authorize/token/register resolve under the bundle path; revoke does not. Root
/oauth/*routing was intentionally dropped once clients moved to the Connection Hub endpoints, so the advertised root path is dead.Impact
RFC 7009 revocation is unreachable for any deployment that mounts the adapter under a bundle path. The docstring at that line states the intent — "a disconnecting client revokes its token here, which also retires its Connection Hub card (no orphan)" — so a client disconnecting a connector cannot retire its grant, and the Connection Hub card is orphaned. Tokens stay valid until natural expiry (access 1h, refresh 180d per
oauth/store.py:26).This is metadata-only; it does not affect the authorization-code or refresh flows.
Suggested fix
Give
revocation_endpointthe same override treatment as its three siblings:and pass the bundle-mounted path from the Connection Hub call site alongside the other three. A test in
oauth/tests/test_discovery.pyasserting all four endpoints honor their overrides would keep them from drifting apart again.