Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REST: assume issued token type is access token #10314

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

adutra
Copy link
Contributor

@adutra adutra commented May 11, 2024

The REST client wrongly assumes that the issued_token_type field is present in all OAuth responses, but that isn't true: e.g. in the client_credentials flow, this field is undefined. See RFC 6749, section 4.4.3:

https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.3

This causes the client to crash when creating a tokens exchange request, since the issued token type becomes the request's subject token type, which is mandatory.

This has been verified against a Keycloak 24.0 server.

This change fixes this issue by assuming that the issued token type is an access token, if the response did not specify any token type.

This change also fixes RESTCatalogAdapter: it was incorrectly including the issued_token_type field in client_credentials responses, thus masking many test failures, e.g. in testCatalogTokenRefresh.

The REST client wrongly assumes that the `issued_token_type`
field is present in all OAuth responses, but that isn't
true: e.g. in the `client_credentials` flow, this field is
undefined. See RFC 6749, section 4.4.3:

https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.3

This causes the client to crash when creating a tokens
exchange request, since the issued token type becomes the
request's subject token type, which is mandatory.

This has been verified against a Keycloak 24.0 server.

This change fixes this issue by assuming that the issued
token type is an access token, if the response did not
specify any token type.

This change also fixes `RESTCatalogAdapter`: it was
incorrectly including the `issued_token_type` field in
`client_credentials` responses, thus masking many test
failures, e.g. in `testCatalogTokenRefresh`.
@@ -763,11 +763,19 @@ private static AuthSession fromTokenResponse(
long startTimeMillis,
AuthSession parent,
String credential) {
// issued token type is not present in every OAuth2 response:
// assume type is access token if none provided.
// See https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adutra do you maybe know if Keycloak fully supports RFC 8693? The token exchange follows https://www.rfc-editor.org/rfc/rfc8693#name-successful-response, where issued_token_type is required

Copy link
Contributor

@nastra nastra May 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found keycloak/keycloak#26502, which states unfortunately that Keycloak deviates from the standard.
I can see that we might add the null check as a workaround for such cases where the auth server doesn't send back an issued_token_type but I'd like to first see what other people in the community think about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, Keycloak does not fully implement it. Quoting from their Securing Applications and Services Guide:

Token exchange in Keycloak is a very loose implementation of the OAuth Token Exchange specification at the IETF. We have extended it a little, ignored some of it, and loosely interpreted other parts of the specification. It is a simple grant type invocation on a realm’s OpenID Connect token endpoint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the problem here is wider: issued_token_type is only defined for the token exchange flow (RFC 8693). This field does not exist for standard flows from RFC 6749. So the following scenario can happen even with fully-compliant servers:

  1. client uses client_credentials to authenticate initially;
  2. server sends a successful response similar to this one; note that it does not contain issued_token_type, which is normal since it's not defined for this flow.
  3. client attempts to refresh the token using the token exchange flow;
  4. client throws IAE because subjectTokenType is null.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nastra also see this comment which gives some context on why we have the problem today: #4771 (comment)

@@ -254,7 +254,6 @@ private static OAuthTokenResponse handleOAuthRequest(Object body) {
case "client_credentials":
return OAuthTokenResponse.builder()
.withToken("client-credentials-token:sub=" + request.get("client_id"))
.withIssuedTokenType("urn:ietf:params:oauth:token-type:access_token")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably leave this as-is, since issued_token_type is required according to RFC 8693

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree: this flow is not governed by RFC 8693, this is RFC 6749 section 4.4.

@adutra
Copy link
Contributor Author

adutra commented May 27, 2024

Hi @nastra any updates on this PR?

FYI here is an example of error from a Spark SQL session connected to REST catalog + external auth server:

24/05/26 21:39:19 WARN Tasks: Retrying task after failure: Invalid token type: null
java.lang.IllegalArgumentException: Invalid token type: null
        at org.apache.iceberg.relocated.com.google.common.base.Preconditions.checkArgument(Preconditions.java:218)
        at org.apache.iceberg.rest.auth.OAuth2Util.tokenExchangeRequest(OAuth2Util.java:244)
        at org.apache.iceberg.rest.auth.OAuth2Util.tokenExchangeRequest(OAuth2Util.java:235)
        at org.apache.iceberg.rest.auth.OAuth2Util.refreshToken(OAuth2Util.java:140)

@nastra
Copy link
Contributor

nastra commented May 27, 2024

@adutra I'm OOO this week, maybe @amogh-jahagirdar gets a chance to look at this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants