Skip to content

Commit

Permalink
access token IP check disabling
Browse files Browse the repository at this point in the history
Signed-off-by: David Kral <david.k.kral@oracle.com>
  • Loading branch information
Verdent committed Feb 21, 2024
1 parent 2c67411 commit 3d91db0
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ public final class OidcConfig extends TenantConfigImpl {
private final OidcCookieHandler stateCookieHandler;
private final boolean tokenSignatureValidation;
private final boolean idTokenSignatureValidation;
private final boolean accessTokenIpCheck;

private OidcConfig(Builder builder) {
super(builder);
Expand Down Expand Up @@ -433,6 +434,7 @@ private OidcConfig(Builder builder) {
this.stateCookieHandler = builder.stateCookieBuilder.build();
this.tokenSignatureValidation = builder.tokenSignatureValidation;
this.idTokenSignatureValidation = builder.idTokenSignatureValidation;
this.accessTokenIpCheck = builder.accessTokenIpCheck;

this.webClientBuilderSupplier = builder.webClientBuilderSupplier;
this.defaultTenant = LazyValue.create(() -> Tenant.create(this, this));
Expand Down Expand Up @@ -816,6 +818,15 @@ public boolean idTokenSignatureValidation() {
return idTokenSignatureValidation;
}

/**
* Whether to check IP address access token was issued for.
*
* @return whether to check IP address access token was issued for
*/
public boolean accessTokenIpCheck() {
return accessTokenIpCheck;
}

Supplier<WebClientConfig.Builder> webClientBuilderSupplier() {
return webClientBuilderSupplier;
}
Expand Down Expand Up @@ -946,6 +957,7 @@ public static class Builder extends BaseBuilder<Builder, OidcConfig> {
private boolean relativeUris = DEFAULT_RELATIVE_URIS;
private boolean tokenSignatureValidation = true;
private boolean idTokenSignatureValidation = true;
private boolean accessTokenIpCheck = true;

protected Builder() {
}
Expand Down Expand Up @@ -1070,6 +1082,7 @@ public Builder config(Config config) {

config.get("token-signature-validation").asBoolean().ifPresent(this::tokenSignatureValidation);
config.get("id-token-signature-validation").asBoolean().ifPresent(this::idTokenSignatureValidation);
config.get("access-token-ip-check").asBoolean().ifPresent(this::accessTokenIpCheck);

config.get("tenants").asList(Config.class)
.ifPresent(confList -> confList.forEach(tenantConfig -> tenantFromConfig(config, tenantConfig)));
Expand Down Expand Up @@ -1720,5 +1733,18 @@ public Builder idTokenSignatureValidation(boolean enabled) {
return this;
}

/**
* Whether to check if current IP address matches the one access token was issued for.
* This check helps with cookie replay attack prevention.
*
* @param enabled whether to check if current IP address matches the one access token was issued for
* @return updated builder instance
*/
@ConfiguredOption("true")
public Builder accessTokenIpCheck(boolean enabled) {
accessTokenIpCheck = enabled;
return this;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -266,17 +266,19 @@ private AuthenticationResponse processAccessToken(String tenantId, ProviderReque
String tokenValue = cookie.get();
String decodedJson = new String(Base64.getDecoder().decode(tokenValue), StandardCharsets.UTF_8);
JsonObject jsonObject = JSON_READER_FACTORY.createReader(new StringReader(decodedJson)).readObject();
Object userIp = providerRequest.env().abacAttribute("userIp").orElseThrow();
if (!jsonObject.getString("remotePeer").equals(userIp)) {
if (LOGGER.isLoggable(System.Logger.Level.DEBUG)) {
LOGGER.log(System.Logger.Level.DEBUG,
"Current peer IP does not match the one this access token was issued for");
if (oidcConfig.accessTokenIpCheck()) {
Object userIp = providerRequest.env().abacAttribute("userIp").orElseThrow();
if (!jsonObject.getString("remotePeer").equals(userIp)) {
if (LOGGER.isLoggable(System.Logger.Level.DEBUG)) {
LOGGER.log(System.Logger.Level.DEBUG,
"Current peer IP does not match the one this access token was issued for");
}
return errorResponse(providerRequest,
Status.UNAUTHORIZED_401,
"peer_host_mismatch",
"Peer host access token mismatch",
tenantId);
}
return errorResponse(providerRequest,
Status.UNAUTHORIZED_401,
"peer_host_mismatch",
"Peer host access token mismatch",
tenantId);
}
return validateAccessToken(tenantId, providerRequest, jsonObject.getString("accessToken"), idToken);
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public void testNotMatchingNonce(WebTarget webTarget) {

@Test
@AddConfig(key = "security.providers.1.oidc.cookie-encryption-enabled", value = "false")
public void testAuthenticationWithoutIdToken(WebTarget webTarget) {
public void testAccessTokenIssuedIp(WebTarget webTarget) {
List<String> setCookies = obtainCookies(webTarget);

//Ignore ID token cookie
Expand Down Expand Up @@ -269,4 +269,36 @@ public void testAuthenticationWithoutIdToken(WebTarget webTarget) {
}
}

@Test
@AddConfig(key = "security.providers.1.oidc.cookie-encryption-enabled", value = "false")
@AddConfig(key = "security.providers.1.oidc.access-token-ip-check", value = "false")
public void testDisabledAccessTokenIssuedIp(WebTarget webTarget) {
List<String> setCookies = obtainCookies(webTarget);

//Ignore ID token cookie
Invocation.Builder request = client2.target(webTarget.getUri()).path("/test").request();
for (String setCookie : setCookies) {
if (!setCookie.startsWith(DEFAULT_COOKIE_NAME + "=")) {
request.header(HttpHeaders.COOKIE, setCookie);
} else {
String encodedJson = setCookie.substring(setCookie.indexOf("=") + 1, setCookie.indexOf(";"));
String json = new String(Base64.getDecoder().decode(encodedJson), StandardCharsets.UTF_8);
JsonObject jsonObject = JSON_READER_FACTORY.createReader(new StringReader(json)).readObject();
JsonObject recreatedJsonObject = JSON_OBJECT_BUILDER_FACTORY.createObjectBuilder(jsonObject)
.add("remotePeer", "1.1.1.1") //some other address than localhost
.build();
String base64 = Base64.getEncoder()
.encodeToString(recreatedJsonObject.toString().getBytes(StandardCharsets.UTF_8));
request.header(HttpHeaders.COOKIE, DEFAULT_COOKIE_NAME + "=" + base64);
}
}

try (Response response = request
.property(ClientProperties.FOLLOW_REDIRECTS, false)
.get()) {
//This means, remote peer was not the same as our IP. We are getting redirected to key cloak again
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
}
}

}

0 comments on commit 3d91db0

Please sign in to comment.