nats-io / nats.net

Full Async C# / .NET client for NATS
https://nats-io.github.io/nats.net
Apache License 2.0
264 stars 54 forks source link

Bring back support for JwtUtils.IssueUserJWT #309

Open aachinfiev opened 10 months ago

aachinfiev commented 10 months ago

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 10 months ago

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

caleblloyd commented 10 months ago

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 10 months ago

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

scottf commented 10 months ago

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.

Jarema commented 10 months ago

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 10 months ago

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

NATS.Client.Core    NATS.Jwt
           \         /
            \       /
             v     v
            NATS.Nkeys
Jarema commented 10 months ago

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

mtmk commented 10 months ago

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

mtmk commented 9 months ago

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.

niklasfp commented 9 months ago

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
oising commented 9 months ago

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

image

oising commented 9 months ago

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

mtmk commented 9 months ago

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?

adamreed90 commented 8 months ago

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 8 months ago

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