Skip to content

Commit

Permalink
adapt spring-xsuaa
Browse files Browse the repository at this point in the history
Signed-off-by: liga-oz <[email protected]>
  • Loading branch information
liga-oz committed Nov 20, 2023
1 parent 115a37c commit 2122264
Show file tree
Hide file tree
Showing 3 changed files with 11 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -142,33 +140,22 @@ private void canVerifyWithKey(String jku, String kid, String uaadomain) {
}
List<String> 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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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://[email protected]/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";
}
}
}

0 comments on commit 2122264

Please sign in to comment.