nats-io/nats.net.v2

Bring back support for JwtUtils.IssueUserJWT

Opened this issue ยท 15 comments

Proposed change

In v1 client there was a JwtUtility which had IssueUserJWT function that can be handy when this things have to be generated on the fly. v2 client doesn't have this feature any more.

After discussing with @mtmk he mentioned that there is a plan to build a separate package to support such functionality in the future. Creating this ticket to keep track of this.

Original code:
https://github.com/nats-io/nats.net/blob/a85c85692021c72ffcfbb0d93a799366afa35669/src/NATS.Client/Internals/JwtUtils.cs#L25

Use case

A system can have its own user set for which there are no pre-generated credentials. There is a need to generate user JWT for NATS authentication.

Contribution

No response

mtmk commented

I'm guessing implementing the functionality in the class mentioned above is pretty much the only thing we're after.

Basically this method:

public static string IssueUserJWT(
    NkeyPair signingKey,
    string publicUserKey,
    string name,
    Duration expiration,
    long issuedAt,
    string audience,
    UserClaim nats)

We can create a new Nuget package NATS.Jwt and implement the functionality there.

cc @scottf @caleblloyd @Jarema

The de-facto JWT Library is the Go library - https://github.com/nats-io/jwt

I think the JS version also has fairly broad support - https://github.com/nats-io/jwt.js

Would need to decide what functionality NATS.Jwt would implement. Just minting User JWTs seems like a good place to start. Would want to make sure that any primitives created could eventually apply to all of the JWT types though.

mtmk commented

@scottf @Jarema do / should we have a spec for this?

do / should we have a spec for this?

As you already noted, implement the IssueUserJWT. There is a version in .net. I'm currently in the process of expanding the java to be able to support the auth callout authorization request and response so those will need to be supported as well.

It would be nice to have a spec for this, but that woud need to cover a lot more than just this, and rather the whole NATS auth suite with NKEYS, etc.

mtmk commented

We also need to move NkeyPair implementation into a new NATS.Nkeys package

NATS.Client.Core    NATS.Jwt
           \         /
            \       /
             v     v
            NATS.Nkeys

I suggest following the the pattern of other languages, where there is a repo nkeys.language.

mtmk commented

repos: nats-io/jwt.net, nats-oi/nkeys.net
target: netstandard-2.0

mtmk commented

note we want to move/copy nkeys implementation out so we can implement JWT utilities as a separate package. we need to check if #390 is netstandard compliant.

note we want to move/copy nkeys implementation out so we can implement JWT utilities as a separate package. we need to check if #390 is netstandard compliant.

It's Is (ish) the static HashData method used, is not. It was introduced the .net 5 timeframe to add optimisations, but it should be easy to do an ifdef for netstandard that just creates, hashes, finalizes and returns the result .

https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.sha512?view=netstandard-2.0

This should do the trick and keep using the goodies from the newer frameworks:

    public static byte[] Hash(byte[] data, int index, int length)
    {
        if (data == null)
        {
            throw new ArgumentNullException(nameof(data));
        }
        
        return HashImpl(data, index, length);
    }

#if NETSTANDARD2_0
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static byte[] HashImpl(byte[] data, int index, int length)
    {
        using var sha512 = SHA512.Create();
        sha512.TransformBlock(data, index, length, null, 0);
        _ = sha512.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
        return sha512.Hash!;
    }
#else
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static byte[] HashImpl(byte[] data, int index, int length)
    {
        return SHA512.HashData(data.AsSpan(index, length));
    }
#endif

Perhaps dotnet user-jwts could be leveraged here instead of rolling something new?

image

Oh, wait... this is not dev/test usage, right? Or is it?

mtmk commented

It's for prod usage as well. But using dotnet tooling at least for dev/test is a great idea regardless. Not sure if the algorithms NATS uses would be available in dotnet user-jwts?

I believe there is great value in wrapping the entire nats-resolver JWT functionality as well with a Context, for simple management JWTs via NATS via the .NET SDK. Which would increase it's viability as a replacement for SignalR ๐Ÿ’ช

mtmk commented

fyi initial import for nkeys and jwt repos are now public. It's an import mostly from the v1 client and we would want to make API changes going forward. Feel free to start contributing or create issues.

https://github.com/nats-io/nkeys.net

https://github.com/nats-io/jwt.net