---------------------------
Java code
---------------------------
package org.example;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.ECDSASigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.Security;
import java.security.interfaces.ECPrivateKey;
import java.time.Instant;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
public class Main {
// === CONFIGURE THESE ===
private static final String CLIENT_ID = "<PLACEHOLDER>"; // Fill this placeholder with your Okta client ID ( example: 0oa9ywfrsjocYDtGN0x2 )
private static final String TOKEN_ENDPOINT = "<PLACEHOLDER>"; // Fill this placeholder with the Okta token URL ( example: https://login.qa.circlek.eu/oauth2/aus4j5ruks4P09nKP0x7/v1/token )
private static final String SCOPE = "<PLACEHOLDER>" ; // Fill this placeholder with the Okta scope (example: eu_b2b_external_transaction_service or space separated list of scopes for your app if there is more than one scope needed)
private static final String PRIVATE_KEY_PATH = "<PLACEHOLDER>"; // Fill this placeholder with the file containing your PRIVATE key (example: qa-private.pem)
private static final String API_URL = "<PLACEHOLDER>"; // # Fill this placeholder with the API URL (example: https://api.uat.circlek.com/eu/transaction-api)
private static final String GRANT_TYPE = "client_credentials";
static {
Security.addProvider(new BouncyCastleProvider());
}
public static void main(String[] args) throws Exception {
String privateKeyPem = Files.readString(Paths.get(PRIVATE_KEY_PATH));
ECPrivateKey privateKey = loadECPrivateKey(privateKeyPem);
// 1. Create JWT client assertion
String clientAssertion = createClientAssertion(privateKey);
// 2. Request Access Token
String accessToken = requestAccessToken(clientAssertion);
// 3. Call Ping Endpoint
callPingEndpoint(accessToken);
}
private static ECPrivateKey loadECPrivateKey(String pem) throws Exception {
Reader rdr = new StringReader(pem);
Object parsed = new PEMParser(rdr).readObject();
KeyPair pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair) parsed);
return (ECPrivateKey) pair.getPrivate();
}
private static String createClientAssertion(ECPrivateKey privateKey) throws Exception {
Instant now = Instant.now();
JWTClaimsSet claims = new JWTClaimsSet.Builder()
.issuer(CLIENT_ID)
.subject(CLIENT_ID)
.audience(TOKEN_ENDPOINT)
.issueTime(java.util.Date.from(now))
.expirationTime(java.util.Date.from(now.plusSeconds(3000)))
.build();
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES256)
.type(JOSEObjectType.JWT)
.build();
SignedJWT signedJWT = new SignedJWT(header, claims);
signedJWT.sign(new ECDSASigner(privateKey));
return signedJWT.serialize();
}
private static String requestAccessToken(String clientAssertion) throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
String form = "scope=" + SCOPE +
"&grant_type=" + GRANT_TYPE +
"&client_assertion=" + clientAssertion +
"&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(TOKEN_ENDPOINT))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(form))
.build();
System.out.println("*** REQUESTING OKTA TOKEN ***");
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("ACCESS TOKEN RESPONSE: " + response.body());
// Basic extraction of token (ideally use JSON parser)
return extractTokenFromJson(response.body());
}
private static String extractTokenFromJson(String json) {
// Super basic parser (use Jackson or Gson in production)
String marker = "\"access_token\":\"";
int start = json.indexOf(marker) + marker.length();
int end = json.indexOf("\"", start);
return json.substring(start, end);
}
private static void callPingEndpoint(String accessToken) throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
String pingUrl = API_URL + "/ping";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(pingUrl))
.header("Authorization", "Bearer " + accessToken)
.GET()
.build();
System.out.println("*** CALLING PING ENDPOINT USING OBTAINED OKTA TOKEN ***");
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("URL: " + pingUrl);
System.out.println("STATUS CODE: " + response.statusCode());
System.out.println("RESPONSE BODY: " + response.body());
}
}
---------------------------
POM dependencies
---------------------------
<dependencies>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.37.3</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.81</version>
</dependency>
</dependencies>