From 39b79c777a241fadce1d03d1b0b7d5650709eb6b Mon Sep 17 00:00:00 2001 From: Alfonso Valdez Date: Mon, 8 Nov 2021 19:48:00 -0600 Subject: [PATCH 1/2] FISH-5725: adding code to support get Claims from ID Token --- .gitignore | 1 + .../openid/AzureDefinitionConverter.java | 7 +++- .../openid/GoogleDefinitionConverter.java | 7 +++- .../controller/ConfigurationController.java | 6 ++-- .../openid/domain/OpenIdConfiguration.java | 13 ++++++- .../openid/domain/OpenIdContextImpl.java | 35 +++++++++++++++++-- .../AzureAuthenticationDefinition.java | 9 ++++- .../GoogleAuthenticationDefinition.java | 10 +++++- .../OpenIdAuthenticationDefinition.java | 15 +++++++- 9 files changed, 92 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index af13bd1b..2982f76f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ buildNumber.properties # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) !/.mvn/wrapper/maven-wrapper.jar *.iml +.idea diff --git a/openid/src/main/java/fish/payara/security/openid/AzureDefinitionConverter.java b/openid/src/main/java/fish/payara/security/openid/AzureDefinitionConverter.java index 8168c2c4..df1ecbbb 100644 --- a/openid/src/main/java/fish/payara/security/openid/AzureDefinitionConverter.java +++ b/openid/src/main/java/fish/payara/security/openid/AzureDefinitionConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -169,6 +169,11 @@ public boolean tokenAutoRefresh() { public int tokenMinValidity() { return azureDefinition.tokenMinValidity(); } + + @Override + public boolean userClaimsFromIDToken() { + return azureDefinition.userClaimsFromIDToken(); + } }; } diff --git a/openid/src/main/java/fish/payara/security/openid/GoogleDefinitionConverter.java b/openid/src/main/java/fish/payara/security/openid/GoogleDefinitionConverter.java index c24e0076..e6575a2b 100644 --- a/openid/src/main/java/fish/payara/security/openid/GoogleDefinitionConverter.java +++ b/openid/src/main/java/fish/payara/security/openid/GoogleDefinitionConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -157,6 +157,11 @@ public boolean tokenAutoRefresh() { public int tokenMinValidity() { return googleDefinition.tokenMinValidity(); } + + @Override + public boolean userClaimsFromIDToken() { + return googleDefinition.userClaimsFromIDToken(); + } }; } diff --git a/openid/src/main/java/fish/payara/security/openid/controller/ConfigurationController.java b/openid/src/main/java/fish/payara/security/openid/controller/ConfigurationController.java index a5f65e78..ea082096 100644 --- a/openid/src/main/java/fish/payara/security/openid/controller/ConfigurationController.java +++ b/openid/src/main/java/fish/payara/security/openid/controller/ConfigurationController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -215,6 +215,7 @@ public OpenIdConfiguration buildConfig(OpenIdAuthenticationDefinition definition boolean tokenAutoRefresh = OpenIdUtil.getConfiguredValue(Boolean.class, definition.tokenAutoRefresh(), provider, OpenIdAuthenticationDefinition.OPENID_MP_TOKEN_AUTO_REFRESH); int tokenMinValidity = OpenIdUtil.getConfiguredValue(Integer.class, definition.tokenMinValidity(), provider, OpenIdAuthenticationDefinition.OPENID_MP_TOKEN_MIN_VALIDITY); + boolean userClaimsFromIDToken = OpenIdUtil.getConfiguredValue(Boolean.class, definition.userClaimsFromIDToken(), provider, OpenIdAuthenticationDefinition.OPENID_MP_CLAIMS_FROM_ID_TOKEN); OpenIdConfiguration configuration = new OpenIdConfiguration() .setProviderMetadata( @@ -256,7 +257,8 @@ public OpenIdConfiguration buildConfig(OpenIdAuthenticationDefinition definition .setJwksConnectTimeout(jwksConnectTimeout) .setJwksReadTimeout(jwksReadTimeout) .setTokenAutoRefresh(tokenAutoRefresh) - .setTokenMinValidity(tokenMinValidity); + .setTokenMinValidity(tokenMinValidity) + .setClaimsFromIDToken(userClaimsFromIDToken); validateConfiguration(configuration); diff --git a/openid/src/main/java/fish/payara/security/openid/domain/OpenIdConfiguration.java b/openid/src/main/java/fish/payara/security/openid/domain/OpenIdConfiguration.java index 2f099b8c..17a6be9b 100644 --- a/openid/src/main/java/fish/payara/security/openid/domain/OpenIdConfiguration.java +++ b/openid/src/main/java/fish/payara/security/openid/domain/OpenIdConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -70,6 +70,7 @@ public class OpenIdConfiguration { private boolean tokenAutoRefresh; private int tokenMinValidity; private JWTValidator validator; + private boolean claimsFromIDToken; static final String BASE_URL_EXPRESSION = "${baseURL}"; @@ -253,6 +254,15 @@ public OpenIdConfiguration setTokenMinValidity(int tokenMinValidity) { return this; } + public boolean isClaimsFromIDToken() { + return claimsFromIDToken; + } + + public OpenIdConfiguration setClaimsFromIDToken(boolean claimsFromIDToken) { + this.claimsFromIDToken = claimsFromIDToken; + return this; + } + @Override public String toString() { return OpenIdConfiguration.class.getSimpleName() @@ -273,6 +283,7 @@ public String toString() { + ", encryptionMetadata=" + encryptionMetadata + ", tokenAutoRefresh=" + tokenAutoRefresh + ", tokenMinValidity=" + tokenMinValidity + + ", claimsFromIDToken=" + claimsFromIDToken + '}'; } diff --git a/openid/src/main/java/fish/payara/security/openid/domain/OpenIdContextImpl.java b/openid/src/main/java/fish/payara/security/openid/domain/OpenIdContextImpl.java index 6592379f..abba6114 100644 --- a/openid/src/main/java/fish/payara/security/openid/domain/OpenIdContextImpl.java +++ b/openid/src/main/java/fish/payara/security/openid/domain/OpenIdContextImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -42,13 +42,15 @@ import fish.payara.security.openid.api.OpenIdClaims; import fish.payara.security.openid.api.OpenIdContext; import fish.payara.security.openid.api.RefreshToken; +import fish.payara.security.openid.api.JwtClaims; +import fish.payara.security.openid.api.OpenIdConstant; import fish.payara.security.openid.controller.AuthenticationController; import fish.payara.security.openid.OpenIdUtil; -import fish.payara.security.openid.api.OpenIdConstant; import fish.payara.security.openid.controller.UserInfoController; import java.io.IOException; import java.util.Optional; +import static java.util.logging.Level.FINEST; import static java.util.logging.Level.WARNING; import java.util.logging.Logger; import java.util.Set; @@ -162,7 +164,12 @@ public void setExpiresIn(Long expiresIn) { public JsonObject getClaimsJson() { if (claims == null) { if (configuration != null && accessToken != null) { - claims = userInfoController.getUserInfo(configuration, accessToken); + if(!configuration.isClaimsFromIDToken()) { + claims = userInfoController.getUserInfo(configuration, accessToken); + } else { + LOGGER.log(FINEST, "Processing user info from id token"); + claims = processClaimsFromIDToken(); + } } else { claims = Json.createObjectBuilder().build(); } @@ -170,6 +177,28 @@ public JsonObject getClaimsJson() { return claims; } + /** + * Method to get user information from Id Token + * @return JsonObject with user information + */ + private JsonObject processClaimsFromIDToken() { + JwtClaims identityTokenJWTClaims = identityToken.getJwtClaims(); + JwtClaims accessTokenJWTClaims = accessToken.getJwtClaims(); + //setting profile claims from id token + JsonObject userInfo = Json.createObjectBuilder() + .add(OpenIdConstant.SUBJECT_IDENTIFIER, identityTokenJWTClaims.getStringClaim(OpenIdConstant.SUBJECT_IDENTIFIER).orElse("")) + .add(OpenIdConstant.NAME, identityTokenJWTClaims.getStringClaim(OpenIdConstant.NAME).orElse("")) + .add(OpenIdConstant.FAMILY_NAME, accessTokenJWTClaims.getStringClaim(OpenIdConstant.FAMILY_NAME).orElse("")) + .add(OpenIdConstant.GIVEN_NAME, accessTokenJWTClaims.getStringClaim(OpenIdConstant.FAMILY_NAME).orElse("")) + .add(OpenIdConstant.EMAIL, identityTokenJWTClaims.getStringClaim(OpenIdConstant.EMAIL).orElse("")).build(); + + if(!this.getSubject().equals(userInfo.getString(OpenIdConstant.SUBJECT_IDENTIFIER))) { + throw new IllegalStateException("UserInfo Response is invalid as sub claim must match with the sub Claim in the ID Token"); + } + + return userInfo; + } + @Override public OpenIdClaims getClaims() { return new JsonClaims(getClaimsJson()); diff --git a/security-connectors-api/src/main/java/fish/payara/security/annotations/AzureAuthenticationDefinition.java b/security-connectors-api/src/main/java/fish/payara/security/annotations/AzureAuthenticationDefinition.java index d3b7140f..e28b54a9 100644 --- a/security-connectors-api/src/main/java/fish/payara/security/annotations/AzureAuthenticationDefinition.java +++ b/security-connectors-api/src/main/java/fish/payara/security/annotations/AzureAuthenticationDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -240,4 +240,11 @@ * @return */ int tokenMinValidity() default 10 * 1000; + + /** + * Optional. Indicates to skip the /userinfo endpoint call and get the user information from ID Token. + * + * @return + */ + boolean userClaimsFromIDToken() default false; } diff --git a/security-connectors-api/src/main/java/fish/payara/security/annotations/GoogleAuthenticationDefinition.java b/security-connectors-api/src/main/java/fish/payara/security/annotations/GoogleAuthenticationDefinition.java index db153b5f..7b256a61 100644 --- a/security-connectors-api/src/main/java/fish/payara/security/annotations/GoogleAuthenticationDefinition.java +++ b/security-connectors-api/src/main/java/fish/payara/security/annotations/GoogleAuthenticationDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -227,4 +227,12 @@ * @return */ int tokenMinValidity() default 10 * 1000; + + /** + * Optional. Indicates to skip the /userinfo endpoint call and get the user information from ID Token. + * + * @return + */ + boolean userClaimsFromIDToken() default false; + } diff --git a/security-connectors-api/src/main/java/fish/payara/security/annotations/OpenIdAuthenticationDefinition.java b/security-connectors-api/src/main/java/fish/payara/security/annotations/OpenIdAuthenticationDefinition.java index def24677..2dced1b5 100644 --- a/security-connectors-api/src/main/java/fish/payara/security/annotations/OpenIdAuthenticationDefinition.java +++ b/security-connectors-api/src/main/java/fish/payara/security/annotations/OpenIdAuthenticationDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2021 Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -232,6 +232,13 @@ */ int tokenMinValidity() default 10 * 1000; + /** + * Optional. Indicates to skip the /userinfo endpoint call and get the user information from ID Token. + * + * @return + */ + boolean userClaimsFromIDToken() default false; + /** * The Microprofile Config key for the provider uri is {@value} */ @@ -359,4 +366,10 @@ * The Microprofile Config key for evaluating EL expressions for every HTTP session is {@value} */ String OPENID_MP_SESSION_SCOPED_CONFIGURATION = "payara.security.openid.sessionScopedConfiguration"; + + /** + * The Microprofile Config key to skip the /userinfo endpoint call + * and get the user information from ID Token is {@value} + */ + String OPENID_MP_CLAIMS_FROM_ID_TOKEN = "payara.security.openid.claimsFromIDToken"; } From 2e65b52e58ff16289587e6458f4f9a73fa48b186 Mon Sep 17 00:00:00 2001 From: Alfonso Valdez Date: Tue, 9 Nov 2021 09:10:06 -0600 Subject: [PATCH 2/2] FISH-5725: fixing comments from PR --- .../openid/controller/ConfigurationController.java | 4 ++-- .../security/openid/domain/OpenIdConfiguration.java | 12 ++++++------ .../security/openid/domain/OpenIdContextImpl.java | 10 +++++----- .../annotations/OpenIdAuthenticationDefinition.java | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/openid/src/main/java/fish/payara/security/openid/controller/ConfigurationController.java b/openid/src/main/java/fish/payara/security/openid/controller/ConfigurationController.java index ea082096..c49789cf 100644 --- a/openid/src/main/java/fish/payara/security/openid/controller/ConfigurationController.java +++ b/openid/src/main/java/fish/payara/security/openid/controller/ConfigurationController.java @@ -215,7 +215,7 @@ public OpenIdConfiguration buildConfig(OpenIdAuthenticationDefinition definition boolean tokenAutoRefresh = OpenIdUtil.getConfiguredValue(Boolean.class, definition.tokenAutoRefresh(), provider, OpenIdAuthenticationDefinition.OPENID_MP_TOKEN_AUTO_REFRESH); int tokenMinValidity = OpenIdUtil.getConfiguredValue(Integer.class, definition.tokenMinValidity(), provider, OpenIdAuthenticationDefinition.OPENID_MP_TOKEN_MIN_VALIDITY); - boolean userClaimsFromIDToken = OpenIdUtil.getConfiguredValue(Boolean.class, definition.userClaimsFromIDToken(), provider, OpenIdAuthenticationDefinition.OPENID_MP_CLAIMS_FROM_ID_TOKEN); + boolean userClaimsFromIDToken = OpenIdUtil.getConfiguredValue(Boolean.class, definition.userClaimsFromIDToken(), provider, OpenIdAuthenticationDefinition.OPENID_MP_USER_CLAIMS_FROM_ID_TOKEN); OpenIdConfiguration configuration = new OpenIdConfiguration() .setProviderMetadata( @@ -258,7 +258,7 @@ public OpenIdConfiguration buildConfig(OpenIdAuthenticationDefinition definition .setJwksReadTimeout(jwksReadTimeout) .setTokenAutoRefresh(tokenAutoRefresh) .setTokenMinValidity(tokenMinValidity) - .setClaimsFromIDToken(userClaimsFromIDToken); + .setUserClaimsFromIDToken(userClaimsFromIDToken); validateConfiguration(configuration); diff --git a/openid/src/main/java/fish/payara/security/openid/domain/OpenIdConfiguration.java b/openid/src/main/java/fish/payara/security/openid/domain/OpenIdConfiguration.java index 17a6be9b..c508b275 100644 --- a/openid/src/main/java/fish/payara/security/openid/domain/OpenIdConfiguration.java +++ b/openid/src/main/java/fish/payara/security/openid/domain/OpenIdConfiguration.java @@ -70,7 +70,7 @@ public class OpenIdConfiguration { private boolean tokenAutoRefresh; private int tokenMinValidity; private JWTValidator validator; - private boolean claimsFromIDToken; + private boolean userClaimsFromIDToken; static final String BASE_URL_EXPRESSION = "${baseURL}"; @@ -254,12 +254,12 @@ public OpenIdConfiguration setTokenMinValidity(int tokenMinValidity) { return this; } - public boolean isClaimsFromIDToken() { - return claimsFromIDToken; + public boolean isUserClaimsFromIDToken() { + return userClaimsFromIDToken; } - public OpenIdConfiguration setClaimsFromIDToken(boolean claimsFromIDToken) { - this.claimsFromIDToken = claimsFromIDToken; + public OpenIdConfiguration setUserClaimsFromIDToken(boolean userClaimsFromIDToken) { + this.userClaimsFromIDToken = userClaimsFromIDToken; return this; } @@ -283,7 +283,7 @@ public String toString() { + ", encryptionMetadata=" + encryptionMetadata + ", tokenAutoRefresh=" + tokenAutoRefresh + ", tokenMinValidity=" + tokenMinValidity - + ", claimsFromIDToken=" + claimsFromIDToken + + ", userClaimsFromIDToken=" + userClaimsFromIDToken + '}'; } diff --git a/openid/src/main/java/fish/payara/security/openid/domain/OpenIdContextImpl.java b/openid/src/main/java/fish/payara/security/openid/domain/OpenIdContextImpl.java index abba6114..7433bcf3 100644 --- a/openid/src/main/java/fish/payara/security/openid/domain/OpenIdContextImpl.java +++ b/openid/src/main/java/fish/payara/security/openid/domain/OpenIdContextImpl.java @@ -164,11 +164,11 @@ public void setExpiresIn(Long expiresIn) { public JsonObject getClaimsJson() { if (claims == null) { if (configuration != null && accessToken != null) { - if(!configuration.isClaimsFromIDToken()) { + if(!configuration.isUserClaimsFromIDToken()) { claims = userInfoController.getUserInfo(configuration, accessToken); } else { - LOGGER.log(FINEST, "Processing user info from id token"); - claims = processClaimsFromIDToken(); + LOGGER.log(FINEST, "Processing user info from ID Token"); + claims = processUserClaimsFromIDToken(); } } else { claims = Json.createObjectBuilder().build(); @@ -181,7 +181,7 @@ public JsonObject getClaimsJson() { * Method to get user information from Id Token * @return JsonObject with user information */ - private JsonObject processClaimsFromIDToken() { + private JsonObject processUserClaimsFromIDToken() { JwtClaims identityTokenJWTClaims = identityToken.getJwtClaims(); JwtClaims accessTokenJWTClaims = accessToken.getJwtClaims(); //setting profile claims from id token @@ -189,7 +189,7 @@ private JsonObject processClaimsFromIDToken() { .add(OpenIdConstant.SUBJECT_IDENTIFIER, identityTokenJWTClaims.getStringClaim(OpenIdConstant.SUBJECT_IDENTIFIER).orElse("")) .add(OpenIdConstant.NAME, identityTokenJWTClaims.getStringClaim(OpenIdConstant.NAME).orElse("")) .add(OpenIdConstant.FAMILY_NAME, accessTokenJWTClaims.getStringClaim(OpenIdConstant.FAMILY_NAME).orElse("")) - .add(OpenIdConstant.GIVEN_NAME, accessTokenJWTClaims.getStringClaim(OpenIdConstant.FAMILY_NAME).orElse("")) + .add(OpenIdConstant.GIVEN_NAME, accessTokenJWTClaims.getStringClaim(OpenIdConstant.GIVEN_NAME).orElse("")) .add(OpenIdConstant.EMAIL, identityTokenJWTClaims.getStringClaim(OpenIdConstant.EMAIL).orElse("")).build(); if(!this.getSubject().equals(userInfo.getString(OpenIdConstant.SUBJECT_IDENTIFIER))) { diff --git a/security-connectors-api/src/main/java/fish/payara/security/annotations/OpenIdAuthenticationDefinition.java b/security-connectors-api/src/main/java/fish/payara/security/annotations/OpenIdAuthenticationDefinition.java index 2dced1b5..ce04f37b 100644 --- a/security-connectors-api/src/main/java/fish/payara/security/annotations/OpenIdAuthenticationDefinition.java +++ b/security-connectors-api/src/main/java/fish/payara/security/annotations/OpenIdAuthenticationDefinition.java @@ -371,5 +371,5 @@ * The Microprofile Config key to skip the /userinfo endpoint call * and get the user information from ID Token is {@value} */ - String OPENID_MP_CLAIMS_FROM_ID_TOKEN = "payara.security.openid.claimsFromIDToken"; + String OPENID_MP_USER_CLAIMS_FROM_ID_TOKEN = "payara.security.openid.userClaimsFromIDToken"; }