-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Rewrite OIDC implementation #8641
Conversation
7432889
to
624fc4d
Compare
251b945
to
f48bd09
Compare
{ | ||
return service.parseClaimsJws(jws); | ||
Map<String, Object> claims = service.convertTokenToClaims(token); | ||
if (claims.containsKey(principalField)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can drop the wrapping if
statement here. The Optional.ofNullable(claims.get(principalField))
will be an empty optional if the field is missing.
Also the String.class::cast
can fail if you have the wrong type... you'll get a class cast exception. If that is not what you want, consider filter
, using map(Object::toString)
or check the type and throw a more specific exception. My guess the cast is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the runtime exception is fine, the only time that will happen is if they misconfigure the principal field to point at an array or object which are not common.
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Authenticator.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2CallbackResource.java
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Config.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Config.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Config.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Service.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Service.java
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Service.java
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Service.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Service.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Service.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Service.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/ui/OAuth2WebUiAuthenticationFilter.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Config.java
Show resolved
Hide resolved
77e4a39
to
b46f60b
Compare
@@ -26,6 +26,7 @@ | |||
protected void setup(Binder binder) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix OIDC implementation to resolve correctness issues
Can you please explain what was the issue about?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed the commit message to be clearer. Specifically the commit fixes several structural issues where there were inappropriate dependencies and duplicate/confusing code. Additionally there are several security fixes including correctly validating ID and access tokens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a new commit message yet.
Specifically the commit fixes several structural issues where there were inappropriate dependencies and duplicate/confusing code.
Let's have each of the issue handled in separate commit, currently it is hard to follow what is fixed where.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I worked with Nik on this PR and agreed to the current commit structure.
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Config.java
Outdated
Show resolved
Hide resolved
} | ||
|
||
@Override | ||
protected AuthenticationException needAuthentication(ContainerRequestContext request, String message) | ||
{ | ||
UUID authId = UUID.randomUUID(); | ||
URI redirectUri = service.startRestChallenge(request.getUriInfo().getBaseUri().resolve(CALLBACK_ENDPOINT), authId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is changing the protocol needed to solve the "correctness issues"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not change the protocol. It simply changes the x_redirect_server
URL returned in the authenticate header, but the protocol is completely unchanged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Protocol is not changed as there is no needed change in the client.
However there is a new round trip. And there is new endpoint method created. Commit message does not explain the motivation behind it. Previously we were starting the challenge at at the first call, now it is postponed and it is started once the new endpoint method is called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, and we should have always done that. It lets us use a nonce to protect the authentication code. Also in the future, we can use this to timeout clients faster... basically if a client doesn't hit the initiate in a few seconds, we kill the auth because likely the browser did not launch.
@11xor6 in the commit message mention the new use of nonce cookies for query clients.
@@ -43,6 +43,7 @@ | |||
private String authUrl; | |||
private String tokenUrl; | |||
private String jwksUrl; | |||
private String userinfoUrl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we log a warning when this field is not set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This field is not required, if it's specified we use it to validate access tokens, otherwise we treat access tokens as JWTs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like @lukasz-walkiewicz to do a review of this before merging.
b46f60b
to
c581e04
Compare
feef632
to
27ebe5a
Compare
799ac9b
to
17e0817
Compare
Please notice #8756, oauth2 product tests started to fail deterministically. We won't be able to merge this pull request without fixing product tests first. @lukasz-walkiewicz Did you have a chance to understand why it started to fail? |
17e0817
to
3c7392d
Compare
3b30853
to
34f07a2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good although I'm not sure I would agree with:
I'll add a config item for the access token issuer. However that said Authentication by definition needs to be as strict as possible.
I would say that it should be as strict as possible by default but in this case there should be a way to relax this check in order to make some unusual providers work. AD FS is just an example of IdP which would not work with the issuer check you implemented. I wouldn't be surprised if there are cases like this. Especially since access token format is not really defined in OAuth 2.0 spec and JWT spec even states that iss
is optional.
That said, I'm fine with the current solution. Thanks for bringing support for opaque tokens to Trino 👍
this.scopes = oauth2Config.getScopes(); | ||
this.challengeTimeout = Duration.ofMillis(oauth2Config.getChallengeTimeout().toMillis()); | ||
this.stateHmac = oauth2Config.getStateKey() | ||
.map(key -> sha256().hashString(key, UTF_8).asBytes()) | ||
.orElseGet(() -> secureRandomBytes(32)); | ||
|
||
this.issuer = oauth2Config.getIssuer(); | ||
this.accessTokenIssuer = oauth2Config.getAccessTokenIssuer().orElse(issuer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think it would be better to move this fallback to the config class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally had it written that way, however I was asked to write it this way instead.
|
||
public interface OAuth2TokenHandler | ||
{ | ||
void setAccessToken(String hashedState, String accessToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I would say it's a hashed authId.
core/trino-main/src/main/java/io/trino/server/security/oauth2/OAuth2Config.java
Show resolved
Hide resolved
This includes: * Properly validating ID and access tokens * A single point of entry for initiating OAuth2 flows * Add an OAuth2 initiation endpoint for query clients which allows use of a nonce cookie in token exchange. * Breaking a hard dependency between OAuth2TokenExchange and OAuth2Service * Use a hashed UUID/UUID pair in token exchange to prevent a comprimised browser from obtaining the access token * Support trusting additional audiences in access tokens
34f07a2
to
1384f52
Compare
@Config("http-server.authentication.oauth2.audience") | ||
@ConfigDescription("The required audience of a token") | ||
public OAuth2Config setAudience(String audience) | ||
@LegacyConfig("http-server.authentication.oauth2.audience") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed this during review. this should be something like below (if the configs are indeed equivalent).
@LegacyConfig(value = "http-server.authentication.oauth2.audience", replacedBy = "http-server.authentication.oauth2.additional-audiences")
Otherwise old config files will cause Trino startup to fail with an error like:
io.airlift.bootstrap.ApplicationConfigurationException:
Configuration errors:
1) Error: Invalid configuration property http-server.authentication.oauth2.issuer: may not be null (for class OAuth2Config.issuer)
2) Error: Configuration property 'http-server.authentication.oauth2.audience' has been replaced. Use 'http-server.authentication.oauth2.additional-audiences' instead.
This should also be mentioned in the release notes and documentation. cc: @11xor6 @kokosing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the configs are not interchangeable then the existing code looks good to me.
Opaque tokens are supported since 361: trinodb#8641
Opaque tokens are supported since 361: #8641
Opaque tokens are supported since 361: trinodb#8641
No description provided.