Skip to content

Commit

Permalink
Issue 21826: Add and read metatype for remaining OIDC Private Key JWT…
Browse files Browse the repository at this point in the history
… config attributes

- Adds the following attributes to the metatype for the `<openidConnectClient>` and `<oidcLogin>` elements:
    - tokenEndpointAuthSigningAlgorithm
    - keyAliasName

For OpenLiberty#21826
  • Loading branch information
ayoho committed Apr 25, 2023
1 parent 063297b commit 80482ac
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
/*******************************************************************************
* Copyright (c) 2018, 2020 IBM Corporation and others.
* Copyright (c) 2018, 2023 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.ws.security.common.config;

Expand All @@ -24,11 +21,12 @@
import com.ibm.ws.security.common.crypto.HashUtils;

public class DiscoveryConfigUtils {

public static final TraceComponent tc = Tr.register(DiscoveryConfigUtils.class, TraceConstants.TRACE_GROUP, TraceConstants.MESSAGE_BUNDLE);

private JSONObject discoveryjson;
private String tokenEndpointAuthMethod;
private String tokenEndpointAuthSigningAlgorithm;
private String scope;
private String signatureAlgorithm;
private String id;
Expand All @@ -39,29 +37,31 @@ public class DiscoveryConfigUtils {
private String discoveryDocumentHash;

private long discoveryPollingRate;

public static final String OPDISCOVERY_AUTHZ_EP_URL = "authorization_endpoint";
public static final String OPDISCOVERY_TOKEN_EP_URL = "token_endpoint";
public static final String OPDISCOVERY_INTROSPECTION_EP_URL = "introspection_endpoint";
public static final String OPDISCOVERY_JWKS_EP_URL = "jwks_uri";
public static final String OPDISCOVERY_USERINFO_EP_URL = "userinfo_endpoint";
public static final String OPDISCOVERY_ISSUER = "issuer";
public static final String OPDISCOVERY_TOKEN_EP_AUTH = "token_endpoint_auth_methods_supported";
public static final String OPDISCOVERY_TOKEN_EP_AUTH_SIGNING_ALGS_SUPPORTED = "token_endpoint_auth_signing_alg_values_supported";
public static final String OPDISCOVERY_SCOPES = "scopes_supported";
public static final String OPDISCOVERY_IDTOKEN_SIGN_ALG = "id_token_signing_alg_values_supported";

public static final String CFG_KEY_SCOPE = "scope";
public static final String CFG_KEY_TOKEN_ENDPOINT_AUTH_METHOD = "tokenEndpointAuthMethod";
public static final String CFG_KEY_TOKEN_ENDPOINT_AUTH_SIGNING_ALGORITHM = "tokenEndpointAuthSigningAlgorithm";
public static final String CFG_KEY_SIGNATURE_ALGORITHM = "signatureAlgorithm";
public static final String KEY_authorizationEndpoint = "authorizationEndpoint";
public static final String KEY_tokenEndpoint = "tokenEndpoint";
public static final String KEY_USERINFO_ENDPOINT = "userInfoEndpoint";
public static final String KEY_jwksUri = "jwksUri";
public static final String KEY_ISSUER = "issuer";
public static final String KEY_DISCOVERY_ENDPOINT = "discoveryEndpoint";

public DiscoveryConfigUtils() {

}

public DiscoveryConfigUtils initialConfig(String configId, String ep, long discoveryRate) {
Expand All @@ -70,42 +70,54 @@ public DiscoveryConfigUtils initialConfig(String configId, String ep, long disco
this.discoveryPollingRate = discoveryRate;
return this;
}
public DiscoveryConfigUtils discoveredConfig(String alg, String tokenepAuthMethod, String scope) {

public DiscoveryConfigUtils discoveredConfig(String alg, String tokenepAuthMethod, String tokenEndpointAuthSigningAlgorithm, String scope) {
this.signatureAlgorithm = alg;
this.tokenEndpointAuthMethod = tokenepAuthMethod;
this.tokenEndpointAuthSigningAlgorithm = tokenEndpointAuthSigningAlgorithm;
this.scope = scope;
return this;
}
public DiscoveryConfigUtils discoveryDocumentHash(String discoveryHash) {

public DiscoveryConfigUtils discoveryDocumentHash(String discoveryHash) {
this.discoveryDocumentHash = discoveryHash;
return this;
}
public DiscoveryConfigUtils discoveryDocumentResult(JSONObject json) {

public DiscoveryConfigUtils discoveryDocumentResult(JSONObject json) {
this.discoveryjson = json;
return this;
}

public String adjustTokenEndpointAuthMethod() {
ArrayList<String> discoveryTokenepAuthMethod = discoverOPConfig(discoveryjson.get(OPDISCOVERY_TOKEN_EP_AUTH));
if (isSocialRPUsingDefault("authMethod") && !opHasSocialRPDefault("authMethod", discoveryTokenepAuthMethod)) {
this.tokenEndpointAuthMethod = adjustConfigAttributeValueBasedOnListOfSupportedValues(OPDISCOVERY_TOKEN_EP_AUTH, CFG_KEY_TOKEN_ENDPOINT_AUTH_METHOD, tokenEndpointAuthMethod);
return this.tokenEndpointAuthMethod;
}

public String adjustTokenEndpointAuthSigningAlgorithm() {
this.tokenEndpointAuthSigningAlgorithm = adjustConfigAttributeValueBasedOnListOfSupportedValues(OPDISCOVERY_TOKEN_EP_AUTH_SIGNING_ALGS_SUPPORTED, CFG_KEY_TOKEN_ENDPOINT_AUTH_SIGNING_ALGORITHM, tokenEndpointAuthSigningAlgorithm);
return this.tokenEndpointAuthSigningAlgorithm;
}

public String adjustConfigAttributeValueBasedOnListOfSupportedValues(String discoveryDocKey, String configAttributeName, String originalConfigValue) {
String overideValue = originalConfigValue;
ArrayList<String> opValuesSupported = discoverOPConfig(discoveryjson.get(discoveryDocKey));
if (isSocialRPUsingDefault(configAttributeName) && !opHasSocialRPDefault(configAttributeName, opValuesSupported)) {
if (tc.isDebugEnabled()) {
Tr.debug(tc, "See if we need to adjusted the token endpoint authmethod. The original is : " + tokenEndpointAuthMethod);
Tr.debug(tc, "See if we need to adjusted " + configAttributeName + ". The original is : " + originalConfigValue);
}
String supported = socialRPSupportsOPConfig("authMethod", discoveryTokenepAuthMethod);
String supported = socialRPSupportsOPConfig(configAttributeName, opValuesSupported);
if (supported != null) {
Tr.info(tc, "OIDC_CLIENT_DISCOVERY_OVERRIDE_DEFAULT", this.tokenEndpointAuthMethod, CFG_KEY_TOKEN_ENDPOINT_AUTH_METHOD, supported, getId());
this.tokenEndpointAuthMethod = supported;
Tr.info(tc, "OIDC_CLIENT_DISCOVERY_OVERRIDE_DEFAULT", originalConfigValue, configAttributeName, supported, getId());
overideValue = supported;
if (tc.isDebugEnabled()) {
Tr.debug(tc, "The adjusted value is : " + tokenEndpointAuthMethod);
Tr.debug(tc, "The adjusted value is : " + overideValue);
}
}
}
}
return this.tokenEndpointAuthMethod;
return overideValue;
}

private String getId() {
return id;
}
Expand All @@ -116,28 +128,30 @@ private String getId() {
*/
public String adjustScopes() {
ArrayList<String> discoveryScopes = discoverOPConfig(discoveryjson.get(OPDISCOVERY_SCOPES));
if (isSocialRPUsingDefault("scope") && !opHasSocialRPDefault("scope", discoveryScopes)) {
if (isSocialRPUsingDefault(CFG_KEY_SCOPE) && !opHasSocialRPDefault(CFG_KEY_SCOPE, discoveryScopes)) {
if (tc.isDebugEnabled()) {
Tr.debug(tc, "See if we need to adjusted the scopes. The original is : " + this.scope);
}
String supported = socialRPSupportsOPConfig("scope", discoveryScopes);
String supported = socialRPSupportsOPConfig(CFG_KEY_SCOPE, discoveryScopes);
if (supported != null) {
Tr.info(tc, "OIDC_CLIENT_DISCOVERY_OVERRIDE_DEFAULT", this.scope, CFG_KEY_SCOPE, supported, getId());
this.scope = supported;
if (tc.isDebugEnabled()) {
Tr.debug(tc, "The adjusted value is : " + this.scope);
}
}
}
}
return this.scope;
}

private boolean isSocialRPUsingDefault(String key) {
if ("authMethod".equals(key)) {
if (CFG_KEY_TOKEN_ENDPOINT_AUTH_METHOD.equals(key)) {
return matches("client_secret_post", this.tokenEndpointAuthMethod);
} else if ("alg".equals(key)) {
} else if (CFG_KEY_TOKEN_ENDPOINT_AUTH_SIGNING_ALGORITHM.equals(key)) {
return matches("RS256", this.tokenEndpointAuthSigningAlgorithm);
} else if (CFG_KEY_SIGNATURE_ALGORITHM.equals(key)) {
return matches("RS256", this.signatureAlgorithm);
} else if ("scope".equals(key)) {
} else if (CFG_KEY_SCOPE.equals(key)) {
return (matchesMultipleValues("openid profile email", this.scope));
}
return false;
Expand All @@ -151,25 +165,34 @@ private String socialRPSupportsOPConfig(String key, ArrayList<String> values) {

String rpSupportedSignatureAlgorithms = "RS256";
String rpSupportedTokenEndpointAuthMethods = "client_secret_post client_secret_basic";
String rpSupportedTokenEndpointAuthSigningAlgs = "RS256 RS384 RS512 ES256 ES384 ES512";
String rpSupportedScopes = "openid profile email";

if ("alg".equals(key) && values != null) {
if (CFG_KEY_SIGNATURE_ALGORITHM.equals(key) && values != null) {
for (String value : values) {
if (rpSupportedSignatureAlgorithms.contains(value)) {
return value;
}
}
}

if ("authMethod".equals(key) && values != null) {
if (CFG_KEY_TOKEN_ENDPOINT_AUTH_METHOD.equals(key) && values != null) {
for (String value : values) {
if (rpSupportedTokenEndpointAuthMethods.contains(value)) {
return value;
}
}
}

if ("scope".equals(key) && values != null) {
if (CFG_KEY_TOKEN_ENDPOINT_AUTH_SIGNING_ALGORITHM.equals(key) && values != null) {
for (String value : values) {
if (rpSupportedTokenEndpointAuthSigningAlgs.contains(value)) {
return value;
}
}
}

if (CFG_KEY_SCOPE.equals(key) && values != null) {
String scopes = null;
for (String value : values) {
if (rpSupportedScopes.contains(value)) {
Expand All @@ -185,7 +208,7 @@ private String socialRPSupportsOPConfig(String key, ArrayList<String> values) {
}
return null;
}

/**
* @param object
*/
Expand Down Expand Up @@ -227,17 +250,19 @@ private ArrayList<String> parseJsonArray(JSONArray jsonArrayOfStrings) {
}
}
}

return jsonString;
}

private boolean opHasSocialRPDefault(String key, ArrayList<String> opconfig) {

if ("authMethod".equals(key)) {
if (CFG_KEY_TOKEN_ENDPOINT_AUTH_METHOD.equals(key)) {
return matches("client_secret_post", opconfig);
} else if ("alg".equals(key)) {
} else if (CFG_KEY_TOKEN_ENDPOINT_AUTH_SIGNING_ALGORITHM.equals(key)) {
return matches("RS256", opconfig);
} else if (CFG_KEY_SIGNATURE_ALGORITHM.equals(key)) {
return matches("RS256", opconfig);
} else if ("scope".equals(key)) {
} else if (CFG_KEY_SCOPE.equals(key)) {
return matches("openid", opconfig) && matches("profile", opconfig) && matches("email", opconfig);
}
return false;
Expand Down Expand Up @@ -274,7 +299,7 @@ private boolean matchesMultipleValues(String rpdefault, String rpconfig) {
}
return true;
}

/**
* @param object
* @return
Expand Down Expand Up @@ -304,35 +329,35 @@ public void logDiscoveryWarning(Map<String, Object> props) {
}
if ((ep = configUtils.trim((String) props.get(KEY_jwksUri))) != null) {
endpoints = buildDiscoveryWarning(endpoints, KEY_jwksUri);
}
}
if (!endpoints.isEmpty()) {
logWarning("OIDC_CLIENT_DISCOVERY_OVERRIDE_EP", endpoints);
}

if ((ep = configUtils.trim((String) props.get(KEY_ISSUER))) != null) {
logWarning("OIDC_CLIENT_DISCOVERY_OVERRIDE_ISSUER", KEY_ISSUER);
}

}

/**
* @param endpoints
*/
private void logWarning(String key, String endpoints) {

Tr.warning(tc, key, this.discoveryURL, endpoints, getId());

}

/**
* @param endpoints
* @param ep
* @return
*/
private String buildDiscoveryWarning(String endpoints, String ep) {
private String buildDiscoveryWarning(String endpoints, String ep) {
return endpoints.concat(ep).concat(", ");
}

/**
* @param string
*/
Expand All @@ -343,7 +368,7 @@ public void logDiscoveryMessage(String key, String nlsMessage, String defaultMes
Tr.info(tc, nlsMessage);
} else {
Tr.info(tc, getNlsMessage(key, defaultMessage));
}
}
}

private String getNlsMessage(String key, String defaultMessage) {
Expand All @@ -352,7 +377,7 @@ private String getNlsMessage(String key, String defaultMessage) {
message = TraceNLS.getFormattedMessage(getClass(),
bundleName, key,
new Object[] { getId(), this.discoveryURL }, defaultMessage);

return message;
}

Expand All @@ -372,9 +397,9 @@ public boolean calculateDiscoveryDocumentHash(JSONObject json) {
}
return updated;
}

public String getDiscoveryDocumentHash() {
return this.discoveryDocumentHash;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,19 @@ tokenEndpointAuthMethod.desc=The method to use for sending credentials to the to

tokenEndpointAuthMethod.privateKeyJwt=Private key JWT client authentication.

tokenEndpointAuthSigningAlgorithm=Token endpoint authentication signing algorithm
tokenEndpointAuthSigningAlgorithm.desc=The signature algorithm to use to sign tokens that are used for client authentication.

tokenEndpointAuthSigningAlgorithm.RS256=Use the RS256 signature algorithm to sign tokens that are used for client authentication
tokenEndpointAuthSigningAlgorithm.RS384=Use the RS384 signature algorithm to sign tokens that are used for client authentication
tokenEndpointAuthSigningAlgorithm.RS512=Use the RS512 signature algorithm to sign tokens that are used for client authentication
tokenEndpointAuthSigningAlgorithm.ES256=Use the ES256 signature algorithm to sign tokens that are used for client authentication
tokenEndpointAuthSigningAlgorithm.ES384=Use the ES384 signature algorithm to sign tokens that are used for client authentication
tokenEndpointAuthSigningAlgorithm.ES512=Use the ES512 signature algorithm to sign tokens that are used for client authentication

keyAliasName=Key alias name
keyAliasName.desc=The alias for the private key that is used for signing tokens that are used for client authentication.

authnSessionDisabled=Authentication session cookie disabled
authnSessionDisabled.desc=An authentication session cookie will not be created for inbound propagation. The client is expected to send a valid OAuth token for every request.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@
<Option label="post" value="post" />
<Option label="%tokenEndpointAuthMethod.privateKeyJwt" value="private_key_jwt" />
</AD>
<AD id="tokenEndpointAuthSigningAlgorithm" name="%tokenEndpointAuthSigningAlgorithm" description="%tokenEndpointAuthSigningAlgorithm.desc" required="false"
type="String" default="RS256" ibm:beta="true" >
<Option label="%tokenEndpointAuthSigningAlgorithm.RS256" value="RS256" />
<Option label="%tokenEndpointAuthSigningAlgorithm.RS384" value="RS384" />
<Option label="%tokenEndpointAuthSigningAlgorithm.RS512" value="RS512" />
<Option label="%tokenEndpointAuthSigningAlgorithm.ES256" value="ES256" />
<Option label="%tokenEndpointAuthSigningAlgorithm.ES384" value="ES384" />
<Option label="%tokenEndpointAuthSigningAlgorithm.ES512" value="ES512" />
</AD>
<AD id="keyAliasName" name="%keyAliasName" description="%keyAliasName.desc" required="false" type="String" ibm:beta="true" />
<AD id="jsonWebKey" name="internal" description="internal use only" required="false" type="String" />
<AD id="prompt" name="internal" description="internal use only" required="false" type="String" />
<AD id="jwt" name="internal" description="internal use only" ibm:type="pid" ibm:reference="com.ibm.ws.security.openidconnect.client.jwt"
Expand Down
Loading

0 comments on commit 80482ac

Please sign in to comment.