Automating Enterprise Integrations: Mastering JWT Bearer Flow in Java

Published on 30 January 2026 at 13:05

Information

In modern cloud environments ecure service-to-service communication often relies on JSON Web Tokens (JWT). Unlike simple Base64 encoding, the JWT Bearer Flow uses a digital signature created with a Private Key (RS256) to prove the requester's identity to an authorization server.

Step 1: Add the Necessary Library

While Java's standard libraries handle some security, using a specialized library like Auth0's java-jwt is the industry standard for safe signing.

Maven Dependency (Auth0):

<dependency>
 <groupId>com.auth0</groupId>
 <artifactId>java-jwt</artifactId>
 <version>4.5.0</version>
</dependency>

 

Step 2: Prepare the Private Key

Your JSON details typically contain a private_key in PKCS#8 format. Java requires you to convert this string into a RSAPrivateKey object before it can be used to sign a token.

 

import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

// Clean the PEM string and convert to RSAPrivateKey
private RSAPrivateKey getPrivateKey(String pemKey) throws Exception {
String cleanKey = pemKey.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", ""); // Remove all whitespace
byte[] decoded = Base64.getDecoder().decode(cleanKey);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
return (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(spec);
}

Step 3: Create and Sign the JWT

The JWT consists of a header and specific "claims" (the payload). These claims identify who you are (iss), what you want to access (aud), and how long the token is valid (exp).

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;

public String createAssertion(RSAPrivateKey privateKey) {
Algorithm algorithm = Algorithm.RSA256(null, privateKey); // RS256 signing
long now = System.currentTimeMillis();

return JWT.create()
.withKeyId("your_private_key_id") // 'kid' header claim
.withIssuer("client-email@example.com") // 'iss'
.withAudience("https://oauth2.example.com/token") // 'aud'
.withIssuedAt(new Date(now)) // 'iat'
.withExpiresAt(new Date(now + 3600000)) // 'exp' (set for 1 hour)
.sign(algorithm);
}

Step 4: Exchange the JWT for an Access Token

You do not send this signed JWT directly to your final API. Instead, you send it to the token_uri to receive an Access Token.

import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;

public void exchangeToken(String signedAssertion) throws Exception {
String form = "grant_type=" + java.net.URLEncoder.encode("urn:ietf:params:oauth:grant-type:jwt-bearer", "UTF-8") +
"&assertion=" + signedAssertion;

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://oauth2.example.com/token"))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(form))
.build();

HttpResponse < String > response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Access Token Body: " + response.body()); // Parse JSON for access_token
}

Security Best Practices for Java

  • Key Protection: Never hardcode your private key in your source code. Use environment variables or a secure secret manager like AWS Secrets Manager or Google Cloud KMS.

  • Restrictive Permissions: If storing keys on a local filesystem, ensure only the user running the application has read-only access.

     

  • Timeouts: Keep your JWT expiration (exp) as short as possible (e.g., 5 minutes) to reduce the risk if a token is intercepted.

     

  • Audit Usage: Log token generation events to monitor for anomalies, but never log the actual private key or tokens themselves.

 

 


Add comment

Comments

There are no comments yet.