Skip to content

Commit

Permalink
Merge pull request #43417 from sberyozkin/oidc_client_expires_in
Browse files Browse the repository at this point in the history
Add OidcClient expiresIn property
  • Loading branch information
sberyozkin authored Sep 21, 2024
2 parents 78ff380 + 153d3e6 commit d66745c
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 3 deletions.
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

0 comments on commit d66745c

Please sign in to comment.