Introduction

As I mentioned in my earlier article about NATS Jetstream Implementation, in this article, we are going to deep-dive into the Authentication and Authorisation module to connect NATS. Since we are moving toward digitalization, we need to make sure about access grants and resource usage. We shouldn't allow unauthenticated users or systems to access our system or message queue, and this applies to internal and external networks.

I mean internal network, as you create a product on your premises, which acts as a global product. You have to make sure in your line of business who should access this product. In this case, we should be aware of how many requests we are getting from each system so that we can caveat the product scalability, performance upgrade, etc.,

In this digital world and distributed system, secure communication is non-negotiable. This article dives into NATS authentication, ensuring secure messaging and reliable system integrity.

NATS has a built-in authentication mechanism; it supports multiple types of authentication and can be extended with custom solutions.

Types of Authentication

Hierarchy Flow of NATS Authentication

In NATS, these are the three account hierarchies, and the benefits of this model are multi-tenancy, resource isolation, and fine-grain access control.

Environment Setup HLD

In this proof of concept, we use the three free-tier AWS ec2 instances to make a cluster NATS environment.

NATS Environment Setup HLD

Authentication Sequence Diagram

Authentication Sequence Diagram

Authentication Keys Generation

In NATS, we can configure or customize configuration using.conf file and this needs to be a parameter while starting the NATS server. For example, we need to use this command to start "nats-server -c nats-server.conf"

Username and Password Authentication

Basic Authentication

# NATS Server Configuration
authorization {
    users = [
        { user: "client1", password: "client1" }
        { user: "client2", password: "client2", permissions: {
            publish = ["orders.>"] 
            subscribe = ["payments.>"]
        }}
    ]
}

Client Side Code

var opts = ConnectionFactory.GetDefaultOptions();

// Specify the server URL
opts.Url = "nats://localhost:4222";
// Set the username and password
opts.User = "client1";
opts.Password = "client1";

TokenBased Authentication

It's easy to configure and ready to use; there is no need to manage individual user credentials. This JWT needs to be shared with the client side.

# NATS Server Configuration
authorization {
    token: "jwt_secure_token"
}

Client-Side Code

var opts = ConnectionFactory.GetDefaultOptions();
// Specify the server URL
opts.Url = "nats://localhost:4222";
// Set the token
opts.Token = "my_secure_token";

NKEY Authentication

Prerequisites

Please note. Always use a JWT in the operator configuration. Accounts and users could use either their NKEYs directly or their JWTs for a more flexible setup.

No additional NKEY generation is required after adding the user. The user’s NKEY is automatically generated and can be viewed using the nsc describe command.

# Define operator
operator: "/path/to/operator.nk"

# Define resolver for accounts and users
resolver: {
    type: "MEMORY" # This is in-memory, we also have another option called as File
    accounts: {
        "MyAccount": {
            nkey: "ADAAAAA...AAAA", # Account NKEY / Public Key
            users: [
                {
                    nkey: "UDAAAAA...AAAA", # User NKEY
                    permissions: {
                        publish: ["orders.>"],      # User can publish to "orders.>"
                        subscribe: ["orders.booking"]  # User can subscribe to "orders.booking"
                    }
                },
                {
                    nkey: "UDBBBBB...BBBB", # Another User NKEY
                    permissions: {
                        publish: ["orders.*"],
                        subscribe: ["orders.*"]
                    }
                }
            ]
        }
    }
}

Client-Side Code

 var options = ConnectionFactory.GetDefaultOptions();
 options.Url = "nats://localhost:4222"; // NATS server URL
 // Set up NKEY authentication
 options.NKey = nkeyPublic;
 options.NKeySeed = nkeySeed;

JWT Authentication

Prerequisite

Please note. As per the above HLD, we need to store this Account.jwt and Operator.jwt in S3, and Account.jwt should be key like <NKEY>.jwt

Please find the sample user.jwt format

-----BEGIN NATS USER JWT-----
eyJ0eXAiOiJKV1QiLCJhbGciOiAiUlMyNTYiL...<JWT>
-----END NATS USER JWT-----
-----BEGIN NATS USER SIGNATURE-----
i0Wx7NRo1K...g... <NKEY>
-----END NATS USER SIGNATURE-----
# Path to the operator JWT file
operator: "/path/to/operator.jwt" //s3 path

# Use File Resolver
resolver: {
    type: "FILE",  # Specify the resolver type as FILE
    config: "/path/to/accounts"  # S3 Directory containing account files
}

Client-side Code

// Path to the credentials file
string credFilePath = "path/to/user.cred";

// Load the credentials
var userCreds = LoadUserCreds(credFilePath);

// Create a NATS connection options with the credentials
var options = ConnectionFactory.GetDefaultOptions();
options.Url = "nats://localhost:4222"; // Change to your NATS server URL
options.UserCredentials = userCreds;

Custom Authentication

We could integrate with Identity Server; let us dive deep into this specific topic in another article.

Best Practices

Summary

Thank you for reading this article. I hope it provides valuable insights for your time. Whether you are building new applications or enhancing existing ones, the information shared here will help you make informed decisions regarding authentication strategies in NATS.

In this article, we explored various types of authentication methods available in NATS. It’s essential to choose the approach that aligns with your business needs, application security requirements, and overall architecture. By carefully considering these factors, you can select the most appropriate solution to ensure a secure and efficient messaging system.

Reference