wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

JWT for machine to machine communication

Hero image for JWT for machine to machine communication

There are plenty of IdP available that provide OIDC flows for your users, including Domino, even if some can't read RFC 6749) properly (I'm looking at you Okta and Ping)*

When it comes to machine identity the situation becomes murkier. Machines might or might not be in your directory, directory admins might be reluctant to add machines etc. etc. So some utility needs to help out here

Generating access_token

I wrote about about generating JWT before, but in needs more context. So lets have a look what is actually needed:

  • a means to identify the machine
  • the shape of the access token required
  • the trusted key pair to sign and validate the key

Identifying machines

There are plenty of options available:

  • ClientId & ClientSecret (fancily renamed username & password)
  • API Token
  • IP addresses (not very secure)
  • mTLS (my personal favorite)

The shape of the access token required

It's easy to deduct the shape from an existing access token into a JSON template. You want to understand the parameters, so you can strip out time related parameters.

the trusted key pair to sign and validate the key

A key pair typically is RSA or ECC. You need to protect the private key. The public key is needed by your application to check the integrity of the access token.

Sample code

I put together a sample project to illustrate how this works.

  • Generates a key pair on startup i missing
  • accepts submission of new JSON shapes per ClientId. Replies with ClientId & ClientSecret
  • generates the access_token on presentation of a valid clientId & clientSecret

This is not production grade code, you want to harden it, switch to mTLS, implement key rotation etc. At it's core it's just a few lines:

String createJwt(JsonObject payload) {
    try {
      JsonObject header = new JsonObject().put("alg", "ES256").put("typ", "JWT").put("kid", keyId);
      String signingInput = encode(header.encode()) + "." + encode(withExpiry(payload).encode());
      Signature signer = Signature.getInstance("SHA256withECDSA");
      signer.initSign(privateKey);
      signer.update(signingInput.getBytes(StandardCharsets.UTF_8));
      return signingInput + "." + encode(toJoseSignature(signer.sign()));
    } catch (Exception exception) {
      throw new IllegalStateException("Unable to create JWT", exception);
    }
  }

As usual YMMV

* RFC 6749 states: "The value of the scope parameter is expressed as a list of space-
delimited, case-sensitive strings.
". The culprits mechanically interpreted "list" as "Array" and thus send scope or scp as JS array, happily ignoring "space-delimited". Grieving about this before


Posted by on 20 September 2026 | Comments (0) | categories: Java JWT Quarkus

Comments

  1. No comments yet, be the first to comment