diff --git a/dev/com.ibm.ws.security.openidconnect.client/resources/OSGI-INF/metatype/metatype.xml b/dev/com.ibm.ws.security.openidconnect.client/resources/OSGI-INF/metatype/metatype.xml
index c6823913c02a..69132115aa34 100644
--- a/dev/com.ibm.ws.security.openidconnect.client/resources/OSGI-INF/metatype/metatype.xml
+++ b/dev/com.ibm.ws.security.openidconnect.client/resources/OSGI-INF/metatype/metatype.xml
@@ -163,6 +163,10 @@
+
+
+
+
diff --git a/dev/com.ibm.ws.security.openidconnect.client/src/com/ibm/ws/security/openidconnect/client/internal/OidcClientConfigImpl.java b/dev/com.ibm.ws.security.openidconnect.client/src/com/ibm/ws/security/openidconnect/client/internal/OidcClientConfigImpl.java
index 87a5aa0ee06d..6d52e4e7bfa8 100644
--- a/dev/com.ibm.ws.security.openidconnect.client/src/com/ibm/ws/security/openidconnect/client/internal/OidcClientConfigImpl.java
+++ b/dev/com.ibm.ws.security.openidconnect.client/src/com/ibm/ws/security/openidconnect/client/internal/OidcClientConfigImpl.java
@@ -23,6 +23,7 @@
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.StringTokenizer;
import javax.net.ssl.SSLSocketFactory;
@@ -176,6 +177,7 @@ public class OidcClientConfigImpl implements OidcClientConfig {
public static final String CFG_KEY_ACCESS_TOKEN_CACHE_TIMEOUT = "accessTokenCacheTimeout";
public static final String CFG_KEY_PKCE_CODE_CHALLENGE_METHOD = "pkceCodeChallengeMethod";
public static final String CFG_KEY_TOKEN_REQUEST_ORIGIN_HEADER = "tokenRequestOriginHeader";
+ public static final String CFG_KEY_TOKEN_ORDER_TOFETCH_CALLER_CLAIMS = "tokenOrderToFetchCallerClaims";
public static final String OPDISCOVERY_AUTHZ_EP_URL = "authorization_endpoint";
public static final String OPDISCOVERY_TOKEN_EP_URL = "token_endpoint";
@@ -308,6 +310,8 @@ public class OidcClientConfigImpl implements OidcClientConfig {
private boolean useSystemPropertiesForHttpClientConnections = false;
private boolean tokenReuse = false;
+ private List tokenOrderToFetchCallerClaims;
+
private final OidcSessionCache oidcSessionCache = new InMemoryOidcSessionCache();
// see defect 218708
@@ -563,6 +567,8 @@ private void processConfigProps(Map props) {
// validateAuthzTokenEndpoints(); //TODO: update tests to expect the error if the validation here fails
+ tokenOrderToFetchCallerClaims = split(trimIt((String) props.get(CFG_KEY_TOKEN_ORDER_TOFETCH_CALLER_CLAIMS)));
+
if (discovery) {
logDiscoveryMessage("OIDC_CLIENT_DISCOVERY_COMPLETE");
}
@@ -637,7 +643,9 @@ private void processConfigProps(Map props) {
Tr.debug(tc, "accessTokenCacheTimeout:" + accessTokenCacheTimeout);
Tr.debug(tc, "pkceCodeChallengeMethod:" + pkceCodeChallengeMethod);
Tr.debug(tc, "tokenRequestOriginHeader:" + tokenRequestOriginHeader);
+ Tr.debug(tc, "tokenOrderToFetchCallerClaims:" + tokenOrderToFetchCallerClaims);
}
+
}
private void initializeAccessTokenCache() {
@@ -1942,4 +1950,28 @@ public String getTokenRequestOriginHeader() {
return tokenRequestOriginHeader;
}
+ @Override
+ public List getTokenOrderToFetchCallerClaims() {
+ return tokenOrderToFetchCallerClaims;
+ }
+
+ static List split(String str) {
+ List rvalue = new ArrayList();
+ if (str != null) {
+ StringTokenizer st = new StringTokenizer(str, ", ");
+ while (st.hasMoreElements()) {
+ rvalue.add(st.nextToken());
+ }
+ }
+ return rvalue;
+ }
+
+ public static void main(String[] args) {
+ String str = "AccessToken, IDToken,Userinfo";
+ List splitList = split(str);
+ for (String aStr : splitList) {
+ System.out.println(aStr);
+ }
+ }
+
}
diff --git a/dev/com.ibm.ws.security.openidconnect.clients.common/bnd.bnd b/dev/com.ibm.ws.security.openidconnect.clients.common/bnd.bnd
index 43007befaa87..235ba3d6da0b 100644
--- a/dev/com.ibm.ws.security.openidconnect.clients.common/bnd.bnd
+++ b/dev/com.ibm.ws.security.openidconnect.clients.common/bnd.bnd
@@ -84,7 +84,11 @@ Private-Package: \
com.ibm.ws.config;version=latest,\
io.openliberty.security.oidcclientcore.internal;version=latest,\
io.openliberty.security.common.jwt;version=latest,\
- com.ibm.ws.security.oauth.2.0;version=latest
+ com.ibm.ws.security.oauth.2.0;version=latest,\
+ jakarta.security.enterprise-api,\
+ jakarta.security.enterprise-api,\
+ jakarta.security.enterprise-api,\
+ jakarta.security.enterprise-api
-testpath: \
../build.sharedResources/lib/junit/old/junit.jar;version=file,\
diff --git a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/client/jose4j/util/Jose4jUtil.java b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/client/jose4j/util/Jose4jUtil.java
index 078202e6c819..7688363c5954 100644
--- a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/client/jose4j/util/Jose4jUtil.java
+++ b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/client/jose4j/util/Jose4jUtil.java
@@ -4,7 +4,7 @@
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-2.0/
- *
+ *
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
@@ -15,10 +15,17 @@
import java.security.AccessController;
import java.security.Key;
import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Base64;
import java.util.Date;
+import java.util.HashMap;
import java.util.Hashtable;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+import javax.net.ssl.SSLSocketFactory;
import javax.security.auth.Subject;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
@@ -52,6 +59,7 @@
import com.ibm.ws.security.openidconnect.clients.common.OidcSessionInfo;
import com.ibm.ws.security.openidconnect.clients.common.OidcUtil;
import com.ibm.ws.security.openidconnect.clients.common.TraceConstants;
+import com.ibm.ws.security.openidconnect.clients.common.UserInfoHelper;
import com.ibm.ws.security.openidconnect.jose4j.Jose4jValidator;
import com.ibm.ws.security.openidconnect.token.JWTTokenValidationFailedException;
import com.ibm.ws.webcontainer.security.AuthResult;
@@ -92,39 +100,109 @@ public Jose4jUtil(SSLSupport sslSupport) {
this.sslSupport = sslSupport;
}
+ private JwtClaims getClaimsFromAccessToken(String accessTokenStr) throws Exception {
+ JwtClaims jwtClaims = null;
+ String[] parts = accessTokenStr.split(Pattern.quote(".")); // split out the "parts" (header, payload and signature)
+
+ if (parts.length > 1) {
+ String claimsAsJsonString = new String(Base64.getDecoder().decode(parts[1]), "UTF-8");
+ jwtClaims = JwtClaims.parse(claimsAsJsonString);
+ } else {
+ // do nothing
+ }
+ return jwtClaims;
+ }
+
+ private JwtClaims getClaimsFromIdToken(String tokenStr, ConvergedClientConfig clientConfig, OidcClientRequest oidcClientRequest) throws Exception {
+ String clientId = clientConfig.getClientId();
+ if (tokenStr == null || tokenStr.isEmpty()) {
+ // This is for ID Token only
+ Tr.error(tc, "OIDC_CLIENT_IDTOKEN_REQUEST_FAILURE", new Object[] { clientId, clientConfig.getTokenEndpointUrl() });
+ throw new Exception();
+ }
+
+ JwtContext jwtContext = validateJwtStructureAndGetContext(tokenStr, clientConfig);
+ JwtClaims jwtClaims = parseJwtWithValidation(clientConfig, jwtContext.getJwt(), jwtContext, oidcClientRequest);
+ if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
+ Tr.debug(tc, "post jwtClaims: " + jwtClaims + " firstPass jwtClaims=" + jwtContext.getJwtClaims());
+ }
+
+ return jwtClaims;
+ }
+
+ private JwtClaims getClaimsFromUserInfo(String userInfoStr) throws Exception {
+ return JwtClaims.parse(userInfoStr);
+ }
+
+ private OidcTokenImplBase getOidcToken(JwtClaims jwtClaims, String accessToken, String refreshToken, String clientId,
+ String tokenTypeNoSpace) {
+ return new OidcTokenImplBase(jwtClaims, accessToken, refreshToken, clientId, tokenTypeNoSpace);
+ }
+
// eliminate the FFDC since we will have the Tr.error and most of the Exception already handled by FFDC
@FFDCIgnore({ Exception.class })
public ProviderAuthenticationResult createResultWithJose4J(String responseState,
Map tokens,
ConvergedClientConfig clientConfig,
- OidcClientRequest oidcClientRequest) {
+ OidcClientRequest oidcClientRequest,
+ SSLSocketFactory sslSocketFactory) {
//oidcClientRequest.setTokenType(OidcClientRequest.TYPE_); // decided by the caller
// This is for ID Token only at the writing time
ProviderAuthenticationResult oidcResult = null;
- String tokenStr = getIdToken(tokens, clientConfig);
- String originalIdTokenString = tokenStr;
- String accessToken = tokens.get(Constants.ACCESS_TOKEN);
- String refreshToken = tokens.get(Constants.REFRESH_TOKEN);
+ String idTokenStr = getIdToken(tokens, clientConfig);
+ String originalIdTokenString = idTokenStr;
+ String accessTokenStr = tokens.get(Constants.ACCESS_TOKEN);
+ String refreshTokenStr = tokens.get(Constants.REFRESH_TOKEN);
String clientId = clientConfig.getClientId();
+ Hashtable customProperties = new Hashtable();
+
try {
- if (tokenStr == null || tokenStr.isEmpty()) {
- // This is for ID Token only
- Tr.error(tc, "OIDC_CLIENT_IDTOKEN_REQUEST_FAILURE", new Object[] { clientId, clientConfig.getTokenEndpointUrl() });
+ Map tokenClaimsMap = new HashMap();
+
+ JwtClaims idTokenClaims = getClaimsFromIdToken(idTokenStr, clientConfig, oidcClientRequest);
+ OidcTokenImplBase idToken = getOidcToken(idTokenClaims, accessTokenStr, refreshTokenStr, clientId, Constants.TOKEN_TYPE_ID_TOKEN);
+ String sub = idToken.getSubject();
+ if (sub == null) {
return new ProviderAuthenticationResult(AuthResult.SEND_401, HttpServletResponse.SC_UNAUTHORIZED);
}
- JwtContext jwtContext = validateJwtStructureAndGetContext(tokenStr, clientConfig);
- JwtClaims jwtClaims = parseJwtWithValidation(clientConfig, jwtContext.getJwt(), jwtContext, oidcClientRequest);
- if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
- Tr.debug(tc, "post jwtClaims: " + jwtClaims + " firstPass jwtClaims=" + jwtContext.getJwtClaims());
+ tokenClaimsMap.put(Constants.TOKEN_TYPE_ID_TOKEN, idTokenClaims);
+
+ // access token
+ JwtClaims accessTokenClaims = getClaimsFromAccessToken(accessTokenStr);
+ tokenClaimsMap.put(Constants.TOKEN_TYPE_ACCESS_TOKEN, accessTokenClaims);
+
+ UserInfoHelper userInfoHelper = new UserInfoHelper(clientConfig, sslSupport);
+ String userInfoStr = userInfoHelper.getUserInfoIfPossible(sub, accessTokenStr, sslSocketFactory, oidcClientRequest);
+ if (userInfoStr != null) {
+ try {
+ JwtClaims userInfoClaims = getClaimsFromUserInfo(userInfoStr);
+ tokenClaimsMap.put(Constants.TOKEN_TYPE_USER_INFO, userInfoClaims);
+ } catch (Exception e) {
+ Tr.error(tc, "Invalid user info: " + userInfoStr);
+ }
}
- OidcTokenImplBase idToken = new OidcTokenImplBase(jwtClaims, accessToken, refreshToken, clientId, oidcClientRequest.getTokenTypeNoSpace());
- if (idToken.getSubject() == null) {
+ List tokesInOrder = clientConfig.getTokenOrderToFetchCallerClaims();
+
+ String userName = this.getUserName(clientConfig, tokesInOrder, tokenClaimsMap);
+ if (userName == null || userName.isEmpty()) {
return new ProviderAuthenticationResult(AuthResult.SEND_401, HttpServletResponse.SC_UNAUTHORIZED);
}
- AttributeToSubject attributeToSubject = new AttributeToSubject(clientConfig, idToken);
- if (attributeToSubject.checkUserNameForNull()) {
- return new ProviderAuthenticationResult(AuthResult.SEND_401, HttpServletResponse.SC_UNAUTHORIZED);
+
+ if (!clientConfig.isMapIdentityToRegistryUser()) {
+ String realm = this.getRealmName(clientConfig, tokesInOrder, tokenClaimsMap);
+ if (realm != null && !realm.isEmpty()) {
+ customProperties.put(AttributeNameConstants.WSCREDENTIAL_REALM, realm);
+ }
+ String uniqueSecurityName = this.getUniqueSecurityName(clientConfig, tokesInOrder, tokenClaimsMap, userName);
+
+ List groups = getGroups(clientConfig, tokesInOrder, tokenClaimsMap, realm);
+ if (groups != null && !groups.isEmpty()) {
+ customProperties.put(AttributeNameConstants.WSCREDENTIAL_GROUPS, groups);
+ }
+
+ String uniqueID = new StringBuffer("user:").append(realm).append("/").append(uniqueSecurityName).toString();
+ customProperties.put(AttributeNameConstants.WSCREDENTIAL_UNIQUEID, uniqueID);
}
boolean isImplicit = Constants.IMPLICIT.equals(clientConfig.getGrantType());
@@ -148,25 +226,28 @@ public ProviderAuthenticationResult createResultWithJose4J(String responseState,
}
Hashtable props = new Hashtable();
props.put(Constants.ID_TOKEN, originalIdTokenString);
- props.put(Constants.ACCESS_TOKEN, accessToken);
- if (refreshToken != null) {
- props.put(Constants.REFRESH_TOKEN, refreshToken);
+ props.put(Constants.ACCESS_TOKEN, accessTokenStr);
+ if (refreshTokenStr != null) {
+ props.put(Constants.REFRESH_TOKEN, refreshTokenStr);
}
if (idToken != null) {
props.put(Constants.ID_TOKEN_OBJECT, idToken);
}
+ if (userInfoStr != null) {
+ customProperties.put(Constants.USERINFO_STR, userInfoStr);
+ }
oidcResult = new ProviderAuthenticationResult(AuthResult.SUCCESS, HttpServletResponse.SC_OK, null, null, props, null);
if (isRunningBetaMode()) {
- createWASOidcSession(oidcClientRequest, jwtClaims, clientConfig);
+ //createWASOidcSession(oidcClientRequest, jwtClaims, clientConfig);
+ createWASOidcSession(oidcClientRequest, idToken.getJwtClaims(), clientConfig);
}
return oidcResult;
}
- Hashtable customProperties = new Hashtable();
if (clientConfig.isIncludeCustomCacheKeyInSubject() || clientConfig.isDisableLtpaCookie()) {
- long storingTime = new Date().getTime();
+ //long storingTime = new Date().getTime();
String customCacheKey = oidcClientRequest.getAndSetCustomCacheKeyValue(); //username + tokenStr.toString().hashCode();
- customProperties.put(ClientConstants.CREDENTIAL_STORING_TIME_MILLISECONDS, Long.valueOf(storingTime));
+ //customProperties.put(ClientConstants.CREDENTIAL_STORING_TIME_MILLISECONDS, Long.valueOf(storingTime));
if (clientConfig.isIncludeCustomCacheKeyInSubject()) {
customProperties.put(AttributeNameConstants.WSCREDENTIAL_CACHE_KEY, customCacheKey);
}
@@ -178,11 +259,11 @@ public ProviderAuthenticationResult createResultWithJose4J(String responseState,
subject.getPrivateCredentials().add(idToken); // add the external IDToken
customProperties.putAll(tokens); // add ALL tokens to props.
} else {
- if (refreshToken != null) {
- customProperties.put(Constants.REFRESH_TOKEN, refreshToken);
+ if (refreshTokenStr != null) {
+ customProperties.put(Constants.REFRESH_TOKEN, refreshTokenStr);
}
- if (accessToken != null) {
- customProperties.put(Constants.ACCESS_TOKEN, accessToken);
+ if (accessTokenStr != null) {
+ customProperties.put(Constants.ACCESS_TOKEN, accessTokenStr);
}
}
if (idToken != null) {
@@ -191,11 +272,16 @@ public ProviderAuthenticationResult createResultWithJose4J(String responseState,
//addJWTTokenToSubject(customProperties, idToken, clientConfig);
+ if (userInfoStr != null) {
+ customProperties.put(Constants.USERINFO_STR, userInfoStr);
+ }
+
+ customProperties.put(ClientConstants.CREDENTIAL_STORING_TIME_MILLISECONDS, Long.valueOf(new Date().getTime())); // this is GMT/UTC time already
+
//doIdAssertion(customProperties, payload, clientConfig);
- oidcResult = attributeToSubject.doMapping(customProperties, subject);
- //oidcResult = new ProviderAuthenticationResult(AuthResult.SUCCESS, HttpServletResponse.SC_OK, username, subject, customProperties, null);
+ oidcResult = new ProviderAuthenticationResult(AuthResult.SUCCESS, HttpServletResponse.SC_OK, userName, subject, customProperties, null);
if (oidcResult.getStatus() == AuthResult.SUCCESS && isRunningBetaMode()) {
- createWASOidcSession(oidcClientRequest, jwtClaims, clientConfig);
+ createWASOidcSession(oidcClientRequest, idToken.getJwtClaims(), clientConfig);
}
} catch (Exception e) {
Tr.error(tc, "OIDC_CLIENT_IDTOKEN_VERIFY_ERR", new Object[] { e.getLocalizedMessage(), clientId });
@@ -483,4 +569,236 @@ public JwtClaims validateJwsSignature(JwtContext jwtContext, ConvergedClientConf
return validator.validateJwsSignature((JsonWebSignature) jwStructure, jwtContext.getJwt());
}
+ //////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Find the user name from one of the tokens.
+ *
+ * @param clientConfig
+ * @param tokesInOrder
+ * @param tokenClaimsMap
+ * @return
+ * @throws MalformedClaimException
+ */
+ String getUserName(ConvergedClientConfig clientConfig, List tokesInOrder, Map tokenClaimsMap) throws MalformedClaimException {
+ String userNameClaim = getUserNameClaim(clientConfig);
+ String userName = getClaimValueFromTokens(userNameClaim, String.class, tokesInOrder, tokenClaimsMap);
+ if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
+ Tr.debug(tc, "user name = '" + userName + "' and the user identifier = " + userNameClaim);
+ }
+
+ if (userName == null) {
+ Tr.error(tc, "OIDC_CLIENT_JWT_MISSING_CLAIM", new Object[] { clientConfig.getClientId(), userNameClaim });
+ Tr.debug(tc, "There is no principal");
+ }
+ return userName;
+ }
+
+ /**
+ * Find the claim of the user name from the configuration.
+ *
+ * @param clientConfig
+ * @param tokesInOrder
+ * @param tokenClaimsMap
+ * @return
+ * @throws MalformedClaimException
+ */
+ String getUserNameClaim(ConvergedClientConfig clientConfig) {
+ String attrUsedToCreateSubject = clientConfig.isSocial() ? "userNameAttribute" : "userIdentifier";
+ String uid = clientConfig.getUserIdentifier();
+ if (uid == null || uid.isEmpty()) {
+ attrUsedToCreateSubject = clientConfig.isSocial() ? "userNameAttribute" : "userIdentityToCreateSubject";
+ uid = clientConfig.getUserIdentityToCreateSubject();
+ }
+
+ if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
+ Tr.debug(tc, "The " + attrUsedToCreateSubject + " config attribute is used");
+ Tr.debug(tc, "the user identifier = " + uid);
+ }
+ return uid;
+ }
+
+ /**
+ * Find the realm name from one of the tokens.
+ *
+ * @param clientConfig
+ * @param tokesInOrder
+ * @param tokenClaimsMap
+ * @return
+ * @throws MalformedClaimException
+ */
+ String getRealmName(ConvergedClientConfig clientConfig, List tokesInOrder, Map tokenClaimsMap) throws MalformedClaimException {
+ String realm = clientConfig.getRealmName();
+ if (realm == null) {
+ for (String claim : getRealmNameClaim(clientConfig)) {
+ realm = getClaimValueFromTokens(claim, String.class, tokesInOrder, tokenClaimsMap);
+ if (realm != null && !realm.isEmpty()) {
+ break;
+ }
+ }
+ }
+ if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
+ Tr.debug(tc, "realm name = ", realm);
+ }
+ return realm;
+ }
+
+ /**
+ * Find the claim(s) of the user name from the configuration.
+ *
+ * @param clientConfig
+ * @return
+ */
+ List getRealmNameClaim(ConvergedClientConfig clientConfig) {
+ List claims = new ArrayList();
+ String claim = clientConfig.getRealmIdentifier();
+ if (claim != null && !claim.isEmpty()) {
+ claims.add(claim);
+ }
+ claims.add(ClientConstants.ISS);
+ return claims;
+ }
+
+ /**
+ * Find the unique security name from one of the tokens.
+ *
+ * @param clientConfig
+ * @param tokesInOrder
+ * @param tokenClaimsMap
+ * @param userName
+ * @return
+ * @throws MalformedClaimException
+ */
+ String getUniqueSecurityName(ConvergedClientConfig clientConfig, List tokesInOrder, Map tokenClaimsMap, String userName) throws MalformedClaimException {
+ String uniqueSecurityNameClaim = getUserNameClaim(clientConfig);
+ String uniqueSecurityName = getClaimValueFromTokens(uniqueSecurityNameClaim, String.class, tokesInOrder, tokenClaimsMap);
+
+ if (uniqueSecurityName == null || uniqueSecurityName.isEmpty()) {
+ uniqueSecurityName = userName;
+ }
+ return uniqueSecurityName;
+ }
+
+ /**
+ * Find the claim of the unique security name from the configuration.
+ *
+ * @param clientConfig
+ * @return
+ */
+ String getUniqueSecurityNameClaim(ConvergedClientConfig clientConfig) {
+ return clientConfig.getUniqueUserIdentifier();
+ }
+
+ /**
+ * Build the list of groups using the group id(s) previously found from one of the tokens.
+ *
+ * @param clientConfig
+ * @param tokesInOrder
+ * @param tokenClaimsMap
+ * @param realm
+ * @return
+ * @throws MalformedClaimException
+ */
+ List getGroups(ConvergedClientConfig clientConfig, List tokesInOrder, Map tokenClaimsMap, String realm) throws MalformedClaimException {
+ List groups = new ArrayList();
+ List groupIds = getGroupIds(clientConfig, tokesInOrder, tokenClaimsMap);
+ for (String gid : groupIds) {
+ String group = new StringBuffer("group:").append(realm).append("/").append(gid).toString();
+ groups.add(group);
+ }
+ return groups;
+ }
+
+ /**
+ * Find the group id(s) from one of the tokens.
+ *
+ * @param clientConfig
+ * @param tokesInOrder
+ * @param tokenClaimsMap
+ * @return
+ * @throws MalformedClaimException
+ */
+ @SuppressWarnings("unchecked")
+ List getGroupIds(ConvergedClientConfig clientConfig, List tokesInOrder, Map tokenClaimsMap) throws MalformedClaimException {
+ String groupIdsClaim = getGroupIdsClaim(clientConfig);
+ List groupIds = null;
+ try {
+ groupIds = getClaimValueFromTokens(groupIdsClaim, List.class, tokesInOrder, tokenClaimsMap);
+ } catch (Exception e) {
+ } finally {
+ if (groupIds == null) {
+ groupIds = new ArrayList();
+ String groupIdsStr = getClaimValueFromTokens(groupIdsClaim, String.class, tokesInOrder, tokenClaimsMap);
+ if (groupIdsStr != null) {
+ groupIds.add(groupIdsStr);
+ }
+ }
+ }
+ if (groupIds.size() > 0 && TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
+ Tr.debug(tc, "groupIds=" + groupIds.toString() + " groups size = ", groupIds.size());
+ }
+ return groupIds;
+ }
+
+ /**
+ * Find the claim of the group id(s) from the configuration.
+ *
+ * @param clientConfig
+ * @return
+ */
+ String getGroupIdsClaim(ConvergedClientConfig clientConfig) {
+ return clientConfig.getGroupIdentifier();
+ }
+
+ /**
+ * Jakarta way of implementation: get claim value from one of tokens for a specific claim.
+ *
+ * @param
+ * @param claim
+ * @param claimType
+ * @param tokesInOrder
+ * @param tokenClaimsMap
+ * @return
+ * @throws MalformedClaimException
+ */
+ T getClaimValueFromTokens(String claim, Class claimType, List tokesInOrder, Map tokenClaimsMap) throws MalformedClaimException {
+ if (claim == null || claim.isEmpty()) {
+ return null;
+ }
+ T claimValue = null;
+ for (String token : tokesInOrder) {
+ JwtClaims tokenClaims = tokenClaimsMap.get(token);
+ if (tokenClaims != null) {
+ claimValue = tokenClaims.getClaimValue(claim, claimType);
+ if (valueExistsAndIsNotEmpty(claimValue, claimType))
+ break;
+ }
+ }
+ return claimValue;
+ }
+
+ /**
+ * Jakarta way of implementation: check whether value exists.
+ *
+ * @param
+ * @param claimValue
+ * @param claimType
+ * @return
+ */
+ @SuppressWarnings("rawtypes")
+ boolean valueExistsAndIsNotEmpty(T claimValue, Class claimType) {
+ if (claimValue == null) {
+ return false;
+ }
+ if (claimType.equals(String.class) && ((String) claimValue).isEmpty()) {
+ return false;
+ }
+ if (claimType.equals(Set.class) && ((Set) claimValue).isEmpty()) {
+ return false;
+ }
+ if (claimType.equals(List.class) && ((List) claimValue).isEmpty()) {
+ return false;
+ }
+ return true;
+ }
}
diff --git a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/AttributeToSubject.java b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/AttributeToSubject.java
index d06ab2f7a854..f18af7451bcb 100644
--- a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/AttributeToSubject.java
+++ b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/AttributeToSubject.java
@@ -358,5 +358,4 @@ public AttributeToSubject(ConvergedClientConfig clientConfig, OidcTokenImplBase
}
}
}
-
}
diff --git a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/AuthorizationCodeHandler.java b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/AuthorizationCodeHandler.java
index a2d6d2683451..840d69e93539 100644
--- a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/AuthorizationCodeHandler.java
+++ b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/AuthorizationCodeHandler.java
@@ -171,11 +171,18 @@ ProviderAuthenticationResult sendTokenRequestAndValidateResult(OidcClientRequest
oidcClientRequest.setTokenType(ClientConstants.TYPE_ID_TOKEN);
+ // ----------------------------------------------
+ // TODO: START: TICKET LIBERTY-i-70: to update to include userinfo in addition on to idToken, accessToken in createResultWithJose4J()
+ // but still need to update the authentication result to include it
+
// this has a LOT of dependencies.
- ProviderAuthenticationResult oidcResult = jose4jUtil.createResultWithJose4J(responseState, tokens, clientConfig, oidcClientRequest);
+ ProviderAuthenticationResult oidcResult = jose4jUtil.createResultWithJose4J(responseState, tokens, clientConfig, oidcClientRequest, sslSocketFactory);
//go get the userinfo if configured to do so, and update the authentication result to include it.
- new UserInfoHelper(clientConfig, sslSupport).getUserInfoIfPossible(oidcResult, tokens, sslSocketFactory, oidcClientRequest);
+ //new UserInfoHelper(clientConfig, sslSupport).getUserInfoIfPossible(oidcResult, tokens, sslSocketFactory, oidcClientRequest);
+
+ // TODO: END: TICKET LIBERTY-i-70: to update
+ // ----------------------------------------------
addAuthCodeToUsedList(authzCode);
diff --git a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/Constants.java b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/Constants.java
index 05564ffd13bd..25a1525111e0 100644
--- a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/Constants.java
+++ b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/Constants.java
@@ -4,7 +4,7 @@
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-2.0/
- *
+ *
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
@@ -48,4 +48,5 @@ public class Constants {
public static final String TOKEN_TYPE_ID_TOKEN = "IDToken";
public static final String TOKEN_TYPE_ACCESS_TOKEN = "AccessToken";
public static final String TOKEN_TYPE_JWT = "JsonWebToken";
+ public static final String TOKEN_TYPE_USER_INFO = "Userinfo"; // added t o support tokenOrderToFetchCallerClaims
}
\ No newline at end of file
diff --git a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/ConvergedClientConfig.java b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/ConvergedClientConfig.java
index 290418ebe4bc..bc2ae732a4a3 100644
--- a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/ConvergedClientConfig.java
+++ b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/ConvergedClientConfig.java
@@ -142,4 +142,5 @@ public interface ConvergedClientConfig extends JwtConsumerConfig {
public String getTokenRequestOriginHeader();
+ public List getTokenOrderToFetchCallerClaims(); // tokenOrderToFetchCallerClaims
}
diff --git a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/OIDCClientAuthenticatorUtil.java b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/OIDCClientAuthenticatorUtil.java
index ef313bc319de..2bcf5b2be561 100644
--- a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/OIDCClientAuthenticatorUtil.java
+++ b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/OIDCClientAuthenticatorUtil.java
@@ -4,7 +4,7 @@
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-2.0/
- *
+ *
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
@@ -232,11 +232,16 @@ ProviderAuthenticationResult handleImplicitFlowTokens(HttpServletRequest req,
oidcClientRequest.setTokenType(OidcClientRequest.TYPE_ID_TOKEN);
- oidcResult = jose4jUtil.createResultWithJose4J(responseState, reqParameters, clientConfig, oidcClientRequest);
+ // ------------------------------------------------------
+ // TODO: START: ISSUE #25460
+ oidcResult = jose4jUtil.createResultWithJose4J(responseState, reqParameters, clientConfig, oidcClientRequest, getSSLSocketFactory(clientConfig));
- if (clientConfig.getUserInfoEndpointUrl() != null) {
- getUserInfo(clientConfig, reqParameters, oidcClientRequest, oidcResult);
- }
+ // if (clientConfig.getUserInfoEndpointUrl() != null) {
+ // getUserInfo(clientConfig, reqParameters, oidcClientRequest, oidcResult);
+ // }
+
+ // TODO: END: ISSUE #25460
+ // ------------------------------------------------------
return oidcResult;
}
@@ -256,6 +261,25 @@ void getUserInfo(ConvergedClientConfig clientConfig, Hashtable r
new UserInfoHelper(clientConfig, sslSupport).getUserInfoIfPossible(oidcResult, reqParameters, sslSocketFactory, oidcClientRequest);
}
+ // ------------------------------------------------------
+ // TODO: START: ISSUE #25460
+ SSLSocketFactory getSSLSocketFactory(ConvergedClientConfig clientConfig) {
+ SSLSocketFactory sslSocketFactory = null;
+ try {
+ sslSocketFactory = new OidcClientHttpUtil().getSSLSocketFactory(clientConfig.getSSLConfigurationName(), sslSupport);
+ } catch (com.ibm.websphere.ssl.SSLException e) {
+ Tr.error(tc, "OIDC_CLIENT_HTTPS_WITH_SSLCONTEXT_NULL", new Object[] { e, clientConfig.getClientId() });
+ } catch (NoSSLSocketFactoryException e) {
+ boolean needHttps = clientConfig.getUserInfoEndpointUrl().toLowerCase().startsWith("https");
+ if (needHttps) {
+ Tr.error(tc, "OIDC_CLIENT_HTTPS_WITH_SSLCONTEXT_NULL", new Object[] { "Null ssl socket factory", clientConfig.getClientId() });
+ }
+ }
+ return sslSocketFactory;
+ }
+ // TODO: END: ISSUE #25460
+ // ------------------------------------------------------
+
public static boolean checkHttpsRequirement(ConvergedClientConfig clientConfig, String urlStr) {
boolean metHttpsRequirement = true;
if (clientConfig.isHttpsRequired()) {
diff --git a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/UserInfoHelper.java b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/UserInfoHelper.java
index 3900f03d01bb..a6f0b2ed331c 100644
--- a/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/UserInfoHelper.java
+++ b/dev/com.ibm.ws.security.openidconnect.clients.common/src/com/ibm/ws/security/openidconnect/clients/common/UserInfoHelper.java
@@ -4,7 +4,7 @@
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-2.0/
- *
+ *
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
@@ -126,7 +126,8 @@ protected void updateAuthenticationResultPropertiesWithUserInfo(ProviderAuthenti
}
// per oidc-connect-core-1.0 sec 5.3.2, sub claim of userinfo response must match sub claim in id token.
- protected boolean isUserInfoValid(String userInfoStr, String subClaim) {
+ // protected boolean isUserInfoValid(String userInfoStr, String subClaim) {
+ public boolean isUserInfoValid(String userInfoStr, String subClaim) {
String userInfoSubClaim = getUserInfoSubClaim(userInfoStr);
if (userInfoSubClaim == null || subClaim == null || userInfoSubClaim.compareTo(subClaim) != 0) {
Tr.error(tc, "USERINFO_INVALID", new Object[] { userInfoStr, subClaim });
@@ -254,4 +255,26 @@ String extractClaimsFromJwsResponse(String responseString, OidcClientConfig clie
return null;
}
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * get userinfo from provider's UserInfo Endpoint if configured and active.
+ *
+ * @return the user info
+ *
+ */
+ public String getUserInfoIfPossible(String sub, String accessToken, SSLSocketFactory sslsf, OidcClientRequest oidcClientRequest) {
+ if (!willRetrieveUserInfo() || accessToken == null) {
+ return null;
+ }
+
+ if (sub != null) {
+ String userInfoStr = getUserInfoFromURL(clientConfig, sslsf, accessToken, oidcClientRequest);
+ if (userInfoStr != null) {
+ if (isUserInfoValid(userInfoStr, sub))
+ return userInfoStr;
+ }
+ }
+ return null;
+ }
}
diff --git a/dev/com.ibm.ws.security.social/src/com/ibm/ws/security/social/internal/OidcLoginConfigImpl.java b/dev/com.ibm.ws.security.social/src/com/ibm/ws/security/social/internal/OidcLoginConfigImpl.java
index ca05031206bd..75be245f088f 100644
--- a/dev/com.ibm.ws.security.social/src/com/ibm/ws/security/social/internal/OidcLoginConfigImpl.java
+++ b/dev/com.ibm.ws.security.social/src/com/ibm/ws/security/social/internal/OidcLoginConfigImpl.java
@@ -19,6 +19,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.StringTokenizer;
import javax.net.ssl.SSLSocketFactory;
@@ -90,7 +91,7 @@ public class OidcLoginConfigImpl extends Oauth2LoginConfigImpl implements Conver
private String discoveryEndpointUrl = null;
private JSONObject discoveryjson = null;
private boolean discovery = false;
-
+
public static final String KEY_DISCOVERY_POLLING_RATE = "discoveryPollingRate";
private long discoveryPollingRate = 5 * 60 * 1000; // 5 minutes in milliseconds
private String discoveryDocumentHash = null;
@@ -142,6 +143,9 @@ public class OidcLoginConfigImpl extends Oauth2LoginConfigImpl implements Conver
private String tokenEndpointAuthSigningAlgorithm = null;
public static final String CFG_KEY_TOKEN_REQUEST_ORIGIN_HEADER = "tokenRequestOriginHeader";
private String tokenRequestOriginHeader = null;
+
+ public static final String CFG_KEY_TOKEN_ORDER_TOFETCH_CALLER_CLAIMS = "tokenOrderToFetchCallerClaims";
+ private List tokenOrderToFetchCallerClaims;
HttpUtils httputils = new HttpUtils();
ConfigUtils oidcConfigUtils = new ConfigUtils(null);
@@ -982,4 +986,28 @@ public String getTokenRequestOriginHeader() {
return tokenRequestOriginHeader;
}
+ @Override
+ public List getTokenOrderToFetchCallerClaims() {
+ return tokenOrderToFetchCallerClaims;
+ }
+
+ static List split(String str) {
+ List rvalue = new ArrayList();
+ if (str != null) {
+ StringTokenizer st = new StringTokenizer(str, ", ");
+ while (st.hasMoreElements()) {
+ rvalue.add(st.nextToken());
+ }
+ }
+ return rvalue;
+ }
+
+ public static void main(String[] args) {
+ String str = "AccessToken, IDToken,Userinfo";
+ List splitList = split(str);
+ for (String aStr : splitList) {
+ System.out.println(aStr);
+ }
+ }
+
}