Configuring JWT Private Key Authentication with WSO2 IS and OpenSSL

Achini Udari Jayasena
6 min readOct 27, 2024

--

Introduction

In the modern world of API security, OAuth 2.0 and OpenID Connect (OIDC) play a pivotal role in ensuring secure communications between clients and servers. One of the secure mechanisms for client authentication under these protocols is JWT (JSON Web Token) with private key authentication. In this article, we will walk through how to configure JWT private key authentication for WSO2 Identity Server (IS) using OpenSSL, along with a real-world scenario.

What is JWT Private Key Authentication?

JWT private key authentication is a client authentication mechanism where the client (application) signs a JSON Web Token (JWT) using its private key. The server then verifies this signature using the corresponding public key. This ensures that only the client that possesses the private key can authenticate successfully.

This method is particularly useful for confidential clients — applications that can safely store secrets — such as backend services, servers, or applications running in secure environments.

Real-World Scenario

Securing API Communication Between a Microservice and an Identity Server

Let’s consider a real-world scenario where we have a microservice that needs to securely communicate with WSO2 Identity Server to request tokens. To ensure security, the microservice uses JWT private key authentication to prove its identity to the Identity Server.

The following steps show how to set up JWT private key authentication between the microservice and WSO2 Identity Server using OpenSSL.

Real-World Scenario Explained:

Let’s say you have an e-commerce microservice that needs to interact with WSO2 Identity Server to get access tokens to access customer order APIs. This microservice uses JWT private key authentication to securely communicate with the Identity Server and retrieve tokens.

Here’s the workflow:

  1. The microservice generates a JWT, signs it with its private key, and sends it to WSO2 IS.
  2. WSO2 IS verifies the JWT signature using the public key uploaded earlier.
  3. If the JWT is valid, WSO2 IS issues an OAuth token that the microservice can use to interact with the protected APIs.
Source: WSO2 (2023). Private Key JWT authentication in WSO2 Identity Server #Identityin15. [online] YouTube. Available at: https://www.youtube.com/watch?v=UhVMAZ2LX5Y

This ensures a secure, non-repudiable mechanism for client authentication, crucial for APIs handling sensitive data like customer orders.

Step-by-Step Guide to Configure JWT Private Key Authentication

Step 01: Install OpenSSL (if it’s not installed):

sudo apt update
sudo apt install openssl

Step 2: Generate a Private Key and Public Certificate

You need to generate a private key and the corresponding public certificate. You can do this using OpenSSL.

Generate private key

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

Generate public certificate from the private key

openssl req -new -x509 -key private_key.pem -out certificate.pem -days 365

You now have two files:
- private_key.pem: The private key that will be used to sign the JWT.
- public_cert.pem: The public certificate that will be uploaded to WSO2 Identity Server to verify the JWT signature.

Step 3: Upload the Public Certificate to WSO2 Identity Server

Next, you need to upload the public certificate to WSO2 Identity Server for the client application.

  1. Log in to the WSO2 IS management console. (https://localhost:9443/console/getting-started)
  2. Navigate to Applications -> Click on create new application -> Select Traditional web application-> Select OIDC as the Protocol and create application.

Application name : Application

Redirect URI : https://oidcdebugger.com/debug

Authors work

3. Navigate to the Edit -> Protocol section of the created application
4. Enable Private Key JWT

Authors work

5. Upload the public_cert.pem file and save changes

Authors Work

Step 4: Create the JWT Payload

Now, you need to create the JWT payload. This JWT will include several claims like the issuer (iss), subject (sub), audience (aud), expiration time (exp), issued at time (iat), and a unique identifier (jti).

Here’s an example of a payload you’ll use for the JWT:

cat <<EOF > payload.json
{
"iss": "YOUR_CLIENT_ID",
"sub": "YOUR_CLIENT_ID",
"aud": "https://localhost:9443/oauth2/token",
"exp": $(($(date +%s) + 300)),
"iat": $(date +%s),
"jti": "$(uuidgen)"
}
EOF

In this payload:
- iss and sub are set to the client ID.
- aud is the URL of the token endpoint.
- exp is the expiration time (in seconds from epoch). Here, its expire 5 minutes in the future
- iat is the issued-at time.
- jti is a unique identifier for this token. You could, for example, generate a UUID or use a timestamp-based unique string.

JTI

The jti (JWT ID) is a unique identifier for a specific JWT. Its primary purpose is to prevent "replay attacks," where someone could capture and reuse a JWT to try to gain unauthorized access.

  1. Uniqueness: Each time a JWT is created, a unique jti value (like a UUID) is added to the payload. This ensures the JWT is unique.
  2. One-time Use: The authorization server will keep track of the jti values it has received. Once a specific jti has been used, the server will not accept another token with the same jti.
  3. Replay Prevention: By rejecting reused jti values, the server prevents replay attacks where someone could capture a JWT and reuse it in a new request.

Step 5: Generate JWT Assertion

1. Generate JWT header

echo -n '{"alg":"RS256","typ":"JWT"}' | openssl base64 -e | tr -d '=' | tr '/+' '_-' > header_base64.txt

2. Generate the Base64 Encoded Payload

openssl base64 -in payload.json -e | tr -d '=' | tr '/+' '_-' > payload_base64.txt

3. Sign the JWT Using the Private Key
Concatenate the header and payload, sign the result, and encode it in base64:

# Combine header and payload
echo -n "$(cat header_base64.txt).$(cat payload_base64.txt)" > signing_input.txt

# Create the signature
openssl dgst -sha256 -sign private_key.pem -out signature.bin signing_input.txt

# Base64 encode the signature and make it URL safe
openssl base64 -in signature.bin -e | tr -d '=' | tr '/+' '_-' > signature_base64.txt

# Combine header, payload, and signature to create the JWT
echo -n "$(cat header_base64.txt).$(cat payload_base64.txt).$(cat signature_base64.txt)" > jwt_assertion.txt

Now you should be able to use jwt_assertion.txt in your cURL command for request access token

Step 6: Test the Token in OIDC Debugger

You can use an external tool like [OIDC Debugger](https://oidcdebugger.com) to test the token exchange and see if everything works as expected. Simply copy the jwt_assertion and use it in the OAuth flow in the debugger.

  1. Access the your application (https://oidcdebugger.com)
  2. Add client ID, redirection URI and authentication end point and click login. User will redirect to the Identity serve authentication end point.
Authors work

3. Add username and password and click login

Authors work

4. Copy the generated Authorization code

Authors work

Step 7: Make the Token Request

Now, with your JWT created and signed, you can use it in the client assertion to request an OAuth token. Use the following curl command to request the token:

curl --location --request POST 'https://localhost:9443/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'code=<AUTHORIZATION_CODE>' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'redirect_uri=https://oidcdebugger.com/debug' \
--data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode "client_assertion=$(cat jwt_assertion.txt)" \
--data-urlencode 'scope=openid' \
-k

Conclusion

JWT private key authentication provides a robust, secure way for applications to authenticate with Identity Servers like WSO2 IS. By following the steps above, you can set up this authentication mechanism, ensuring only the application with the private key can obtain OAuth tokens. This approach is ideal for backend services and microservices in distributed architectures.

Feel free to experiment with the setup and adapt it to your specific use cases!

🔐 Unlock IAM Excellence!

📖 Follow me on Medium for insights on into Identity and Access Management strategies, WSO2 Identity Server, Asgardeo and tech trends. Connect with me on LinkedIn and Twitter for more content!

📧 Got questions? Email me at aaujayasena@gmail.com 😊

--

--

Achini Udari Jayasena
Achini Udari Jayasena

Written by Achini Udari Jayasena

🌟 With over 8 years in IT, I'm Senior Software Quality Engineer, dedicated to delivering excellence. Let's build exceptional software experiences together

No responses yet