From aee82de2396451f6d2cb6a7d59cd0bb598041ba6 Mon Sep 17 00:00:00 2001 From: Liu Woon Yung Date: Fri, 6 Dec 2024 14:04:17 +0800 Subject: [PATCH] Change validation of OIDC token issuer to support parameterized queries (#818) --- .../modules/oidc/JwtHandler.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/openam-authentication/openam-auth-oidc/src/main/java/org/forgerock/openam/authentication/modules/oidc/JwtHandler.java b/openam-authentication/openam-auth-oidc/src/main/java/org/forgerock/openam/authentication/modules/oidc/JwtHandler.java index fddd7a553a..703e101006 100644 --- a/openam-authentication/openam-auth-oidc/src/main/java/org/forgerock/openam/authentication/modules/oidc/JwtHandler.java +++ b/openam-authentication/openam-auth-oidc/src/main/java/org/forgerock/openam/authentication/modules/oidc/JwtHandler.java @@ -32,6 +32,8 @@ import org.forgerock.util.Reject; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.Set; import static org.forgerock.openam.authentication.modules.oidc.OpenIdConnectConfig.*; @@ -68,7 +70,7 @@ public JwtClaimsSet validateJwt(String jwtValue) throws AuthLoginException { final SignedJwt signedJwt = getSignedJwt(jwtValue); JwtClaimsSet jwtClaimSet = signedJwt.getClaimsSet(); final String jwtClaimSetIssuer = jwtClaimSet.getIssuer(); - if (!config.getConfiguredIssuer().equals(jwtClaimSetIssuer)) { + if (!config.getConfiguredIssuer().equals(jwtClaimSetIssuer) && !isJwtFromIssuerFormat(jwtClaimSet)) { logger.error("The issuer configured for the module, " + config.getConfiguredIssuer() + ", and the " + "issuer found in the token, " + jwtClaimSetIssuer + ", do not match. This means that the token " + "authentication was directed at the wrong module, or the targeted module is mis-configured."); @@ -136,6 +138,30 @@ public JwtClaimsSet validateJwt(String jwtValue) throws AuthLoginException { return jwtClaimSet; } + /** + * Indicates whether the JWT token is issued by configured issuer, with parameterized substitution from the claims set. + * Example: https://login.microsoftonline.com/{tid}/v2.0 shall have {tid} replaced with the iss claim's value. + * + * @param jwtClaimSet The JWT claims. + * @return Whether the JWT token is issued by the configured issuer. + */ + private boolean isJwtFromIssuerFormat(JwtClaimsSet jwtClaimSet) { + /* Since the OpenID Connect Core says "The Issuer Identifier [...] MUST exactly match the value of the iss (issuer) Claim.", + allow only parameterized matching. */ + final Matcher m = Pattern.compile("\\{[^\\}]+\\}").matcher(config.getConfiguredIssuer()); + final StringBuffer issuer = new StringBuffer(); + while (m.find()) { + final String group = m.group(); + final String key = group.substring(1, group.length() - 1); + final Object value = jwtClaimSet.getClaim(key); + if (value != null) + m.appendReplacement(issuer, value.toString()); + } + m.appendTail(issuer); + + return issuer.toString().equals(jwtClaimSet.getIssuer()); + } + /** * Retrieve the actual JWT token from the encoded JWT token. *