Tutorial
IBM MQ JWT authentication with JWKS for JMS applications
Configure an IBM MQ queue manager for JWT token authentication and connect a JMS client using JWKSSince version 9.4.0, IBM MQ has supported JSON Web Key Sets (JWKS) for JWT authentication. This feature provides a streamlined and secure way to manage public keys for token validation.
A JSON Web Token (JWT) is a compact, open standard that allows secure transmission of information. These tokens contain encoded information that verifies the identity of a user and determines which resources that user has permission to access.
A JSON Web Key Set (JWKS) provides a standardized approach to manage public keys for JWT token validation.
Important: This tutorial requires access to an x86-64 machine, because other platforms do not currently support JWKS. All host terminal instructions in this tutorial are performed on a Linux machine.
What is JWKS?
JWKS (JSON Web Key Set) is a document that holds a collection of JSON Web Keys, each representing a cryptographic key with unique attributes. These attributes let you know how the key is to be used. Here’s a quick look at some of these key attributes:
- kty: The key type, such as RSA or EC.
- kid: Key ID, a unique identifier for each key.
- use: Key usage, indicating if the key is for signing (sig) or encryption (enc).
- alg: Cryptographic algorithm associated with the key, like RS256.
Since a JWKS only contains public (or symmetric) keys, there's no risk of private keys being compromised. These public keys are what IBM MQ uses to verify client JWTs by confirming the token issuer and signature.
Why use JWKS over just JWT?
While JWT provides a compact way to transmit data securely, JWKS adds another layer of convenience and security. With JWKS, IBM MQ can dynamically retrieve and validate keys directly from a JWKS endpoint, eliminating the need to handle and store public keys manually. This has several benefits:
- Centralized key management: JWKS allows for centralized key control, making it easier to rotate keys and ensuring that clients stay updated without needing individual updates.
- Dynamic key discovery: MQ can pull the latest keys automatically from a JWKS URL.
- Enhanced security with key ID (kid): The kid attribute ensures that the correct public key is used to validate each JWT.
Therefore, JWKS enables a more scalable, secure, and error-resistant mechanism for public key management compared to a standalone JWT.
How does JWT authentication work with JWKS and IBM MQ?
JWT authentication works as follows:
Deploy a token issuer to obtain JWTs and a JWKS: A token issuer is an entity that creates and signs tokens. It can be an LDAP user registry, a single sign-on service, or an open-source application such as Keycloak. Once this authorization server is set up and accessible, your client and queue manager can retrieve tokens and a JWKS.
Configure IBM MQ to trust and connect to the token issuer: Your MQ queue manager will then need to be configured with the JWKS endpoint to be able to validate your client’s token.
Retrieve the JWKS for token validation: The queue manager retrieves the JWKS from the authorization server using HTTPS to ensure integrity. It uses the JWKS endpoint configured earlier to do so.
The application sends over the JWT: Your messaging client application uses the JWT endpoint to retrieve a token and passes it into the queue manager at connection time.
Queue manager validation: Each JWT header contains a kid that identifies which public key should validate it. IBM MQ’s queue manager compares this kid in the client’s token with the kids provided in the JWKS.
Verifying the signature for authentication: Finally, the queue manager uses the appropriate public key from the JWKS to confirm the JWT’s signature, allowing authenticated clients to access resources in MQ securely.
Architecture for configuring token-based authentication
In this tutorial, you learn how to configure an MQ queue manager to process JWTs using a JWKS and run a JMS client that will communicate with your queue manager using token-based authentication.
The following diagram illustrates the components that you set up in this tutorial. The token issuer and queue manager run in separate containers on your machine, and your JMS client runs as a standalone application.

Prerequisites
- Podman or Docker
- Maven to compile Java applications
- Java Software Development Kit (JDK), Version 11, to develop and run applications
- OpenSSL to create a keystore and self-signed certificate to enable HTTPS communication
Steps
- Step 1. Set up a Keycloak server in a container
- Step 2. Configure your queue manager to accept authentication via JWKS
- Step 3. Configure and run your JMS client
Step 1. Set up a Keycloak server in a container
You run a Keycloak server in a container.
For JWKS authentication, an HTTPS certificate creates a secure connection between the queue manager and the Keycloak server (token issuer). In production, you use certificates that a certificate authority issues.
In this development sample, you create a local keystore and self-signed certificate to enable HTTPS communication:
Open a terminal and execute the following command to create a Keycloak keystore directory:
mkdir keycloakKeystoreUse OpenSSL to create your private and public keys, and then combine them to create your keystore:
openssl req -newkey rsa:2048 -nodes -keyout keycloakKeystore/keycloakPrivate.pem -x509 -days 365 -out keycloakKeystore/keycloakPublic.pem -subj "/C=GB/ST=MYSTATE/L=MYCITY/O=MYORG, Inc./OU=IT/CN=<hostname>" -addext "subjectAltName=DNS:<hostname>,IP:127.0.0.1"Replace
hostnamewith the host name of your host machine. To find out your machine’s host name, run a simplehostnamecommand in your terminal.Then run the following command to create your keystore.
openssl pkcs12 -inkey keycloakKeystore/keycloakPrivate.pem -in keycloakKeystore/keycloakPublic.pem -export -out keycloakKeystore/keycloak.p12Make sure that you set a keystore password when prompted to do so.
Confirm your keystore has been created using this command:
openssl pkcs12 -in keycloakKeystore/keycloak.p12 -noout -infoYou should see output similar to this:

Give this keystore the necessary permissions to be read.
chmod 770 keycloakKeystore && chmod 440 keycloakKeystore/keycloak.p12Start the Keycloak server, passing the keystore directory as a volume by running the following command.
Remember to edit both passwords in the command. Replace
KEYCLOAK_ADMIN_PASSWORDwith a password of your choice, replacehttps-key-store-passwordwith the password that you used to create your Keycloak keystore, and replacepathwith the path to your Keycloak keystore directory.podman run -p 32030:32030 -e KEYCLOAK_ADMIN=kcadmin -e KEYCLOAK_ADMIN_PASSWORD=passw0rd -v /path/to/keycloakKeystore/:/path/to/keycloakKeystore/ quay.io/keycloak/keycloak:latest start --hostname-strict=false --https-key-store-file=/path/to/keycloakKeystore/keycloak.p12 --https-key-store-password=password --https-port=32030Next, use the Keycloak console for the rest of the configuration.
By default, your Keycloak server issues lightweight tokens, which the queue manager does not accept. To address this issue, change the Keycloak configuration. Open a web browser and go to your JWKS endpoint,
https://<hostname>:32030, specifying your host name.
Log in using your credentials that you set up when starting your Keycloak server. If you used the same credentials that were used in this tutorial, then these credentials are
kcadminandpassw0rd.Go to Clients > admin-cli > Advanced > Advanced settings. Then, click on the toggle to turn the lightweight token off and click Save.

Now, create your messaging app user by using the console. Navigate to the Users tab on the left side menu, and click Add user.

In the displayed dialog, name your user
appso that MQ can authorize your token. Then, click Create.
Navigate to the top panel, and click the Credentials tab.
Click Set password. Specify a password, turn off Temporary, and then click Save.

The JMS client application presents this Keycloak user when it fetches an authentication token from the JWT endpoint.
Now, check that your server works and serves both a JWT and a JWKS with your new Keycloak user credentials.
Open up a new terminal window and execute the following commands.
To fetch a JWT (as needed by the client application), run this command:
curl -k -X POST "https://<hostname>:32030/realms/master/protocol/openid-connect/token" -H "Content-Type: application/x-www-form-urlencoded" -d "username=app" -d "password=passw0rd" -d "grant_type=password" -d "client_id=admin-cli"Then, to fetch a JWKS response (as needed by the validating queue manager), run this command:
curl -k -X GET https://<hostname>:32030/realms/master/protocol/openid-connect/certs
You now have a token issuer running in a container.
Step 2. Configure your queue manager to accept authentication via JWKS
The first step is to set up a queue manager that runs in a container with an external volume for persistence. If you do not already have one set up, complete the steps in the “Get an IBM MQ queue for development in a container” tutorial.
If you are working on a Windows machine, follow this IBM MQ container setup guide for WSL to set up a customized IBM MQ container on WSL. All host terminal instructions in this tutorial are performed in a Linux subsystem.
Now, if you run podman ps, you should see 2 containers running. One is your queue manager, and the other is the Keycloak server.

Your queue manager has a file called qm.ini. This file contains your queue manager’s configuration and must be edited so the queue manager can find your JWKS endpoint and keystore. Edit the file locally, and then copy it back to your queue manager. You can edit the file either in a terminal or by using an editor of your choice.
For convenience, this file is in the mq-dev-patterns repository. This repository also contains the JMS samples that you run. Run the following command to clone the repository from GitHub.
git clone https://github.com/ibm-messaging/mq-dev-patterns.git
Change to the jwt-jwks-docs directory:
cd mq-dev-patterns/jwt-jwks-docs
If you run an ls command, you should see the qm.ini file listed. Edit this file.
Edit the SSL stanza in the
qm.inifile, and add a JWKS stanza at the end. Make sure that you replace the hostname with your host name. When you are done, it should look like this:
The JWKS endpoint contains 3 important fields:
- IssuerName: Refers to URL of your token issuer. It is the ‘iss’ claim in a JWT.
- Endpoint: This is the URL to access the public keys to verify your JWT’s signature.
- UserClaim: Declares which field in an incoming token will be taken as the MQ user identity.
Copy this file back into your queue manager.
podman cp qm.ini QM1:/var/mqm/qmgrs/QM1/ cd ../..You must also give your queue manager the Keycloak server’s public certificate so that it can trust the token issuer.
podman cp keycloakKeystore/keycloakPublic.pem QM1:/var/mqm/qmgrs/QM1/ssl/Next, create your queue manager’s keystore, which is where the Keycloak public certificate is imported into.
To do so, you need command-line access inside the queue manager:
podman exec -ti QM1 bashIf your container is not called ‘QM1’, change the previous command to use the name of your container.
Run the following command to create your queue manager’s keystore in the default location.
opt/mqm/bin/runmqakm -keydb -create -db /var/mqm/qmgrs/QM1/ssl/mqdefcer.p12 -pw password -type pkcs12 -stashNow, import the Keycloak’s certificate into your queue manager keystore.
runmqakm -cert -add -db /var/mqm/qmgrs/QM1/ssl/mqdefcer.p12 -pw password -label keycloakPublicLabel -file /var/mqm/qmgrs/QM1/ssl/keycloakPublic.pemRefresh security in the queue manager so that it can recognize the JWKS endpoint and read the keystore by running these 2 commands:
runmqsc REFRESH SECURITYRun
exittwice to return to your home directory on your host machine.
Your queue manager can now trust the Keycloak server.
Step 3. Configure and run your JMS client
To connect to the Keycloak server, your JMS application will need a Java keystore holding the Keycloak public certificate to validate the Keycloak server’s HTTPS certificate.
Import the Keycloak public certificate into a Java keystore.
keytool -importcert -file keycloakKeystore/keycloakPublic.pem -keystore clientkey.jks -alias "jmsClientHttps"Follow the prompts asking you to enter both passwords for your new Java keystore and your source keystore.
You can check whether the certificate was successfully imported with this command:
keytool -list -v -keystore clientkey.jksChange directories to the
mq-dev-patternsdirectory, and modify theenv.jsonfile to replace the queue manager endpoints and the JWT endpoints with the following code. Be sure to update<hostname>in theJWT_TOKEN_ENDPOINTvariable, and specify theJWT_TOKEN_USERNAMEandJWT_TOKEN_PWDvalues for theappuser that you set up earlier by using the Keycloak console.{ "MQ_ENDPOINTS": [{ "HOST": "127.0.0.1", "PORT": "1415", "CHANNEL": "DEV.APP.SVRCONN", "QMGR": "*QM1TLS", "APP_USER": "app", "APP_PASSWORD": "passw0rd", "QUEUE_NAME": "DEV.QUEUE.1", "BACKOUT_QUEUE": "DEV.QUEUE.2", "MODEL_QUEUE_NAME": "DEV.APP.MODEL.QUEUE", "DYNAMIC_QUEUE_PREFIX": "APP.REPLIES.*", "TOPIC_NAME": "dev/" }], "JWT_ISSUER" : { "JWT_TOKEN_ENDPOINT":"https://<hostname>:32030/realms/master/protocol/openid-connect/token", "JWT_TOKEN_USERNAME":"app", "JWT_TOKEN_PWD":"passw0rd", "JWT_TOKEN_CLIENTID":"admin-cli" } }Navigate to the JMS directory, and build the samples with Maven using the following command.
mvn clean packageRun the following command to run the JmsPut sample.
java -Djavax.net.ssl.trustStore=/path/to/clientkey.jks -Djavax.net.ssl.trustStorePassword=password -cp target/mq-dev-patterns-0.1.0.jar com.ibm.mq.samples.jms.JmsPut
Your output should look similar to this, and you should be able to see your client fetching a token (deliberately blurred in the following image) from your Keycloak server.

Your client application has successfully connected to your IBM MQ queue manager via JWT-based authentication.
Troubleshooting JWT authentication
Setting up JWT authentication for a queue manager and the MQI client with a token issuer requires configuration on all 3 sides: the Keycloak server, the queue manager, and the JMS client. The details can be easy to miss, which can lead to confusing errors.
Before you review specific cases, see the following documentation to understand the error codes that you might encounter.
For more troubleshooting information, see the MQ Cheat Sheet for Developers for common IBM MQ errors.
A common issue: Are your containers still up and running?
In this tutorial, you set up 2 containers on your host machine: the queue manager and the Keycloak server. If either container stops, you might see a “connection refused” error like the one in the following figure when the Keycloak server is not running.

Or, you might see a “failed to connect” error like the one in the following figure, when the queue manager is not running.

To check if the containers are up and running, run a simple podman ps, which will show you all containers that are currently running.
The Access Token not found error
A JSON parsing exception such as the “access token not found” error usually implies that there is an issue with your Keycloak server configuration or that your env.json file has invalid endpoints.

First, check if you can fetch a token:
curl -k -X POST "https://<hostname>:32030/realms/master/protocol/openid-connect/token" -H "Content-Type: application/x-www-form-urlencoded" -d "username=app" -d "password=passw0rd" -d "grant_type=password" -d "client_id=admin-cli"
If you see a message that says {"error":"invalid_grant","error_description":"Invalid user credentials"}, this suggests that you either did not create an app user or set it up as a temporary user. Return to the Keycloak web console, and add a new user as described at the end of step 1.
If you already have the app user but did not turn off Temporary, you must delete that user and recreate the app user.

Once the curl command works and you can successfully fetch a token, try running the JMS app again. If the issue persists, check your env.json file for incorrect endpoints/credentials.
The 2035 MQRC unauthorized error
Whenever you see the 2035 error code, it means that something went wrong with the interaction between your client and MQ. To understand the specific issue, inspect the error logs.
The default location for the errors subdirectory is /var/mqm/qmgrs/<queue manager name>/errors on AIX and Linux systems or C:\Program Files\IBM\MQ\qmgrs\<queue manager name>\errors on Windows systems.
If you followed this tutorial and you have a queue manager running in a Linux-based container, you must first attach to the container to get command-line access.
podman exec -ti QM1 bash
Now navigate to the errors subdirectory:
cd /var/mqm/qmgrs/QM1/errors/
If you now run ls, you should see the error logs:

The latest logs are written to the AMQERR01.LOG file:
cat AMQERR01.LOG
The 111 error code
A common error code is 111. This code means that your qm.ini file either does not have a JWKS stanza or has one with invalid values.

Ensure that the following stanza is at the bottom of your qm.ini file with the correct IssuerName claim:

You can use jwt.io to check your IssuerName. This website allows you to decode tokens, format them clearly, and check associated attributes such as IssuerName.
Use the curl command from earlier to fetch a token. Then, copy the access token section and paste it into the jwt.io website.

You can see all the claims from the token on the right side. The iss claim refers to the IssuerName. Once you correct the IssuerName in the qm.ini file, run the refresh security MQSC command to update the queue manager with the new configuration.
The 107 error code
Another error code that you might see is 107. This code usually means that your token does not have a user claim that the queue manager can adopt for the user ID. This issue tends to happen when your Keycloak server issues lightweight tokens, which results in insufficient information for the queue manager to validate that token.

Go to the Keycloak web console, Clients > admin-cli > Advanced > Advanced settings and switch lightweight token off.

Summary
Congratulations! In this tutorial, you learned how IBM MQ integrates token authentication by using JWTs and JWKS. You also learned how to configure an IBM MQ queue manager to enable token authentication and then connect a JMS application to the queue manager to put messages onto a queue.
To learn more about how IBM MQ works with authentication tokens, take a look at the IBM MQ authentication tokens documentation.