OpenID Client Credentials with a private key is a robust and secure way for client applications to authenticate themselves when interacting with OpenID Connect providers. This method involves the use of a public-private key pair, similar to SSL/TLS certificates, to verify the client's identity. The private key is securely held by the client, while the public key is registered with the authorization server.
For additional information regarding the implement in Liberty, please refer to this link.
- Set up a Microsoft Entra ID application using a private key JWT
- Deploy ODM on a container configured with Microsoft Entra ID Part 2
- Troubleshooting
- License
-
Create the ODM application.
In Microsoft Entra Id / Manage / App registration, click New Registration:
- Name: ODM Application
- Supported account types / Who can use this application or access this API?: select
Accounts in this organizational directory only (Default Directory only - Single tenant)
- Click Register
-
Retrieve Tenant and Client information.
In Microsoft Entra Id / Manage / App Registration, select ODM Application and click Overview:
- Application (client) ID: Client ID. It will be referenced as
CLIENT_ID
in the next steps. - Directory (tenant) ID: Your Tenant ID. It will be referenced as
TENANT_ID
in the next steps.
- Application (client) ID: Client ID. It will be referenced as
-
Register a public certificate.
To manage private key JWT authentication, you need a private certificate (.key file) and a public certificate (.crt file), which should be registered on the ODM client side (RP) application. On the Microsoft Entra ID (OP) side, you are required to register the public certificate.
If you don't have a trusted certificate, you can utilize OpenSSL and other cryptography and certificate management libraries to generate a certificate file and a private key, define the domain name, and set the expiration date. The following command will create a self-signed certificate (.crt file) and a private key (.key file) that will accept the domain name myodmcompany.com. The expiration is set to 1000 days:
openssl req -x509 -nodes -days 1000 -newkey rsa:2048 -keyout myodmcompany.key \
-out myodmcompany.crt -subj "/CN=myodmcompany.com/OU=it/O=myodmcompany/L=Paris/C=FR" \
-addext "subjectAltName = DNS:myodmcompany.com"
In Microsoft Entra Id / Manage / App registrations, select ODM Application:
- From the Overview page, click on the link Client credentials: Add a certificate or secret or on the Manage / Certificates & secrets tab
- Select the Certificates tab
- Click Upload certificate
- Select the
myodmcompany.crt
or your own public file - Description:
For ODM integration
- Click Add
- Select the
-
Add Claims.
In Microsoft Entra Id / Manage / App registrations, select ODM Application, and in Manage / Token Configuration:
-
Add Optional email ID Claim
-
Click + Add optional claim
-
Select ID
-
Check email
-
Click Add
-
Turn on Microsoft Graph email permission
- Check Turn on the Microsoft Graph email permission
- Click Add
-
-
Add Optional email Access Claim
- Click + Add optional claim
- Select Access
- Check email
- Click Add
-
Add Group Claim
- Click + Add groups claim
- Check Security Groups
- Click Add
-
Create a custom claim named "identity"
To enable the ODM REST API to use both the 'Password Credentials' flow with email as the user identifier and the 'Client Credentials' flow with client_id as the user identifier, we must establish a new claim named "identity" that will dynamically capture the appropriate value based on the chosen flow: In Microsoft Entra Id / Manage / Enterprise applications, select ODM Application, and in Manage / Single sign-on:
- Click on Edit of the "Attributes & Claims" section
- Click + Add new claim
- Name:
identity
- Fill 2 Claim conditions in the exact following order:
- User Type: Any / Scoped Groups: 0 / Source: Attribute / Value: <CLIENT_ID>
- User Type: Members / Scoped Groups: 0 / Source: Attribute / Value: user.mail
- Name:
- Click + Add new claim
-
API Permissions.
In Microsoft Entra Id / Manage / App Registration, select ODM Application, and then click API Permissions.
- Click Grant Admin Consent for
-
Manifest change.
In Microsoft Entra Id / Manage / App Registration, select ODM Application, and then click Manifest.
The Manifest feature (a JSON representation of an app registration) is currently in transition. AAD Graph app manifest will be deprecated soon and not editable anymore starting 12/2/2024. It will be replaced by the Microsoft Graph App Manifest
As explained in accessTokenAcceptedVersion attribute explanation, change the value to 2.
ODM OpenID Liberty configuration needs version 2.0 for the issuerIdentifier. See the openIdWebSecurity.xml file.
It is also necessary to set acceptMappedClaims to true to manage claims. Without this setting, you get the exception AADSTS50146: This application is required to be configured with an application-specific signing key. It is either not configured with one, or the key has expired or is not yet valid. when requesting a token.
With Microsoft Graph App Manifest:
- acceptMappedClaims is relocated as a property of the api attribute
- accessTokenAcceptedVersion is relocated as a property of the api attribute and renamed requestedAccessTokenVersion
Then, click Save.
-
To get your entitlement key, log in to MyIBM Container Software Library with the IBMid and password that are associated with the entitled software.
In the Container software library tile, verify your entitlement on the View library page, and then go to Get entitlement key to retrieve the key.
-
Create a pull secret by running a
kubectl create secret
command.$ kubectl create secret docker-registry icregistry-secret \ --docker-server=cp.icr.io \ --docker-username=cp \ --docker-password="<API_KEY_GENERATED>" \ --docker-email=<USER_EMAIL>
Where:
- API_KEY_GENERATED is the entitlement key from the previous step. Make sure you enclose the key in double-quotes.
- USER_EMAIL is the email address associated with your IBMid.
Note: The cp.icr.io value for the docker-server parameter is the only registry domain name that contains the images. You must set the docker-username to cp to use an entitlement key as docker-password.
-
Make a note of the secret name so that you can set it for the image.pullSecrets parameter when you run a helm install of your containers. The image.repository parameter is later set to cp.icr.io/cp/cp4a/odm.
-
Create a secret with the Microsoft Entra ID Server certificate.
To allow ODM services to access the Microsoft Entra ID Server, it is mandatory to provide the Microsoft Entra ID Server certificate. You can create the secret as follows:
keytool -printcert -sslserver login.microsoftonline.com -rfc > microsoft.crt kubectl create secret generic ms-secret --from-file=tls.crt=microsoft.crt
Introspecting the Microsoft Entra ID login.microsoftonline.com certificate, you can see it has been signed by the Digicert Root CA authorithy.
So we will also add the DigiCert Global Root CA from this page:
curl --silent --remote-name https://cacerts.digicert.com/DigiCertGlobalRootCA.crt.pem kubectl create secret generic digicert-secret --from-file=tls.crt=DigiCertGlobalRootCA.crt.pem
-
Create a secret to provide the private and public certificate to manage the private_key_jwt authentication
To allow ODM containers to generate a client_assertion, you have to provide them the private and public certificates with the following myodmcompany secret. Don't change this name with this tutorial as this name is linked to the openidConnectClient keyAliasName="myodmcompany" parameter of the private_key_jwt liberty configuration.
kubectl create secret generic myodmcompany --from-file=tls.key=myodmcompany.key --from-file=tls.crt=myodmcompany.crt
-
Generate the ODM configuration file for Microsoft Entra ID.
If you have not yet done so, download the azuread-odm-script.zip file to your machine. This archive contains the script and the content of the templates_for_privatekeyjwt directory.
The script allows you to generate the necessary configuration files. Generate the files with the following command:
./generateTemplateForPrivateKeyJWT.sh -i <CLIENT_ID> -n <TENANT_ID> -g <GROUP_ID> [-a <SSO_DOMAIN>]
Where:
- TENANT_ID and CLIENT_ID have been obtained from previous step
- GROUP_ID is the identifier of the ODM Admin group created in Manage groups and users (ID of
odm-admin
) - SSO_DOMAIN is the domain name of your SSO. If your AzureAD is connected to another SSO, you should add the SSO domain name in this parameter. If your user has been declared as explained in step Create at least one user that belongs to this new group, you can omit this parameter.
The following four files are generated into the
outputPKeyJWT
directory:- webSecurity.xml contains the mapping between Liberty J2EE ODM roles and Microsoft Entra ID groups and users:
- All ODM roles are given to the GROUP_ID group
- rtsAdministrators/resAdministrators/resExecutors ODM roles are given to the CLIENT_ID (which is seen as a user) to manage the client-credentials flow
- openIdWebSecurity.xml contains two openIdConnectClient Liberty configurations:
- For web access to the Decision Center an Decision Server consoles using userIdentifier="email" with the Authorization Code flow
- For the rest-api call using userIdentifier="aud" with the client-credentials flow
- openIdParameters.properties configures several features like allowed domains, logout, and some internal ODM OpenId features
- OdmOidcProviders.json configures the client-credentials OpenId provider used by the Decision Center server configuration to connect Decision Center to the Decision Server console and Decision Center to the Decision Runner
-
Create the Microsoft Entra ID authentication secret.
kubectl create secret generic azuread-auth-secret \ --from-file=OdmOidcProviders.json=./outputPKeyJWT/OdmOidcProviders.json \ --from-file=openIdParameters.properties=./outputPKeyJWT/openIdParameters.properties \ --from-file=openIdWebSecurity.xml=./outputPKeyJWT/openIdWebSecurity.xml \ --from-file=webSecurity.xml=./outputPKeyJWT/webSecurity.xml
helm repo add ibm-helm https://raw.githubusercontent.com/IBM/charts/master/repo/ibm-helm
helm repo update
helm search repo ibm-odm-prod
NAME CHART VERSION APP VERSION DESCRIPTION
ibm-helm/ibm-odm-prod 24.1.0 9.0.0.1 IBM Operational Decision Manager
You can now install the product. We will use the PostgreSQL internal database and disable the data persistence (internalDatabase.persistence.enabled=false
) to avoid any platform complexity concerning persistent volume allocation.
See the Preparing to install documentation for additional information.
helm install my-odm-release ibm-helm/ibm-odm-prod --version 24.1.0 \
--set image.repository=cp.icr.io/cp/cp4a/odm --set image.pullSecrets=icregistry-secret \
--set oidc.enabled=true \
--set license=true \
--set internalDatabase.persistence.enabled=false \
--set customization.trustedCertificateList='{ms-secret,digicert-secret}' \
--set customization.privateCertificateList='{myodmcompany}' \
--set customization.authSecretRef=azuread-auth-secret \
--set internalDatabase.runAsUser='' --set customization.runAsUser='' --set service.enableRoute=true
Refer to the following documentation to install an NGINX Ingress Controller on:
When the NGINX Ingress Controller is ready, you can install the ODM release with:
helm install my-odm-release ibm-helm/ibm-odm-prod --version 24.1.0 \
--set image.repository=cp.icr.io/cp/cp4a/odm --set image.pullSecrets=icregistry-secret \
--set oidc.enabled=true \
--set license=true \
--set internalDatabase.persistence.enabled=false \
--set customization.trustedCertificateList='{ms-secret,digicert-secret}' \
--set customization.privateCertificateList='{myodmcompany}' \
--set customization.authSecretRef=azuread-auth-secret \
--set service.ingress.enabled=true \
--set service.ingress.annotations={"kubernetes.io/ingress.class: nginx"\,"nginx.ingress.kubernetes.io/backend-protocol: HTTPS"}
Note By default, NGINX does not enable sticky session. If you want to use sticky session to connect to DC, refer to Using sticky session for Decision Center connection
-
Get the ODM endpoints.
Refer to the documentation to retrieve the endpoints. For example, on OpenShift you can get the route names and hosts with:
kubectl get routes --no-headers --output custom-columns=":metadata.name,:spec.host"
You get the following hosts:
my-odm-release-odm-dc-route <DC_HOST> my-odm-release-odm-dr-route <DR_HOST> my-odm-release-odm-ds-console-route <DS_CONSOLE_HOST> my-odm-release-odm-ds-runtime-route <DS_RUNTIME_HOST>
Using an Ingress, the endpoint is the address of the ODM ingress and is the same for all components. You can get it with:
kubectl get ingress my-odm-release-odm-ingress
You get the following ingress address:
NAME CLASS HOSTS ADDRESS PORTS AGE my-odm-release-odm-ingress <none> * <INGRESS_ADDRESS> 80 14d
-
Register the redirect URIs into your Microsoft Entra ID application.
The redirect URIs are built the following way:
Using Routes:
- Decision Center redirect URI:
https://<DC_HOST>/decisioncenter/openid/redirect/odm
- Decision Runner redirect URI:
https://<DR_HOST>/DecisionRunner/openid/redirect/odm
- Decision Server Console redirect URI:
https://<DS_CONSOLE_HOST>/res/openid/redirect/odm
- Decision Server Runtime redirect URI:
https://<DS_RUNTIME_HOST>/DecisionService/openid/redirect/odm
Using Ingress:
- Decision Center redirect URI:
https://<INGRESS_ADDRESS>/decisioncenter/openid/redirect/odm
- Decision Runner redirect URI:
https://<INGRESS_ADDRESS>/DecisionRunner/openid/redirect/odm
- Decision Server Console redirect URI:
https://<INGRESS_ADDRESS>/res/openid/redirect/odm
- Decision Server Runtime redirect URI:
https://<INGRESS_ADDRESS>/DecisionService/openid/redirect/odm
From the Azure console, in Microsoft Entra Id / Manage / App Registrations / ODM Application:
-
Click
Add Redirect URIs link
-
Click
Add Platform
-
Select
Web
-
Redirect URIs
Add the Decision Center redirect URI that you got earlier (https://<DC_HOST>/decisioncenter/openid/redirect/odm
-- don't forget to replace <DC_HOST> with your actual host name!) -
Check
Access Token
andID Token
-
Click
Configure
-
Click
Add URI Link
and enter another redirect URI- Repeat the previous step until all the redirect URIs have been entered
- Decision Center redirect URI:
-
Register the Rule Designer callback into your Microsoft Entra ID application.
The ODM Rule Designer will use the PKCE authorization code flow to connect to Decision Center and Decision Server Console.
From the Azure console, in Microsoft Entra Id / Manage / App Registrations / ODM Application:
Well done! You can now connect to ODM using the endpoints you got earlier and log in as an ODM admin with the account you created in 'manage groups and users' in the first article.
To be able to securely connect your Rule Designer to the Decision Server and Decision Center services that are running in Certified Kubernetes, you need to establish a TLS connection through a security certificate in addition to the OpenID configuration.
-
Get the following configuration files:
https://<DC_HOST>/decisioncenter/assets/truststore.jks
https://<DC_HOST>/decisioncenter/assets/OdmOidcProvidersRD.json
Where DC_HOST is the Decision Center endpoint.
-
Copy the
truststore.jks
andOdmOidcProvidersRD.json
files to your Rule Designer installation directory next to theeclipse.ini
file. -
Edit your
eclipse.ini
file and add the following lines at the end:-Dcom.ibm.rules.studio.oidc.synchro.scopes=<CLIENT_ID>/.default -Dcom.ibm.rules.studio.oidc.res.scopes=<CLIENT_ID>/.default -Djavax.net.ssl.trustStore=<ECLIPSEINITDIR>/truststore.jks -Djavax.net.ssl.trustStorePassword=changeme -Dcom.ibm.rules.authentication.oidcconfig=<ECLIPSEINITDIR>/OdmOidcProvidersRD.json
Where:
- changeme is the fixed password to be used for the default truststore.jks file.
- ECLIPSEINITDIR is the Rule Designer installation directory next to the eclipse.ini file.
-
Restart Rule Designer.
For more information, refer to the documentation.
Get hands-on experience with IBM Operational Decision Manager in a container environment by following this Getting started tutorial.
To manage ODM runtime call on the next steps, we used the Loan Validation Decision Service project
Import the Loan Validation Service in Decision Center connected using myodmuser@YOURDOMAIN created at step 2
Deploy the Loan Validation Service production_deployment ruleapps using the production deployment deployment configuration in the Deployments>Configurations tab.
You can retrieve the payload.json from the ODM Decision Server Console or use the provided payload.
As explained in the ODM on Certified Kubernetes documentation Configuring user access with OpenID, we advise to use basic authentication for the ODM runtime call for performance reasons and to avoid the issue of token expiration and revocation.
You can realize a basic authentication ODM runtime call the following way:
curl -H "Content-Type: application/json" -k --data @payload.json \
-H "Authorization: Basic b2RtQWRtaW46b2RtQWRtaW4=" \
https://<DS_RUNTIME_HOST>/DecisionService/rest/production_deployment/1.0/loan_validation_production/1.0
Where b2RtQWRtaW46b2RtQWRtaW4= is the base64 encoding of the current username:password odmAdmin:odmAdmin
But if you want to execute a bearer authentication ODM runtime call using the Client Credentials flow, you have to get a bearer access token using a client_assertion.
Before to generate the client_assertion, you need a keystore.jks that will be build using the previously generated myodmcompany.key private key and myodmcompany.crt public key PEM files with the commands:
openssl pkcs12 -export -out myodmcompany.p12 -inkey myodmcompany.key -in myodmcompany.crt -passout pass:changeme
keytool -importkeystore -srckeystore myodmcompany.p12 -srcstoretype pkcs12 -srcalias 1 -srcstorepass changeme -destkeystore myodmcompany.jks -deststoretype jks -deststorepass changeme -destalias myalias
Now you can generate the client_assertion following the ODM documentation.
java -cp $DCLIB/jrules-teamserver.jar:$DCLIB/jose4j-0.9.5.jar:$DCLIB/slf4j-api-1.7.25.jar com.ibm.rules.oauth.ClientAssertionHelper -clientId <CLIENT_ID> -tokenEndpoint https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token -keyAliasName myalias -keyStorePwd changeme -keyStoreLocation ./myodmcompany.jks
Now, generate the access token using the client_assertion:
curl -k -X POST -H "Content-Type: application/x-www-form-urlencoded" \
-d 'client_id=<CLIENT_ID>&scope=<CLIENT_ID>%2F.default&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=<CLIENT_ASSERTION>&grant_type=client_credentials' \
'https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token'
And use the retrieved access token in the following way:
curl -H "Content-Type: application/json" -k --data @payload.json \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
https://<DS_RUNTIME_HOST>/DecisionService/rest/production_deployment/1.0/loan_validation_production/1.0
If you encounter any issue, have a look at the common troubleshooting explanation