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

Add OidcClient expiresIn property #43417

Merged
merged 1 commit into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,22 @@ public class OidcClientConfig extends OidcClientCommonConfig {
public Optional<List<String>> scopes = Optional.empty();

/**
* Refresh token time skew in seconds.
* If this property is enabled then the configured number of seconds is added to the current time
* Refresh token time skew.
* If this property is enabled then the configured duration is converted to seconds and is added to the current time
* when checking whether the access token should be refreshed. If the sum is greater than this access token's
* expiration time then a refresh is going to happen.
*/
@ConfigItem
public Optional<Duration> refreshTokenTimeSkew = Optional.empty();

/**
* Access token expiration period relative to the current time.
* This property is only checked when an access token grant response
* does not include an access token expiration property.
*/
@ConfigItem
public Optional<Duration> accessTokenExpiresIn = Optional.empty();

/**
* If the access token 'expires_in' property should be checked as an absolute time value
* as opposed to a duration relative to the current time.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ private Tokens emitGrantTokens(OidcRequestContextProperties requestProps, HttpRe
JsonObject json = buffer.toJsonObject();
// access token
final String accessToken = json.getString(oidcConfig.grant.accessTokenProperty);
final Long accessTokenExpiresAt = getExpiresAtValue(accessToken, json.getValue(oidcConfig.grant.expiresInProperty));
final Long accessTokenExpiresAt = getAccessTokenExpiresAtValue(accessToken,
json.getValue(oidcConfig.grant.expiresInProperty));

final String refreshToken = json.getString(oidcConfig.grant.refreshTokenProperty);
final Long refreshTokenExpiresAt = getExpiresAtValue(refreshToken,
Expand All @@ -261,6 +262,15 @@ private Tokens emitGrantTokens(OidcRequestContextProperties requestProps, HttpRe
}
}

private Long getAccessTokenExpiresAtValue(String token, Object expiresInValue) {
Long expiresAt = getExpiresAtValue(token, expiresInValue);
if (expiresAt == null && oidcConfig.accessTokenExpiresIn.isPresent()) {
final long now = System.currentTimeMillis() / 1000;
expiresAt = now + oidcConfig.accessTokenExpiresIn.get().toSeconds();
}
return expiresAt;
}

private Long getExpiresAtValue(String token, Object expiresInValue) {
if (expiresInValue != null) {
long tokenExpiresIn = expiresInValue instanceof Number ? ((Number) expiresInValue).longValue()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public class FrontendResource {
@NamedOidcClient("non-standard-response")
Tokens tokens;

@Inject
@NamedOidcClient("configured-expires-in")
Tokens tokensConfiguredExpiresIn;

@Inject
@NamedOidcClient("non-standard-response-without-header")
OidcClient tokensWithoutHeader;
Expand Down Expand Up @@ -82,6 +86,16 @@ public String echoTokenNonStandardResponse() {
}
}

@GET
@Path("echoTokenConfiguredExpiresIn")
public String echoTokenConfiguredExpiresIn() {
try {
return tokensConfiguredExpiresIn.getAccessToken() + " " + tokensConfiguredExpiresIn.getAccessTokenExpiresAt();
} catch (OidcClientException ex) {
throw new WebApplicationException(401);
}
}

@GET
@Path("echoTokenNonStandardResponseWithoutHeader")
public Uni<Tokens> echoTokenNonStandardResponseWithoutHeader() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ quarkus.oidc-client.grant.type=password
quarkus.oidc-client.grant-options.password.username=alice
quarkus.oidc-client.grant-options.password.password=alice

quarkus.oidc-client.configured-expires-in.token-path=${keycloak.url}/tokens-without-expires-in
quarkus.oidc-client.configured-expires-in.client-id=quarkus-app
quarkus.oidc-client.configured-expires-in.credentials.client-secret.value=secret
quarkus.oidc-client.configured-expires-in.credentials.client-secret.method=post
quarkus.oidc-client.configured-expires-in.access-token-expires-in=5S

quarkus.oidc-client.jwtbearer.auth-server-url=${keycloak.url}
quarkus.oidc-client.jwtbearer.discovery-enabled=false
quarkus.oidc-client.jwtbearer.token-path=/tokens-jwtbearer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ public Map<String, String> start() {
.withBody(
"{\"access_token\":\"access_token_2\", \"expires_in\":4, \"refresh_token\":\"refresh_token_2\", \"refresh_expires_in\":1}")));

server.stubFor(WireMock.post("/tokens-without-expires-in")
.withRequestBody(matching("grant_type=client_credentials&client_id=quarkus-app&client_secret=secret"))
.willReturn(WireMock
.aResponse()
.withHeader("Content-Type", MediaType.APPLICATION_JSON)
.withBody(
"{\"access_token\":\"access_token_without_expires_in\"}")));

server.stubFor(WireMock.post("/refresh-token-only")
.withRequestBody(
matching("grant_type=refresh_token&refresh_token=shared_refresh_token&extra_param=extra_param_value"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static org.awaitility.Awaitility.given;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
Expand All @@ -28,6 +29,7 @@
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
import io.restassured.response.Response;

@QuarkusTest
@QuarkusTestResource(KeycloakRealmResourceManager.class)
Expand All @@ -44,6 +46,21 @@ public void testEchoTokensJwtBearerAuthentication() {
.body(equalTo("access_token_jwt_bearer"));
}

@Test
public void testGetAccessTokenWithConfiguredExpiresIn() {
Response r = RestAssured.when().get("/frontend/echoTokenConfiguredExpiresIn");
assertEquals(200, r.statusCode());
String[] data = r.body().asString().split(" ");
assertEquals(2, data.length);
assertEquals("access_token_without_expires_in", data[0]);

long now = System.currentTimeMillis() / 1000;
long expectedExpiresAt = now + 5;
long accessTokenExpiresAt = Long.valueOf(data[1]);
assertTrue(accessTokenExpiresAt >= expectedExpiresAt
&& accessTokenExpiresAt <= expectedExpiresAt + 2);
}

@Test
public void testEchoTokensJwtBearerGrant() {
RestAssured.when().get("/frontend/echoTokenJwtBearerGrant")
Expand Down