Implement .well-known/oauth-authorization-server for OAuth2#29015
Implement .well-known/oauth-authorization-server for OAuth2#29015
Conversation
dain
left a comment
There was a problem hiding this comment.
I asked a friend that knows OIDC really well and he thought this was a bad idea, so I asked ChatGPT to analyse this also, with a neutral prompt:
In trino we can be configured to accept OIDC tokens. IIRC they have to be signed by a specific issuer and need some specific field set.. this is all configurable. There is a PR #29015 to add a ".well-known/oauth-authorization-server" endpoint to the server, but I'm not sure if this is something we should have or not.
And it came back with a strong no:
I would lean no, at least not in the form in that PR.
The core issue is that /.well-known/oauth-authorization-server is the standard metadata document for an authorization server. It tells clients where that server’s authorization endpoint, token endpoint, JWKS, and related capabilities live. RFC 8414 is explicit that this metadata is for the authorization server itself, and that the issuer in the document must exactly match the issuer whose well-known URL was queried; otherwise the metadata “MUST NOT be used.”
But Trino, in this setup, is not the authorization server. Trino is an OAuth/OIDC client / relying party / protected resource that redirects users to the real IdP and validates tokens from that IdP. Trino’s own docs describe it that way: Trino uses the authorization code flow, redirects the browser to the authorization server, exchanges the code for a token, and can itself consume OIDC discovery metadata from the IdP.
That makes the current PR look semantically wrong. The implementation serves /.well-known/oauth-authorization-server from the Trino coordinator, but fills it with the upstream IdP’s metadata, including issuer = config.getIssuer() and the upstream authorization_endpoint, token_endpoint, and jwks_uri. So a client would fetch metadata from https://trino-coordinator/... and receive an issuer like https://hydra:4444/. Per RFC 8414, that is exactly the mismatch that clients are supposed to reject.
I went on to ask about what we should be doing and it said:
The right standard is OAuth 2.0 Protected Resource Metadata, published as RFC 9728 in 2025. It is specifically for a resource server / protected resource to publish metadata about itself, including which authorization server(s) clients should use.
The default well-known endpoint is:
/.well-known/oauth-protected-resource
So for a Trino server, the standard shape would be more like:
https://trino.example.com/.well-known/oauth-protected-resource
and the metadata can include:
- resource: the resource identifier for the Trino server
- authorization_servers: one or more issuer URLs for the authorization servers clients should use
There is also a standard way to point clients at that metadata dynamically: a WWW-Authenticate challenge can include a resource_metadata parameter, telling the client where to fetch the protected-resource metadata. RFC 9728 gives exactly that flow: client hits the resource, gets WWW-Authenticate: Bearer resource_metadata="...", fetches the resource metadata, then uses the listed issuer to fetch the authorization server metadata via RFC 8414.
So the standards split is now:
- /.well-known/oauth-authorization-server = metadata for the token issuer / authorization server
- /.well-known/oauth-protected-resource = metadata for the resource server that tells clients what issuer(s) to use
For Trino, that means:
- if Trino is only accepting and validating tokens from some external IdP, then the standards-correct endpoint is oauth-protected-resource, not oauth-authorization-server
- if Trino itself were actually issuing tokens, then oauth-authorization-server would make sense
One practical caveat: RFC 9728 is new, so many OAuth/OIDC clients probably do not support it yet. The spec itself says how a client learns the resource location is out of scope and may still be manual or application-specific.
| metadata.put("response_types_supported", "code"); | ||
| serverConfig.userinfoUrl().ifPresent(uri -> metadata.put("userinfo_endpoint", uri.toString())); | ||
| serverConfig.endSessionUrl().ifPresent(uri -> metadata.put("end_session_endpoint", uri.toString())); | ||
| serverConfig.accessTokenIssuer().ifPresent(issuer -> metadata.put("access_token_issuer", issuer)); |
There was a problem hiding this comment.
This isn't a standard field by RFC 8414 sec. 3.2. The spec doesn't forbid this, but it isn't standard.
Tested with our
singlenode-oauth2endpoint which returns for this new endpoint:Release notes
( ) This is not user-visible or is docs only, and no release notes are required.
( ) Release notes are required. Please propose a release note for me.
(x) Release notes are required, with the following suggested text: