Configuring JWT Private Key Authentication with WSO2 IS and OpenSSL
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:
- The microservice generates a JWT, signs it with its private key, and sends it to WSO2 IS.
- WSO2 IS verifies the JWT signature using the public key uploaded earlier.
- If the JWT is valid, WSO2 IS issues an OAuth token that the microservice can use to interact with the protected APIs.
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.
- Log in to the WSO2 IS management console. (https://localhost:9443/console/getting-started)
- 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
3. Navigate to the Edit -> Protocol section of the created application
4. Enable Private Key JWT
5. Upload the public_cert.pem file and save changes
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.
- 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. - One-time Use: The authorization server will keep track of the
jti
values it has received. Once a specificjti
has been used, the server will not accept another token with the samejti
. - 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.
- Access the your application (https://oidcdebugger.com)
- Add client ID, redirection URI and authentication end point and click login. User will redirect to the Identity serve authentication end point.
3. Add username and password and click login
4. Copy the generated Authorization code
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 😊