NATS Logo by Example

Auth Callout - Centralized in Authentication and Authorization

Auth callout is a new feature in NATS v2.10.0 providing an extension point for integrating with alternative identity and access management (IAM) backends. See the official docs for more details.

This example demonstrates how auth callout can be configured within server configuration. This is combined with a basic service that handles the authorization requests delegated by the NATS server.

CLI Go Python JavaScript Rust C# Java Ruby Elixir Crystal C
Jump to the output or the recording
$ nbe run auth/callout/java
View the source code or learn how to run this example yourself

Code

package example;


import io.nats.client.*;
import io.nats.jwt.*;
import io.nats.nkey.NKey;
import io.nats.service.*;
import io.nats.client.Connection;
import io.nats.client.impl.Headers;


import java.io.IOException;
import java.security.GeneralSecurityException;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;


import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;


import static io.nats.jwt.JwtUtils.getClaimBody;


public class Main {


The main method starts a Service that listens to the Auth Callback subject. The real work for the example is in the AuthCalloutHandler


  public static void main(String[] args) throws Exception {
    String natsURL = System.getenv("NATS_URL");
    if (natsURL == null) {
      natsURL = "nats://127.0.0.1:4222";
    }


    Options options = new Options.Builder()
        .server(natsURL)
        .errorListener(new ErrorListener() {})
        .userInfo("auth", "auth")
        .build();


    try (Connection nc = Nats.connect(options)) {

Endpoints can be created ahead of time or created directly by the ServiceEndpoint builder.

      Endpoint endpoint = Endpoint.builder()
          .name("AuthCallbackEndpoint")
          .subject("$SYS.REQ.USER.AUTH")
          .build();

Create the ServiceEndpoint from the Endpoint The AuthCalloutHandler class is a ServiceMessageHandler

      AuthCalloutHandler handler = new AuthCalloutHandler(nc);
      ServiceEndpoint serviceEndpoint = ServiceEndpoint.builder()
          .endpoint(endpoint)
          .handler(handler)
          .build();

Create the Service from ServiceEndpoint.

      Service acService = new ServiceBuilder()
          .connection(nc)
          .name("AuthCallbackService")
          .version("0.0.1")
          .addServiceEndpoint(serviceEndpoint)
          .build();


      System.out.println("\n" + acService);

Start the services

      CompletableFuture<Boolean> serviceStoppedFuture = acService.startService();

This just gives keeps the main function alive for plenty of time to finish running the main.sh example script

      Thread.sleep(20000);

A real service will do something like this serviceStoppedFuture.get();

    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }


The AuthCalloutHandler is the service implementation that handles the Auth Callout from the server. This is the real work for this example.


  static class AuthCalloutHandler implements ServiceMessageHandler {
    static String ISSUER_NSEED = "SAANDLKMXL6CUS3CP52WIXBEDN6YJ545GDKC65U5JZPPV6WH6ESWUA6YAI";


    static final Map<String, AuthCalloutUser> NATS_USERS;


    static final NKey USER_SIGNING_NKEY;
    static final String USER_SIGNING_PUBLIC_KEY;


    static {

Set up the relevant NKey and public key

      try {
        USER_SIGNING_NKEY = NKey.fromSeed(ISSUER_NSEED.toCharArray());
        USER_SIGNING_PUBLIC_KEY = new String(USER_SIGNING_NKEY.getPublicKey());
      }
      catch (Exception e) {
        throw new RuntimeException(e);
      }

This sets up a map of users to simulate a back end auth system

      NATS_USERS = new HashMap<>();

sys/sys is a SYS account and does not have any restrictions

      NATS_USERS.put("sys", new AuthCalloutUser().userPass("sys").account("SYS"));

alice/alice, is an APP account with no publish or subscribe restrictions

      NATS_USERS.put("alice", new AuthCalloutUser().userPass("alice").account("APP"));

bob/bob is an APP account, can publish only to “bob.>”, can subscribe to only “bob.>”

      Permission pb = new Permission().allow("bob.>");
      ResponsePermission r = new ResponsePermission().max(1);
      NATS_USERS.put("bob", new AuthCalloutUser().userPass("bob").account("APP").pub(pb).sub(pb).resp(r));
    }


    private Connection nc;


    public AuthCalloutHandler(Connection nc) {
      this.nc = nc;
    }


    @Override
    public void onMessage(ServiceMessage smsg) {
      System.out.println("[HANDLER] Received Message");
      System.out.println("[HANDLER] Subject       : " + smsg.getSubject());
      System.out.println("[HANDLER] Headers       : " + headersToString(smsg.getHeaders()));


      try {

Convert the message data into a Claim

        Claim claim = new Claim(getClaimBody(smsg.getData()));
        System.out.println("[HANDLER] Claim-Request : " + claim.toJson());

The Claim should contain an Authorization Request

        AuthorizationRequest ar = claim.authorizationRequest;
        if (ar == null) {
          System.err.println("Invalid Authorization Request Claim");
          return;
        }
        printJson("[HANDLER] Auth Request  : ", ar.toJson(), "server_id", "user_nkey", "client_info", "connect_opts", "client_tls", "request_nonce");

Check if the user exists. In the example we are just checking the NATS_USERS map but in the real world, this will be backed by something specific to your organization.

        AuthCalloutUser acUser = NATS_USERS.get(ar.connectOpts.user);
        if (acUser == null) {

The user was not found, respond with an error * smsg is the original message, it contains a reply-to * ar is the AuthorizationRequest * there is no userJwt in this response * make some text string and the error will be logged

          respond(smsg, ar, null, "User Not Found: " + ar.connectOpts.user);
          return;
        }
        if (!acUser.pass.equals(ar.connectOpts.pass)) {

The user password didn’t match, respond with an error * smsg is the original message, it contains a reply-to * ar is the AuthorizationRequest * there is no userJwt in this response * make some text string and the error will be logged

          respond(smsg, ar, null, "Password does not match: " + acUser.pass + " != " + ar.connectOpts.pass);
          return;
        }

The user is found, start a UserClaim to give back to the server copy the publish and subscribe rights and response permissions

        UserClaim uc = new UserClaim()
            .pub(acUser.pub)
            .sub(acUser.sub)
            .resp(acUser.resp);

After creating the UserClaim, we must issue a JWT based on it.

        String userJwt = new ClaimIssuer()
            .aud(acUser.account)
            .name(ar.connectOpts.user)
            .iss(USER_SIGNING_PUBLIC_KEY)
            .sub(ar.userNkey)
            .nats(uc)
            .issueJwt(USER_SIGNING_NKEY);

The auth was successful Call the method to respond to the server * smsg is the original message, it contains a reply-to * ar is the AuthorizationRequest * userJwt is the JWT string * there is no error in this case

        respond(smsg, ar, userJwt, null);
      }
      catch (Exception e) {

noinspection CallToPrintStackTrace

        e.printStackTrace();
      }
    }

Helper function to respond to the request, it will be for a user or it will be an error.

    private void respond(ServiceMessage smsg,
                         AuthorizationRequest ar,
                         String userJwt,
                         String error) throws GeneralSecurityException, IOException {


      AuthorizationResponse response = new AuthorizationResponse()
          .jwt(userJwt)
          .error(error);


      if (userJwt != null) {
        printJson("[HANDLER] Auth Resp JWT : ", getClaimBody(userJwt), "name", "nats");
      }
      else {
        System.out.println("[HANDLER] Auth Resp ERR : " + response.toJson());
      }

Issue the JWT based on the supplied information and keys previously set up.

      String jwt = new ClaimIssuer()
          .aud(ar.serverId.id)
          .iss(USER_SIGNING_PUBLIC_KEY)
          .sub(ar.userNkey)
          .nats(response)
          .issueJwt(USER_SIGNING_NKEY);


      System.out.println("[HANDLER] Claim-Response: " + getClaimBody(jwt));
      smsg.respond(nc, jwt);
    }
  }


The AuthCalloutUser is just a simple data structure.


  static class AuthCalloutUser {
    public String user;
    public String pass;
    public String account;
    public Permission pub;
    public Permission sub;
    public ResponsePermission resp;


    public AuthCalloutUser userPass(String userPass) {
      this.user = userPass;
      this.pass = userPass;
      return this;
    }


    public AuthCalloutUser account(String account) {
      this.account = account;
      return this;
    }


    public AuthCalloutUser pub(Permission pub) {
      this.pub = pub;
      return this;
    }


    public AuthCalloutUser sub(Permission sub) {
      this.sub = sub;
      return this;
    }


    public AuthCalloutUser resp(ResponsePermission resp) {
      this.resp = resp;
      return this;
    }
  }


Example support functions


  static final String SPACER = "                                                            ";
  static void printJson(String label, String json, String... splits) {
    if (splits != null && splits.length > 0) {
      String indent = SPACER.substring(0, label.length());
      boolean first = true;
      for (String split : splits) {
        int at = json.indexOf("\"" + split + "\"");
        if (at > 0) {
          if (first) {
            first = false;
            System.out.println(label + json.substring(0, at));
          }
          else {
            System.out.println(indent + json.substring(0, at));
          }
          json = json.substring(at);
        }
      }
      System.out.println(indent + json);
    }
    else {
      System.out.println(label + json);
    }
  }


  static String headersToString(Headers h) {
    if (h == null || h.isEmpty()) {
      return "None";
    }


    boolean notFirst = false;
    StringBuilder sb = new StringBuilder("[");
    for (String key : h.keySet()) {
      if (notFirst) {
        sb.append(',');
      }
      else {
        notFirst = true;
      }
      sb.append(key).append("=").append(h.get(key));
    }
    return sb.append(']').toString();
  }
}

Output

"Service":{"id":"ZkDcZyu04AWe5PClLAyLyG","name":"AuthCallbackService","version":"0.0.1"}


--------------------------------------------------
Test alice
[HANDLER] Received Message
[HANDLER] Subject       : $SYS.REQ.USER.AUTH
[HANDLER] Headers       : None
[HANDLER] Claim-Request : {"aud":"nats-authorization-request","jti":"5XBGVSH6YYLMPKBQD5MXP6PVRZJ77NEYO3NTIQSSGNLSYADE6HSA","iat":1717863531,"iss":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","sub":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA","exp":1717863533,"nats":{"client_info":{"kind":"Client","host":"127.0.0.1","name":"NATS CLI Version development","id":8,"type":"nats","user":"alice"},"connect_opts":{"protocol":1,"pass":"alice","name":"NATS CLI Version development","lang":"go","user":"alice","version":"1.30.0"},"server_id":{"name":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","host":"0.0.0.0","id":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","version":"2.10.4"},"type":"authorization_request","version":2,"user_nkey":"UD357KLKNGPPPJWWTKK7WP3TOWNHNWHZJMEWFC6D5DKKTZHQGZEO7E23"}}
[HANDLER] Auth Request  : {"type":"authorization_request","version":2,
                          "server_id":{"name":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","host":"0.0.0.0","id":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","version":"2.10.4"},
                          "user_nkey":"UD357KLKNGPPPJWWTKK7WP3TOWNHNWHZJMEWFC6D5DKKTZHQGZEO7E23",
                          "client_info":{"host":"127.0.0.1","id":8,"user":"alice","name":"NATS CLI Version development","kind":"Client","type":"nats"},
                          "connect_opts":{"user":"alice","pass":"alice","name":"NATS CLI Version development","lang":"go","version":"1.30.0","protocol":1}}
[HANDLER] Auth Resp JWT : {"aud":"APP","jti":"ZYJV3UUUNG22E5RNP6DEY5F6LUEQEPMP57ZX2ONRTM2ASIWNFRLQ","iat":1717863532,"iss":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA",
                          "name":"alice","sub":"UD357KLKNGPPPJWWTKK7WP3TOWNHNWHZJMEWFC6D5DKKTZHQGZEO7E23",
                          "nats":{"type":"user","version":2,"subs":-1,"data":-1,"payload":-1}}
[HANDLER] Claim-Response: {"aud":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","jti":"2N7PJITERYE4VMEBMSEEPDGQJ5VVJQ6PVTXVCZOGTNTJH42KZJKQ","iat":1717863532,"iss":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA","sub":"UD357KLKNGPPPJWWTKK7WP3TOWNHNWHZJMEWFC6D5DKKTZHQGZEO7E23","nats":{"jwt":"eyJ0eXAiOiJKV1QiLCAiYWxnIjoiZWQyNTUxOS1ua2V5In0.eyJhdWQiOiJBUFAiLCJqdGkiOiJaWUpWM1VVVU5HMjJFNVJOUDZERVk1RjZMVUVRRVBNUDU3WlgyT05SVE0yQVNJV05GUkxRIiwiaWF0IjoxNzE3ODYzNTMyLCJpc3MiOiJBQkpITE9WTVBBNENJNlI1S0xOR09CNEdTTE5JWTdJT1VQQUpDNFlGTkRMUVZJT0JZUUdVV1ZMQSIsIm5hbWUiOiJhbGljZSIsInN1YiI6IlVEMzU3S0xLTkdQUFBKV1dUS0s3V1AzVE9XTkhOV0haSk1FV0ZDNkQ1REtLVFpIUUdaRU83RTIzIiwibmF0cyI6eyJ0eXBlIjoidXNlciIsInZlcnNpb24iOjIsInN1YnMiOi0xLCJkYXRhIjotMSwicGF5bG9hZCI6LTF9fQ.DcsEW2JHrXNINHtJxUSulHm99HoP1jE0MsHU2wBRigchq7y1_g1T1kJAEUXpeD3qOYCCua8aq9CCms2YKJt8Dg","type":"authorization_response","version":2}}
16:18:52 Published 16 bytes to "test"


--------------------------------------------------
Test bob on denied subject
[HANDLER] Received Message
[HANDLER] Subject       : $SYS.REQ.USER.AUTH
[HANDLER] Headers       : None
[HANDLER] Claim-Request : {"aud":"nats-authorization-request","jti":"XV5QMQMPXKPXKUJ3JIITLLE7MM6F4AWOQ5W3KPWUTUBNKQ6MHTJQ","iat":1717863532,"iss":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","sub":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA","exp":1717863534,"nats":{"client_info":{"kind":"Client","host":"127.0.0.1","name":"NATS CLI Version development","id":9,"type":"nats","user":"bob"},"connect_opts":{"protocol":1,"pass":"bob","name":"NATS CLI Version development","lang":"go","user":"bob","version":"1.30.0"},"server_id":{"name":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","host":"0.0.0.0","id":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","version":"2.10.4"},"type":"authorization_request","version":2,"user_nkey":"UC3KJ6GV2JLG2UQCWEVRXRM5WSZSWO6NOXPP7U3PR5JWQLHL4X6G4FNL"}}
[HANDLER] Auth Request  : {"type":"authorization_request","version":2,
                          "server_id":{"name":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","host":"0.0.0.0","id":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","version":"2.10.4"},
                          "user_nkey":"UC3KJ6GV2JLG2UQCWEVRXRM5WSZSWO6NOXPP7U3PR5JWQLHL4X6G4FNL",
                          "client_info":{"host":"127.0.0.1","id":9,"user":"bob","name":"NATS CLI Version development","kind":"Client","type":"nats"},
                          "connect_opts":{"user":"bob","pass":"bob","name":"NATS CLI Version development","lang":"go","version":"1.30.0","protocol":1}}
[HANDLER] Auth Resp JWT : {"aud":"APP","jti":"DDMPOZWJ4EKMY2ATU6RRUDX2HFCZPHB5ZV4OJVE7ZLTUPHSSILTQ","iat":1717863532,"iss":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA",
                          "name":"bob","sub":"UC3KJ6GV2JLG2UQCWEVRXRM5WSZSWO6NOXPP7U3PR5JWQLHL4X6G4FNL",
                          "nats":{"type":"user","version":2,"pub":{"allow":["bob.>"]},"sub":{"allow":["bob.>"]},"resp":{"max":1},"subs":-1,"data":-1,"payload":-1}}
[HANDLER] Claim-Response: {"aud":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","jti":"O6FZNQ4C4UZQYU6GLBDLCPN22GR47D63HMEBBHAC7B5CKDVE5ERQ","iat":1717863532,"iss":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA","sub":"UC3KJ6GV2JLG2UQCWEVRXRM5WSZSWO6NOXPP7U3PR5JWQLHL4X6G4FNL","nats":{"jwt":"eyJ0eXAiOiJKV1QiLCAiYWxnIjoiZWQyNTUxOS1ua2V5In0.eyJhdWQiOiJBUFAiLCJqdGkiOiJERE1QT1pXSjRFS01ZMkFUVTZSUlVEWDJIRkNaUEhCNVpWNE9KVkU3WkxUVVBIU1NJTFRRIiwiaWF0IjoxNzE3ODYzNTMyLCJpc3MiOiJBQkpITE9WTVBBNENJNlI1S0xOR09CNEdTTE5JWTdJT1VQQUpDNFlGTkRMUVZJT0JZUUdVV1ZMQSIsIm5hbWUiOiJib2IiLCJzdWIiOiJVQzNLSjZHVjJKTEcyVVFDV0VWUlhSTTVXU1pTV082Tk9YUFA3VTNQUjVKV1FMSEw0WDZHNEZOTCIsIm5hdHMiOnsidHlwZSI6InVzZXIiLCJ2ZXJzaW9uIjoyLCJwdWIiOnsiYWxsb3ciOlsiYm9iLj4iXX0sInN1YiI6eyJhbGxvdyI6WyJib2IuPiJdfSwicmVzcCI6eyJtYXgiOjF9LCJzdWJzIjotMSwiZGF0YSI6LTEsInBheWxvYWQiOi0xfX0.MPa6XORb8XAAG4bitC7Y0ThMV5JfxBfH3n6SGSQD5ELX-FEO7cZO1wdqvZRO5M3TJ0TKV_IgBj-EuIDg0cSaAg","type":"authorization_response","version":2}}
16:18:52 Unexpected NATS error: nats: Permissions Violation for Publish to "test"
nats: error: nats: Permissions Violation for Publish to "test"


--------------------------------------------------
Test bob on allowed subject
[HANDLER] Received Message
[HANDLER] Subject       : $SYS.REQ.USER.AUTH
[HANDLER] Headers       : None
[HANDLER] Claim-Request : {"aud":"nats-authorization-request","jti":"XV5QMQMPXKPXKUJ3JIITLLE7MM6F4AWOQ5W3KPWUTUBNKQ6MHTJQ","iat":1717863532,"iss":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","sub":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA","exp":1717863534,"nats":{"client_info":{"kind":"Client","host":"127.0.0.1","name":"NATS CLI Version development","id":10,"type":"nats","user":"bob"},"connect_opts":{"protocol":1,"pass":"bob","name":"NATS CLI Version development","lang":"go","user":"bob","version":"1.30.0"},"server_id":{"name":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","host":"0.0.0.0","id":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","version":"2.10.4"},"type":"authorization_request","version":2,"user_nkey":"UAWXFWQIM6SNBZQUNMPQKWWSPRQZM2BBNPQ45WACHIA2ATU7B65YNHHO"}}
[HANDLER] Auth Request  : {"type":"authorization_request","version":2,
                          "server_id":{"name":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","host":"0.0.0.0","id":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","version":"2.10.4"},
                          "user_nkey":"UAWXFWQIM6SNBZQUNMPQKWWSPRQZM2BBNPQ45WACHIA2ATU7B65YNHHO",
                          "client_info":{"host":"127.0.0.1","id":10,"user":"bob","name":"NATS CLI Version development","kind":"Client","type":"nats"},
                          "connect_opts":{"user":"bob","pass":"bob","name":"NATS CLI Version development","lang":"go","version":"1.30.0","protocol":1}}
[HANDLER] Auth Resp JWT : {"aud":"APP","jti":"LV2X6BNLJGMLVEKZN45X625GW5LHT7TLZEEFM5K6K57J5BSRV4BQ","iat":1717863532,"iss":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA",
                          "name":"bob","sub":"UAWXFWQIM6SNBZQUNMPQKWWSPRQZM2BBNPQ45WACHIA2ATU7B65YNHHO",
                          "nats":{"type":"user","version":2,"pub":{"allow":["bob.>"]},"sub":{"allow":["bob.>"]},"resp":{"max":1},"subs":-1,"data":-1,"payload":-1}}
[HANDLER] Claim-Response: {"aud":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","jti":"D33BUMFANF55DTDDUQGZXQTOB4YEI7MCQGQWM2P4CXOQ6FGE4W7Q","iat":1717863532,"iss":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA","sub":"UAWXFWQIM6SNBZQUNMPQKWWSPRQZM2BBNPQ45WACHIA2ATU7B65YNHHO","nats":{"jwt":"eyJ0eXAiOiJKV1QiLCAiYWxnIjoiZWQyNTUxOS1ua2V5In0.eyJhdWQiOiJBUFAiLCJqdGkiOiJMVjJYNkJOTEpHTUxWRUtaTjQ1WDYyNUdXNUxIVDdUTFpFRUZNNUs2SzU3SjVCU1JWNEJRIiwiaWF0IjoxNzE3ODYzNTMyLCJpc3MiOiJBQkpITE9WTVBBNENJNlI1S0xOR09CNEdTTE5JWTdJT1VQQUpDNFlGTkRMUVZJT0JZUUdVV1ZMQSIsIm5hbWUiOiJib2IiLCJzdWIiOiJVQVdYRldRSU02U05CWlFVTk1QUUtXV1NQUlFaTTJCQk5QUTQ1V0FDSElBMkFUVTdCNjVZTkhITyIsIm5hdHMiOnsidHlwZSI6InVzZXIiLCJ2ZXJzaW9uIjoyLCJwdWIiOnsiYWxsb3ciOlsiYm9iLj4iXX0sInN1YiI6eyJhbGxvdyI6WyJib2IuPiJdfSwicmVzcCI6eyJtYXgiOjF9LCJzdWJzIjotMSwiZGF0YSI6LTEsInBheWxvYWQiOi0xfX0.0zYVScn1IGqs_v6RG2bJ1zVPeDEWii_H1kSXFjgrHSnmthrJb9IAsMC7kQsJCCBPy-C_vEordlC5X7DSxJ-pBA","type":"authorization_response","version":2}}
16:18:52 Published 14 bytes to "bob.test"


--------------------------------------------------
Test unknown user
[HANDLER] Received Message
[HANDLER] Subject       : $SYS.REQ.USER.AUTH
[HANDLER] Headers       : None
[HANDLER] Claim-Request : {"aud":"nats-authorization-request","jti":"XV5QMQMPXKPXKUJ3JIITLLE7MM6F4AWOQ5W3KPWUTUBNKQ6MHTJQ","iat":1717863532,"iss":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","sub":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA","exp":1717863534,"nats":{"client_info":{"kind":"Client","host":"127.0.0.1","name":"NATS CLI Version development","id":11,"type":"nats","user":"pam"},"connect_opts":{"protocol":1,"pass":"pam","name":"NATS CLI Version development","lang":"go","user":"pam","version":"1.30.0"},"server_id":{"name":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","host":"0.0.0.0","id":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","version":"2.10.4"},"type":"authorization_request","version":2,"user_nkey":"UBF7D35LTBKHP5UVNJRNG6AEXDGZW7DF2RZ6I4H5OUIJNFKCEICGMGLS"}}
[HANDLER] Auth Request  : {"type":"authorization_request","version":2,
                          "server_id":{"name":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","host":"0.0.0.0","id":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","version":"2.10.4"},
                          "user_nkey":"UBF7D35LTBKHP5UVNJRNG6AEXDGZW7DF2RZ6I4H5OUIJNFKCEICGMGLS",
                          "client_info":{"host":"127.0.0.1","id":11,"user":"pam","name":"NATS CLI Version development","kind":"Client","type":"nats"},
                          "connect_opts":{"user":"pam","pass":"pam","name":"NATS CLI Version development","lang":"go","version":"1.30.0","protocol":1}}
[HANDLER] Auth Resp ERR : {"error":"User Not Found: pam","type":"authorization_response","version":2}
[HANDLER] Claim-Response: {"aud":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","jti":"CWTWF4J3ITDP2CREWKCP2NQAPC7C365RXGGE4SOWBZTTJVUJJMYA","iat":1717863532,"iss":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA","sub":"UBF7D35LTBKHP5UVNJRNG6AEXDGZW7DF2RZ6I4H5OUIJNFKCEICGMGLS","nats":{"error":"User Not Found: pam","type":"authorization_response","version":2}}
nats: error: nats: Authorization Violation


--------------------------------------------------
Test system account user
[HANDLER] Received Message
[HANDLER] Subject       : $SYS.REQ.USER.AUTH
[HANDLER] Headers       : None
[HANDLER] Claim-Request : {"aud":"nats-authorization-request","jti":"XV5QMQMPXKPXKUJ3JIITLLE7MM6F4AWOQ5W3KPWUTUBNKQ6MHTJQ","iat":1717863532,"iss":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","sub":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA","exp":1717863534,"nats":{"client_info":{"kind":"Client","host":"127.0.0.1","name":"NATS CLI Version development","id":12,"type":"nats","user":"sys"},"connect_opts":{"protocol":1,"pass":"sys","name":"NATS CLI Version development","lang":"go","user":"sys","version":"1.30.0"},"server_id":{"name":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","host":"0.0.0.0","id":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","version":"2.10.4"},"type":"authorization_request","version":2,"user_nkey":"UD5VWPKT56HEX5D2WHIMYVD6HZQWHTDPXMFKA4QJJ7LZG7HBYQFLH4LH"}}
[HANDLER] Auth Request  : {"type":"authorization_request","version":2,
                          "server_id":{"name":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","host":"0.0.0.0","id":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","version":"2.10.4"},
                          "user_nkey":"UD5VWPKT56HEX5D2WHIMYVD6HZQWHTDPXMFKA4QJJ7LZG7HBYQFLH4LH",
                          "client_info":{"host":"127.0.0.1","id":12,"user":"sys","name":"NATS CLI Version development","kind":"Client","type":"nats"},
                          "connect_opts":{"user":"sys","pass":"sys","name":"NATS CLI Version development","lang":"go","version":"1.30.0","protocol":1}}
[HANDLER] Auth Resp JWT : {"aud":"SYS","jti":"37T6NY5W5S2VE7VCGS7WZL6PVTU33PTPXF5DKOO2ZPEBXNBZXIUQ","iat":1717863532,"iss":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA",
                          "name":"sys","sub":"UD5VWPKT56HEX5D2WHIMYVD6HZQWHTDPXMFKA4QJJ7LZG7HBYQFLH4LH",
                          "nats":{"type":"user","version":2,"subs":-1,"data":-1,"payload":-1}}
[HANDLER] Claim-Response: {"aud":"NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB","jti":"6H5F57LAVQJTAEQUIPH6XW7RTFNV3EBFIZNEVTT6WJGQ47CDKC2A","iat":1717863532,"iss":"ABJHLOVMPA4CI6R5KLNGOB4GSLNIY7IOUPAJC4YFNDLQVIOBYQGUWVLA","sub":"UD5VWPKT56HEX5D2WHIMYVD6HZQWHTDPXMFKA4QJJ7LZG7HBYQFLH4LH","nats":{"jwt":"eyJ0eXAiOiJKV1QiLCAiYWxnIjoiZWQyNTUxOS1ua2V5In0.eyJhdWQiOiJTWVMiLCJqdGkiOiIzN1Q2Tlk1VzVTMlZFN1ZDR1M3V1pMNlBWVFUzM1BUUFhGNURLT08yWlBFQlhOQlpYSVVRIiwiaWF0IjoxNzE3ODYzNTMyLCJpc3MiOiJBQkpITE9WTVBBNENJNlI1S0xOR09CNEdTTE5JWTdJT1VQQUpDNFlGTkRMUVZJT0JZUUdVV1ZMQSIsIm5hbWUiOiJzeXMiLCJzdWIiOiJVRDVWV1BLVDU2SEVYNUQyV0hJTVlWRDZIWlFXSFREUFhNRktBNFFKSjdMWkc3SEJZUUZMSDRMSCIsIm5hdHMiOnsidHlwZSI6InVzZXIiLCJ2ZXJzaW9uIjoyLCJzdWJzIjotMSwiZGF0YSI6LTEsInBheWxvYWQiOi0xfX0.MxWZyRrdqbH0zYqoN1pQpkc2P6vGT9ISIk9XnouPSAClHwauYyVXOP21Tx7s34ezg0P1HBzbvdcApEuGLLTiCg","type":"authorization_response","version":2}}
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│                                                                            Server Overview                                                                            │
├──────────────────────────────────────────────────────────┬─────────┬──────┬─────────┬────┬───────┬──────┬────────┬─────┬────────┬───────┬───────┬──────┬────────┬─────┤
│ Name                                                     │ Cluster │ Host │ Version │ JS │ Conns │ Subs │ Routes │ GWs │ Mem    │ CPU % │ Cores │ Slow │ Uptime │ RTT │
├──────────────────────────────────────────────────────────┼─────────┼──────┼─────────┼────┼───────┼──────┼────────┼─────┼────────┼───────┼───────┼──────┼────────┼─────┤
│ NAMSQRSU2DRUQEL2SXVCD4QOUUSYPLN4HOGNVBKYHZUIZT25MGX463GB │         │ 0    │ 2.10.4  │ no │ 2     │ 73   │      0 │   0 │ 14 MiB │ 0     │     4 │    0 │ 3.11s  │ 1ms │
├──────────────────────────────────────────────────────────┼─────────┼──────┼─────────┼────┼───────┼──────┼────────┼─────┼────────┼───────┼───────┼──────┼────────┼─────┤
│                                                          │ 0       │ 1    │         │ 0  │ 2     │ 73   │        │     │ 14 MIB │       │       │    0 │        │     │
╰──────────────────────────────────────────────────────────┴─────────┴──────┴─────────┴────┴───────┴──────┴────────┴─────┴────────┴───────┴───────┴──────┴────────┴─────╯

Recording

Note, playback is half speed to make it a bit easier to follow.