-
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
Fix HTTP_Status on OAuth2 refresh token redirect #15336
Fix HTTP_Status on OAuth2 refresh token redirect #15336
Conversation
core/trino-main/src/test/java/io/trino/server/ui/TestWebUi.java
Outdated
Show resolved
Hide resolved
389c3c1
to
bb62f43
Compare
@@ -772,6 +811,24 @@ private void assertAuth2Authentication(HttpServerInfo httpServerInfo, String acc | |||
assertResponseCode(client, getLocation(baseUri, "/ui/unknown"), SC_NOT_FOUND); | |||
assertResponseCode(client, getLocation(baseUri, "/ui/api/unknown"), SC_NOT_FOUND); | |||
|
|||
if (refreshTokensEnabled) { | |||
// wait till auth token expires | |||
Thread.sleep(Duration.between(ZonedDateTime.now(), authTokenExpiration).toMillis()); |
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.
Could you issue an already expired token? To get rid of this the sleep.
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.
Yep, done
String state = newJwtBuilder() | ||
.signWith(hmacShaKeyFor(Hashing.sha256().hashString(STATE_KEY, UTF_8).asBytes())) | ||
.setAudience("trino_oauth_ui") | ||
.setExpiration(Date.from(ZonedDateTime.now().plusMinutes(10).toInstant())) | ||
.setExpiration(Date.from(authTokenExpiration.toInstant())) |
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 change is probably not needed, state is only a part of the authorization challenge https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1 so its expiration shouldn't matter.
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.
Right, I mixed them up, reverted
@@ -772,6 +811,24 @@ private void assertAuth2Authentication(HttpServerInfo httpServerInfo, String acc | |||
assertResponseCode(client, getLocation(baseUri, "/ui/unknown"), SC_NOT_FOUND); | |||
assertResponseCode(client, getLocation(baseUri, "/ui/api/unknown"), SC_NOT_FOUND); | |||
|
|||
if (refreshTokensEnabled) { |
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: extract to a method?
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.
Extracted because now I use this part in two places
@@ -148,6 +152,8 @@ | |||
private static final String TEST_PASSWORD2 = "test-password2"; | |||
private static final String HMAC_KEY = Resources.getResource("hmac_key.txt").getPath(); | |||
private static final PrivateKey JWK_PRIVATE_KEY; | |||
private static final String REFRESH_TOKEN = "REFRESH_TOKEN"; | |||
private static final int REFRESH_TOKEN_TIMEOUT_MINUTES = 5; |
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: How about using a Duration
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.
Replaced
.putAll(OAUTH2_PROPERTIES) | ||
.put("http-server.authentication.oauth2.jwks-url", jwkServer.getBaseUrl().toString()) | ||
.put("http-server.authentication.oauth2.refresh-tokens", "true") | ||
.put("http-server.authentication.oauth2.refresh-tokens.issued-token.timeout", REFRESH_TOKEN_TIMEOUT_MINUTES + "m") |
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 is a timeout for the token minted by us (i.e with access token and refresh token) - can we have a test where the minted refresh token is expired ?
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 added additional test to test if it really expires: testOAuth2AuthenticatorRefreshTokenExpiration
e6fd9b0
to
523e1f3
Compare
I pushed updated version. |
523e1f3
to
9a7593c
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.
LGMT 👍
{ | ||
claims = new DefaultClaims(createClaims()); | ||
claims.putAll(additionalClaims); | ||
this.accessTokenValidity = accessTokenValidity; |
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.
rnn
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.
Added
9a7593c
to
a0241a8
Compare
I hit flaky test #15293 |
private static void assertCookieWithRefreshToken(TestingTrinoServer server, HttpCookie authCookie, String accessToken) | ||
{ | ||
TokenPairSerializer tokenPairSerializer = server.getInstance(Key.get(TokenPairSerializer.class)); | ||
TokenPairSerializer.TokenPair deserialize = tokenPairSerializer.deserialize(authCookie.getValue()); |
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: Can we import TokenPair
directly ?
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.
Replaced
HttpCookie cookie = getOnlyElement(cookieManager.getCookieStore().getCookies()); | ||
assertCookieWithRefreshToken(server, cookie, oauthClient.getAccessToken()); | ||
|
||
String location = getLocation(baseUri, "/ui/api/query/20221206_145344_00001_qe6gw/killed"); |
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 it possible for us to check with an existing API. The QueryId doesn't exists in the testing server right ?
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.
Can we use validApiLocation
here ?
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 could replace all of this with:
assertResponseCode(client, getValidApiLocation(baseUri), SC_TEMPORARY_REDIRECT);
assertOk(client, getValidApiLocation(baseUri));
but getValidApiLocation
returns an URL to GET endpoint and here I used PUT method.
Ok, I will replace that. It's much more readable.
When refresh token is retrieved for UI, currently we were sending HTTP Status 303, assuming that all the request will just repeat the call on the Location header. When this works for GET/PUT verbs, it does not for non-idempotent ones like POST, as every js http client should do a GET on LOCATION after 303 on POST. Due to that I change it to 307, that should force every client to repeat exactly the same request, no matter the verb. Co-authored-by: s2lomon <[email protected]>
a0241a8
to
fe1e1c8
Compare
Merged, thanks! |
Description
When refresh token is retrieved for UI, currently we were sending HTTP Status 303, assuming that all the request will just repeat the call on the Location header. When this works for GET/PUT verbs, it does not for non-idempotent ones like POST, as every JS HTTP client should do a GET on LOCATION after 303 on POST. Due to that I change it to 307, that should force every client to repeat the exactly the same request, no matter the verb.
This PR replaces #15184
Release notes
( ) This is not user-visible or 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: