From 2122264897294cf6880eca29f50530afc5cd628e Mon Sep 17 00:00:00 2001 From: liga-oz Date: Mon, 20 Nov 2023 19:11:28 +0100 Subject: [PATCH] adapt spring-xsuaa Signed-off-by: liga-oz --- .../api/MockXsuaaServerConfiguration.java | 2 +- .../token/authentication/XsuaaJwtDecoder.java | 33 +++----- .../authentication/XsuaaJwtDecoderTest.java | 83 ------------------- 3 files changed, 11 insertions(+), 107 deletions(-) diff --git a/spring-xsuaa-it/src/test/java/testservice/api/MockXsuaaServerConfiguration.java b/spring-xsuaa-it/src/test/java/testservice/api/MockXsuaaServerConfiguration.java index c39c490c7..ffcc39e50 100644 --- a/spring-xsuaa-it/src/test/java/testservice/api/MockXsuaaServerConfiguration.java +++ b/spring-xsuaa-it/src/test/java/testservice/api/MockXsuaaServerConfiguration.java @@ -7,7 +7,7 @@ import java.io.IOException; @TestPropertySource(properties = { "xsuaa.xsappname=java-hello-world", "xsuaa.clientid=sb-java-hello-world", - "xsuaa.url=http://localhost:33195", "xsuaa.uaadomain=localhost" }) + "xsuaa.url=http://localhost:33195", "xsuaa.uaadomain=http://localhost:33195" }) public class MockXsuaaServerConfiguration { private static final int DEFAULT_PORT = 33195; private static MockWebServer server; diff --git a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoder.java b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoder.java index eb5156b97..94a4775d2 100644 --- a/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoder.java +++ b/spring-xsuaa/src/main/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoder.java @@ -8,6 +8,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.nimbusds.jwt.JWT; import com.nimbusds.jwt.JWTParser; +import com.sap.cloud.security.config.ServiceConstants; import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; import org.json.JSONObject; import org.slf4j.Logger; @@ -20,8 +21,6 @@ import org.springframework.util.Assert; import org.springframework.web.client.RestOperations; -import java.net.URI; -import java.net.URISyntaxException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.interfaces.RSAPublicKey; @@ -127,8 +126,7 @@ private Jwt verifyToken(JWT jwt) { private Jwt verifyToken(String token, String jku, String kid, String uaaDomain) { try { canVerifyWithKey(jku, kid, uaaDomain); - validateJku(jku, uaaDomain); - return verifyWithKey(token, jku, kid); + return verifyWithKey(token, composeJku(uaaDomain), kid); } catch (JwtValidationException ex) { throw ex; } catch (JwtException ex) { @@ -142,33 +140,22 @@ private void canVerifyWithKey(String jku, String kid, String uaadomain) { } List nullParams = new ArrayList<>(); if (jku == null) - nullParams.add("jku"); + nullParams.add(CLAIM_JKU); if (kid == null) - nullParams.add("kid"); + nullParams.add(CLAIM_KID); if (uaadomain == null) - nullParams.add("uaadomain"); + nullParams.add(ServiceConstants.XSUAA.UAA_DOMAIN); throw new BadJwtException(String.format("Cannot verify with online token key, %s is null", String.join(", ", nullParams))); } - private void validateJku(String jku, String uaadomain) { - try { - URI jkuUri = new URI(jku); - if (jkuUri.getHost() == null) { - throw new BadJwtException("JKU of token is not valid"); - } else if (!jkuUri.getHost().endsWith(uaadomain)) { - logger.warn("Error: Do not trust jku '{}' because it does not match uaa domain '{}'.", - jku, uaadomain); - throw new BadJwtException("Do not trust 'jku' token header."); - } else if (!jkuUri.getPath().endsWith("token_keys") || hasText(jkuUri.getQuery()) - || hasText(jkuUri.getFragment())) { - logger.warn("Error: Do not trust jku '{}' because it contains invalid path, query or fragment.", jku); - throw new BadJwtException("Jwt token does not contain a valid 'jku' header parameter: " + jkuUri); - } - } catch (URISyntaxException e) { - throw new BadJwtException("JKU of token header is not valid"); + private String composeJku(String uaaDomain) { + // uaaDomain in configuration is always without a schema, but for testing purpose http schema can be used + if (uaaDomain.startsWith("http://")){ + return uaaDomain + "/token_keys"; } + return "https://" + uaaDomain + "/token_keys"; } @java.lang.SuppressWarnings("squid:S2259") diff --git a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderTest.java b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderTest.java index 506438f8c..286b3ff72 100644 --- a/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderTest.java +++ b/spring-xsuaa/src/test/java/com/sap/cloud/security/xsuaa/token/authentication/XsuaaJwtDecoderTest.java @@ -5,7 +5,6 @@ */ package com.sap.cloud.security.xsuaa.token.authentication; -import com.nimbusds.jwt.JWT; import com.sap.cloud.security.xsuaa.XsuaaCredentials; import com.sap.cloud.security.xsuaa.XsuaaServiceConfiguration; import com.sap.cloud.security.xsuaa.XsuaaServiceConfigurationCustom; @@ -128,86 +127,4 @@ public void decode_withNonMatchingVerificationKey_throwsException() { .hasMessageContaining("Cannot verify with online token key, jku, kid, uaadomain is null"); } - @Test - public void decode_whenJwksContainsInvalidJwksDomain_throwsException() throws IOException { - String token = IOUtils.resourceToString("/token_user.txt", StandardCharsets.UTF_8); - XsuaaJwtDecoder cut = (XsuaaJwtDecoder) new XsuaaJwtDecoderBuilder(configuration).build(); - - cut.setTokenInfoExtractor(new TokenInfoExtractorImpl("https://subdomain.wrongoauth.ondemand.com/token_keys")); - assertThatThrownBy(() -> cut.decode(token)).isInstanceOf(JwtException.class) - .hasMessageContaining("JWT verification failed: Do not trust 'jku' token header"); - - cut.setTokenInfoExtractor( - new TokenInfoExtractorImpl("http://myauth.ondemand.com@malicious.ondemand.com/token_keys")); - assertThatThrownBy(() -> cut.decode(token)).isInstanceOf(JwtException.class) - .hasMessageContaining("JWT verification failed: Do not trust 'jku' token header"); - - cut.setTokenInfoExtractor(new TokenInfoExtractorImpl( - "http://malicious.ondemand.com/token_keys///myauth.ondemand.com/token_keys")); - assertThatThrownBy(() -> cut.decode(token)).isInstanceOf(JwtException.class) - .hasMessageContaining("JWT verification failed: Do not trust 'jku' token header"); - } - - @Test - public void decode_whenJwksUrlIsNotValid_throwsException() { - XsuaaJwtDecoder cut = (XsuaaJwtDecoder) new XsuaaJwtDecoderBuilder(configuration).build(); - - cut.setTokenInfoExtractor( - new TokenInfoExtractorImpl("http://myauth.ondemand.com\\@malicious.ondemand.com/token_keys")); - assertThatThrownBy(() -> cut.decode(ccToken)).isInstanceOf(JwtException.class) - .hasMessageContaining("JWT verification failed: JKU of token header is not valid"); - } - - @Test - public void decode_whenJwksContainsInvalidPath_throwsException() { - XsuaaJwtDecoder cut = (XsuaaJwtDecoder) new XsuaaJwtDecoderBuilder(configuration).build(); - cut.setTokenInfoExtractor(new TokenInfoExtractorImpl("https://subdomain.myauth.ondemand.com/wrong_endpoint")); - - assertThatThrownBy(() -> cut.decode(ccToken)).isInstanceOf(JwtException.class) - .hasMessageContaining("Jwt token does not contain a valid 'jku' header parameter"); - } - - @Test - public void decode_whenJwksContainQueryParameters_throwsException() { - XsuaaJwtDecoder cut = (XsuaaJwtDecoder) new XsuaaJwtDecoderBuilder(configuration).build(); - cut.setTokenInfoExtractor(new TokenInfoExtractorImpl("https://subdomain.myauth.ondemand.com/token_keys?a=b")); - - assertThatThrownBy(() -> cut.decode(ccToken)).isInstanceOf(JwtException.class) - .hasMessageContaining("Jwt token does not contain a valid 'jku' header parameter: "); - - } - - @Test - public void decode_whenJwksContainsFragment_throwsException() { - XsuaaJwtDecoder cut = (XsuaaJwtDecoder) new XsuaaJwtDecoderBuilder(configuration).build(); - cut.setTokenInfoExtractor( - new TokenInfoExtractorImpl("https://subdomain.myauth.ondemand.com/token_keys#token_keys")); - - assertThatThrownBy(() -> cut.decode(ccToken)).isInstanceOf(JwtException.class) - .hasMessageContaining("Jwt token does not contain a valid 'jku' header parameter:"); - } - - private static class TokenInfoExtractorImpl - implements com.sap.cloud.security.xsuaa.token.authentication.TokenInfoExtractor { - private final String jku; - - public TokenInfoExtractorImpl(String jku) { - this.jku = jku; - } - - @Override - public String getJku(JWT jwt) { - return jku; - } - - @Override - public String getKid(JWT jwt) { - return "kid"; - } - - @Override - public String getUaaDomain(JWT jwt) { - return "myauth.ondemand.com"; - } - } } \ No newline at end of file