uruk-project / jwt Goto Github PK
View Code? Open in Web Editor NEWJSON Web Token implementation for .Net & .Net Core
License: MIT License
JSON Web Token implementation for .Net & .Net Core
License: MIT License
I'm having a hard time figuring out the "defaultAlgorithm" here:
TokenValidationPolicyBuilder RequireSignature(string issuer, Jwks keys, SignatureAlgorithm defaultAlgorithm)
Because the "alg" header parameter (https://tools.ietf.org/html/rfc7515#section-4.1.1):This Header Parameter MUST be present and MUST be understood and processed by implementations.
... default here seems rather strange to me. Is it a way to REQUIRE a given algorithm? In this case, how does this fit with the multiple keys that could be associated to the "kid"?
(This defaultAlgorithm is not optional... just like the "alg" parameter.)
Sorry to ask but I'll appreciate some explanations about this... Thanks in advance.
Originally posted by @olivier-spinelli in #545 (comment)
This API is new in v2.0, still in beta, so there may be some remaining polish on the public API.
The excepted usage is:
var policy = new TokenValidationPolicyBuilder()
.RequireSignature("https://idp.example.com/", key, SignatureAlgorithm.HS256)
.RequireAudience("636C69656E745F6964")
.Build();
By doing so, there is a link between the issuer, the key, and the signature algorithm. The issuer is used as a lookup for finding the required validation. This is the common usage of a JWS: You have an issuer, a key, and an associated signature algorithm.
So this is not a default algorithm.
It is also possible to do so (legacy usage):
var policy = new TokenValidationPolicyBuilder()
.DefaultIssuer("https://idp.example.com/")
.RequireSignatureByDefault(key, SignatureAlgorithm.HS256)
.Build();
The default issuer is not used as lookup, but only as the last chance to validate an issuer, as-well as the signature key. This allow some use cases where there is no issuer.
Your question convinces me on 2 points:
defaultAlgorithm
is a perfect example of name without clear intention.Require***ByDefault
, RequireAlgorithm
. Marking this methods as obsolete might be an option.Hello.
How can I use your library to create an JWT token with Elliptic Curve JSON Web Key?
private const string privkey =
"{"kty":"EC","alg":"ES256","crv":"P-256"," + ""x":"lmzBhWRXSZTx5q6b80PK_GL7b94YYXI1hNB3YdJ6bzQ","y":"sXwmQX8sAm5yoybyq0RvMMhQp7Ox4lvhdpy_xPjfs58"," +""d":"P_MepPu0IPJEIQ_WUL5p12qb-phfMBdiDC8vHgdwvJ4"}";
None of the methods does not work:
1.
var privJwk = new ECJwk(256, "P_MepPu0IPJEIQ_WUL5p12qb-phfMBdiDC8vHgdwvJ4","lmzBhWRXSZTx5q6b80PK_GL7b94YYXI1hNB3YdJ6bzQ","sXwmQX8sAm5yoybyq0RvMMhQp7Ox4lvhdpy_xPjfs58");
2.
var privJwk = ECJwk.FromJson(privkey);
[EditorBrowsable(EditorBrowsableState.Never)]
Found by @olivier-spinelli.
When the JsonObject contains only 1 member, adding the same member name cause a duplicate.
Example:
{
"item1": "value1"
}
Adding "item1": "value2"
produces:
{
"item1": "value1",
"item1": "value2"
}
The expected result would be:
{
"item1": "value2"
}
Shouldn't the boolean parameter for ExportParameters be true if withPrivateKey is true? I was trying a JWE validation with asymmetric key and it kept on failing with "Encryption Key Not found" error -which I think maybe due to this. In file src\JsonWebToken\Jwk.cs
if (withPrivateKey)
{
using var rsa = certificate.GetRSAPrivateKey();
if (!(rsa is null))
{
var rsaParameters = rsa.ExportParameters(false);
key = new RsaJwk(rsaParameters);
}
}
Also, it might be better to add a assert in test\JsonWebToken.Tests\JsonWebKeyTests.cs
to check for jwk.HasPrivateKey property if the input certificate has a private key.
[Theory]
[MemberData(nameof(GetCertificates))]
public void CreateFromCertificate(X509Certificate2 certificate, bool hasPrivateKey, int keySize)
{
var jwk = Jwk.FromX509Certificate(certificate, hasPrivateKey);
Assert.Equal(keySize, jwk.KeySizeInBits);
if(hasPrivateKey)
{
Assert.True(jwk.HasPrivateKey);
}
}
Issue opened at https://github.com/dotnet/core/issues/5787
We're currently wrapping JwksHttpKeyProvider in a custom key provider factory in order to support the following requirement (supports automatic key rotation for our services):
When a key is missing from the JWKS, the validation service shall attempt to reload the JWKS from the
/.well-known/jwks.json
endpoint. This shall be attempted at a rate of no more than once per minute.
I propose adding the following to JwksHttpKeyProvider.cs:
namespace JsonWebToken
{
/// <summary>Represents a <see cref="IKeyProvider"/> that retrieve the key set from an HTTP resource as JWKS.</summary>
public sealed class JwksHttpKeyProvider : IKeyProvider, IDisposable
{
...
// Possible implementation
public void Refresh()
{
_refreshLock.Wait();
try
{
_syncAfter = EpochTime.UtcNow + RefreshInterval;
}
finally
{
_refreshLock.Release();
}
}
}
}
This would allow us to setup refreshing the JWKS if a key is missing.
Since this is a 2.0 release, perhaps this could be rolled up to IKeyProvider
?
Hi
I am working on .Net and interested to use JsonWebToken for Signing Purpose. i want to implement Using OAuth 2.0 for Server to Server Applications in google. The only signing algorithm supported by the Google OAuth 2.0 Authorization Server is RSA using SHA-256 hashing algorithm. I have the private key in my hand.i need to initialize AsymmetricJwk, i think. But the example only showing SymmetricJwk.Can you Please help me, how can i initialize AsymmetricJwk using Private Key.
Thanks
i couldn't find a way to add custom claims to JwsDescriptor which accepts claim value as object, where it get automatically json serialized when jwt is written.
e.g.
descriptor.AddClaim("https://hasura.io/jwt/claims", new HasuraClaim
{
UserId = user.Id,
DefaultRole = "user",
Roles = new[] { "user" }
});
I am attempting to validate an access token coming from Azure AD B2C, and the validation is failing even though jwt.io successfully validates the signature. This is version 1.9.0.
In SignatureValidationPolicy.cs
, line 101, Jwk.CanUseForSignature()
is called with "RS256" as signatureAlgorithm
, and it returns true, because the SignatureAlgorithm
property returns null. Then, on line 103, alg
ends up null because both algorithm
and key.SignatureAlgorithm
are null. That means that the code never tries to validate the signature, and InvalidSignature
is the result.
static async Task Main(string[] args)
{
var http = new HttpClient();
var str = await http.GetStringAsync("https://login.eveonline.com/oauth/jwks");
var jwks = Jwks.FromJson(str);
}
throwing exception The JWKS is malformed.
Hello,
I'm trying to create a SymmetricJwk with user password - so only him can verify the JWT again.
But I got an Exception.
System.FormatException: The input is not a valid Base-64 URL string as it contains a non-base 64 character.
at JsonWebToken.ThrowHelper.ThrowOperationNotDoneException(OperationStatus status)
at JsonWebToken.Base64Url.Decode(ReadOnlySpan`1 base64Url, Span`1 data)
at JsonWebToken.Base64Url.Decode(ReadOnlySpan`1 base64Url)
at JsonWebToken.Base64Url.Decode(String data)
at JsonWebToken.SymmetricJwk..ctor(String k)
I found this https://stackoverflow.com/questions/44866847/convert-frombase64string-does-not-work-in-code-but-works-in-online-tool/44867049 so it explained me, why I got the error exactly.
But I think it might be a bug for creating a JWK, since when I create a token here https://jwt.io/#debugger-io with that password, it is created without any problem.
So I'm not sure, why there is _k = Base64Url.Decode(k);
in the first place, could it be done by System.Text.Encoding.UTF8.GetBytes
instead? I guess it would be more user friendly.
But maybe I see it all wrong (I'm kinda new to JWT world).
This is just a tiny inconsistency I noticed in some testing. It probably wouldn't affect anyone unless they're abusing the ToString
methods of JwsDescriptor
and Jwt
.
The following code:
var descriptor = new JwsDescriptor
{
Algorithm = SignatureAlgorithm.None,
IssuedAt = DateTime.UtcNow,
ExpirationTime = DateTime.UtcNow.AddHours(1),
Issuer = "https://idp.example.com/",
Audience = "636C69656E745F6964"
};
Console.WriteLine("Descriptor ToString():");
Console.WriteLine(descriptor.ToString());
Console.WriteLine();
var tokenBytes = new JwtWriter().WriteToken(descriptor);
Console.WriteLine("Jwt ToString():");
Console.WriteLine(new JwtReader().TryReadToken(tokenBytes, TokenValidationPolicy.NoValidation).Token?.ToString());
Produces the following output:
Descriptor ToString():
{
"alg": "none"
}
.
{
"iat": 1588358068,
"exp": 1588361668,
"iss": "https://idp.example.com/",
"aud": "636C69656E745F6964"
}
Jwt ToString():
{
"alg": "none"
}.{
"aud": "636C69656E745F6964",
"iss": "https://idp.example.com/",
"exp": 1588361668,
"Iat": 1588358068
}
Note that the descriptor ToString
output correctly has the "iat" claim in lowercase but the Jwt ToString
output has it with a capital "I" ("Iat").
This line seems to be the source of the problem.
Hello, should there be a encrypted key present when using this algorithm ?
According to my readings, this algorithm uses Direct Key Agreement, and when using this it seems that the JWE Encrypted key value should have been empty ? https://datatracker.ietf.org/doc/html/rfc7516#section-5.2 (10)
It's turning out to be a invalid JWE, since in the CEK part we are getting ".AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA." instead of the empty octet ?
Example:
string json = "{\r\n \"alg\": \"ES256\",\r\n \"crv\": \"P-256\",\r\n \"kty\": \"EC\",\r\n \"use\": \"sig\",\r\n \"x\": \"OKs1T_4N9Z78RQ87olZ98PW__ROFWL5fw1671XB20zw\",\r\n \"y\": \"8y5YBG5RY4gK2bObN4Aj5eNmXBoLMrCHKEMwykPSTIg\"\r\n }";
string payload = "teste";
var descriptor = new PlaintextJweDescriptor(payload)
{
EncryptionKey = Jwk.FromJson(json),
EncryptionAlgorithm = EncryptionAlgorithm.Aes256Gcm,
Algorithm = KeyManagementAlgorithm.EcdhEs
};
var writer = new JwtWriter();
string jwe = writer.WriteTokenString(descriptor);
Console.WriteLine(jwe);
eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiRUNESC1FUyIsImVwayI6eyJjcnYiOiJQLTI1NiIsIngiOiJNQnpzbF9VM2ZFemxLMzVvbWh4TjlOd05yRm11ZXlRRGR2WUVxd2hENWtnIiwieSI6Ilc1TnJvZGxvQXdTbHptUjZ3cVAwZjVxRHBEQ2NqM0Y0YXEteUNwS1p5a3cifX0.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.2JvH70MzlecMiZtG.Fy85KTI.dHuw2Cw7_im2TAP_YpKrfQ
Thanks in advance @ycrumeyrolle
The nonce is not initialized correctly.
Currently the value is either an empty value, or a value of remaining bytes thanks to SkipLocalsInit
.
RFC 7518 JSON Web Algothrims specify that the authentication tag should be truncated to the first 16 octets.
The AES_CBC_HMAC_SHA2 parameters specific to AES_128_CBC_HMAC_SHA_256 are:
The input key K is 32 octets long.
ENC_KEY_LEN is 16 octets.
MAC_KEY_LEN is 16 octets.
The SHA-256 hash algorithm is used for the HMAC.
The HMAC-SHA-256 output is truncated to T_LEN=16 octets, by
stripping off the final 16 octets.
However when JWE tokens are generated the full 32 octets generated by HMAC SHA256 are being appended. This is generating invalid tokens.
We've begun serializing our JWKS to a well known location and found that under v1.9.1 it is broken. The fix was applied as part of a pre-release:
79695d7#diff-d3b4dcfad73c68c10c1bc32d6fe3679468c657c1f05200384b59204959c0fbea
Is there a possibility of getting a v1.9.2 with just that targeted fix for JWKS? I'm willing to put together the PR and tests if that's a barrier.
Hey,
i tried your library and the one from Microsoft (Microsoft.IdentityModel.JsonWebTokens) and the interoperability between both.
https://datatracker.ietf.org/doc/html/rfc7518#section-4.6.2 seems to state that the Hash should be calculated with SHA256
In Microsoft code it looks like this:
// JWA's spec https://datatracker.ietf.org/doc/html/rfc7518#section-4.6.2 specifies SHA256, saml might be different
byte[] derivedKey = _ecdhPrivate.DeriveKeyFromHash(_ecdhPublic.PublicKey, HashAlgorithmName.SHA256, prepend, append);
In your code the hash algorithm is defined through it's encryption algorithm:
_hashAlgorithm = GetHashAlgorithm(encryptionAlgorithm);
...
var hashAlgorithm = encryptionAlgorithm.SignatureAlgorithm.HashAlgorithm
...
exchangeHash = new ReadOnlySpan<byte>(ephemeralKey.DeriveKeyFromHash(otherPartyKey.PublicKey, _hashAlgorithm, _secretPreprend, secretAppend), 0, _keySizeInBytes);>
If you now use a combination of EcdhEsA128kw and Aes128CbcHmacSha256 it works because here SHA256 is used.
But if you use a combination of EcdhEsA256kw and Aes256CbcHmacSha512 the tokens from the MS-Lib and the tokens of your lib cannot be understood by the other party, because you would use SHA512 in that case.
Who is right - who is wrong? I dont have clue but hope you have an answer to this because i would like to connect two applications using different frameworks ;-)
Hello,
in EC JWK the size is calculated by:
protected internal override int GetCanonicalizeSize()
{
return 35 + Base64Url.GetArraySizeRequiredToEncode(Crv.Name.EncodedUtf8Bytes.Length) + Base64Url.GetArraySizeRequiredToEncode(X.Length) + Base64Url.GetArraySizeRequiredToEncode(Y.Length);
}
but it should be
protected internal override int GetCanonicalizeSize()
{
return 35 + Crv.Name.EncodedUtf8Bytes.Length + Base64Url.GetArraySizeRequiredToEncode(X.Length) + Base64Url.GetArraySizeRequiredToEncode(Y.Length);
}
because Crv.Name.EncodedUtf8Bytes is copied in Canonicalize:
protected internal override void Canonicalize(Span<byte> buffer)
{
int length = StartCanonicalizeValue.Length;
StartCanonicalizeValue.CopyTo(buffer);
EllipticalCurve ellipticalCurve = Crv;
ellipticalCurve.Name.EncodedUtf8Bytes.CopyTo(buffer.Slice(length));
int num = length;
ellipticalCurve = Crv;
length = num + ellipticalCurve.Name.EncodedUtf8Bytes.Length;
Middle1CanonicalizeValue.CopyTo(buffer.Slice(length));
length += Middle1CanonicalizeValue.Length;
length += Base64Url.Encode(X, buffer.Slice(length));
Middle2CanonicalizeValue.CopyTo(buffer.Slice(length));
length += Middle2CanonicalizeValue.Length;
length += Base64Url.Encode(Y, buffer.Slice(length));
EndCanonicalizeValue.CopyTo(buffer.Slice(length));
}
This is a problem because ComputeThumbprint uses this Value for the call to ComputeHash:
Sha256.Shared.ComputeHash(buffer.Slice(0, canonicalizeSize), span);
The buffer is either from ArrayPool or from Stackalloc - ArrayPool does not garantuee to null the array. So you get random Kids when the last bytes differ because they dont get initialized.
I think the Renting is also wrong - you use Stackalloc if canonicalSize is bigger than 256 - i think the condition should be reversed - using stack alloc for smaller arrays right?
I didnt check for RSA and the others, potentially they share some of those bugs.
I can do PRs if you like. Right now our project is running on your "2.0.0-beta.4" i would like to have a stable version :-)
If you dont do maintenance on this project i would be ready to fork this - your library is awesome and much better than the crap Microsoft implemented for their authorization. Want to see it fly :-)
Instead of creating a Sequence
then copying to an array, use a IBufferWriter
AlgorithmId defines:
/// <summary>
/// 'ES256X'
/// https://tools.ietf.org/html/draft-ietf-cose-webauthn-algorithms-05
/// </summary>
ES256X = -46,
Shouldn't it be ES256K as defined here?
https://tools.ietf.org/html/rfc8812#section-3.3
(Sorry if I miss something here... But if it's the case, a comment may be a good idea.)
We're using X509 certs to sign and verify JWTs, and we have come across an issue where we have to explicitly state the signature algorithm in our policy because Jwk.Alg
is null.
I believe this could instead be read from the X509 cert and added to the JWK in FromX509Certificate:
X509Certificate2Collection collection = store.Certificates
.Find(
X509FindType.FindByThumbprint,
thumbprint ?? throw new ArgumentNullException(nameof(thumbprint)));
if (collection.Count == 0)
{
// ...
}
try
{
Jwk jwk = Jwk.FromX509Certificate(collection[0], withPrivateKey);
// jwk.Alg == null
// collection[0].SignatureAlgorithm.FriendlyName == "sha256RSA"
return jwk;
}
catch (Exception ex)
{
// ...
}
Basically supporting RFC 3279 and family (https://tools.ietf.org/html/rfc3279#section-2.2).
I have a token that was retrieved from Auth0, and issued a few minutes ago. When I validate the token with .EnableLifetimeValidation()
, the validation fails, with Status
being NotYetValid
. The code that does the validation is as follows:
var policy = new TokenValidationPolicyBuilder()
.RequireAudience("https://tv-dev-test/")
.RequireSignature(m_keys)
.RequireIssuer(m_domain)
.EnableLifetimeValidation()
.Build();
var reader = new JwtReader();
var header = AuthenticationHeaderValue.Parse(req.Headers[HeaderNames.Authorization]);
var result = reader.TryReadToken(header.Parameter, policy);
Console.WriteLine(result.Succedeed);
Console.WriteLine(result.Status);
Console.WriteLine(result.Exception?.ToString());
Console.WriteLine($"now is {DateTime.UtcNow} {DateTime.UtcNow.Kind}");
Console.WriteLine($"not before {result.Token.NotBefore} {result.Token.NotBefore?.Kind}");
Console.WriteLine($"issued at {result.Token.IssuedAt} {result.Token.IssuedAt?.Kind}");
Console.WriteLine($"expiration time {result.Token.ExpirationTime} {result.Token.ExpirationTime?.Kind}");
The output of this validation is as follows:
False
NotYetValid
now is 9/17/2020 9:49:05 PM Utc
not before
issued at 9/17/2020 9:09:54 PM Utc
expiration time 9/18/2020 9:09:54 PM Utc
The current time is definitely between the "issued at" and the "expires at" times, but the validation still fails. Is there something I am doing wrong?
Hi Yann,
The RFC 7516, while describing the plaintext does not specify that the content needs to be a valid JWS. I am trying to create a JWE using a string as my content, but I get the error that type (string) is not a valid JwsDescriptor. Below is the code that I am using
var keyEncryptionKey = Jwk.FromX509Certificate(new X509Certificate2(), false);
// Creates a JWE descriptor with all its properties
var descriptor = new JweDescriptor<string>()
{
EncryptionKey = keyEncryptionKey,
EncryptionAlgorithm = EncryptionAlgorithm.Aes256Gcm,
Algorithm = KeyManagementAlgorithm.RsaOaep,
Payload = "data"
};
// Generates the UTF-8 string representation of the JWT
var writer = new JwtWriter();
var token = writer.WriteTokenString(descriptor);
Is it possible to produce a such a JWE using the library? Could you help me with a code sample, if its possible?
Introduced in #454, the optimization for the empty data introduce a bug causing the prepend data is not used.
When the code hits JwtWriter.WriteTokenString() on a Windows Server 2012 R2, I get the following error but no other information.
Operation is not supported on this platform.
Stack trace doesn't seam to be much help,
The code is running fine on a number of other servers, all server have the "Microsoft .NET Core 5.00 Windows Server Hosting" installed all other functions appear to working fine.
Other Windows server including a 2012 R2 , 2016 and 2019 appear to be fine, any ideas how to narrow this problem down?
`static void Main()
{
var signatureKey = SymmetricJwk.FromBase64Url("R9MyWaEoyiMYViVWo8Fk4TUGWiSoaW6U1nOqXri8_XU");
var encryptionKey = new SymmetricJwk("R9MyWaEoyiMYViVWo8Fk4T");
var payload = new byte[] { 76, 105, 102, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32, 112, 114, 111, 115, 112, 101, 114, 46 };
var descriptor = new BinaryJweDescriptor(payload)
{
EncryptionKey = encryptionKey,
EncryptionAlgorithm = EncryptionAlgorithm.Aes128CbcHmacSha256,
Algorithm = KeyManagementAlgorithm.Aes128KW
};
var writer = new JwtWriter();
var token = writer.WriteTokenString(descriptor);
Console.WriteLine("The JWT is:");
Console.WriteLine(descriptor);
Console.WriteLine("payload:"+Encoding.UTF8.GetString(payload));
Console.WriteLine("Its compact form is:");
Console.WriteLine(token);
var policy = new TokenValidationPolicyBuilder()
.RequireSignature(signatureKey, SignatureAlgorithm.HmacSha256)
.RequireAudience("636C69656E745F6964")//this is not exist in payload,why result still succed?
.RequireIssuer("https://idp.example.com/")//this is not exist in payload,why result still succed?
.Build();
var reader = new JwtReader(encryptionKey);
var result = reader.TryReadToken(token, policy);
if (result.Succedeed)
{
Console.WriteLine("Decypt token is " + result.Token.Payload.ToString());//payload should be a byteArray,why here is null?
}
else
{
Console.WriteLine("Failed to read the token. Reason: " + Environment.NewLine + result.Status);
}
}`
some not reasonable.
If throw a byteArray or string Payload, that will be good.
It may be common to have a lots of issuers for secevent.
The current way to resolve this is to try to validate against each issuer policy.
A better way to achieve this is to lookup the issuer policy based on the 'iss' claim.
This require to review the TokenValidationPolicy
usage.
##[warning]/opt/hostedtoolcache/dotnet/sdk/3.0.100/Roslyn/Microsoft.CSharp.Core.targets(59,5): Warning MSB3052: The parameter to the compiler is invalid, '/define:$(BUILDCONFIGURATION)' will be ignored.
https://github.com/ycrumeyrolle/Jwt/blob/59bace31afecca486d80cfb87f40db5fdbec4cd8/src/OpenIdConnect/Address.cs#L39-L45
This type of comparison of the raw bytes (using ValueSpan) will not always work and miss property names that are "equal" but contain escaped characters.
Is this expected/fine? If not, consider using ValueTextEqual
APIs t do the property name comparisons. Generally speaking, getting the raw bytes via ValueSpan
and then using SequenceEqual
probably isn't what you want to do unless you control the input or only want to support the exact bytes.
https://github.com/dotnet/corefx/blob/d3911035f2ba3eb5c44310342cc1d654e42aa316/src/System.Text.Json/ref/System.Text.Json.cs#L316-L318
https://docs.microsoft.com/en-us/dotnet/api/system.text.json.utf8jsonreader.valuetextequals?view=netcore-3.0#System_Text_Json_Utf8JsonReader_ValueTextEquals_System_ReadOnlySpan_System_Byte__
For instance:
If the json payload contained formatted
this comparison will work fine, but it wouldn't work for \\u0066ormatted
(f == \u0066).
Something to consider.
I would double check usage of reader.ValueSpan
property in other instances as well (where the comparison is being deferred (when saved in JwtProperty
, etc.).
https://github.com/ycrumeyrolle/Jwt/search?utf8=%E2%9C%93&q=ValueSpan&type=
Like: https://github.com/ycrumeyrolle/Jwt/blob/90431793b0419333d1f47467192b3361f6c76e39/src/JsonWebToken/JwtObject.cs#L188
Also, is the JSON input here trusted or known to be valid UTF-8?
Hello,
I'm trying to generate a token, but it freezes at writing... am I doing something wrong?
I'm using F# with .netcore 3.1 on Mac OS (10.15.4).
printfn "JWT.create ..."
use key = new SymmetricJwk("R9MyWaEoyiMYViVWo8Fk4TUGWiSoaW6U1nOqXri8_XU", SignatureAlgorithm.HmacSha256)
printfn " -> key ok"
let descriptor =
JwsDescriptor(
KeyId = key.ToString(),
JwtId = Guid.NewGuid().ToString(),
IssuedAt = (DateTime.UtcNow |> Nullable),
ExpirationTime = (DateTime.UtcNow.AddMinutes(30.0) |> Nullable),
Issuer = (currentApp |> SoftwareComponent.value),
Audience = (currentApp |> SoftwareComponent.value)
)
|> tee (fun d -> d.ToString() |> printfn " -> descriptor ok \n %A")
printfn " -> writer"
let writer = JwtWriter()
printfn " -> writing token ..."
let token = writer.WriteTokenString(descriptor)
printfn " -> Token:\n%A" token
Output is:
JWT.create ...
-> key ok
-> descriptor ok
"{
"kid": "{\n \u0022kty\u0022: \u0022oct\u0022,\n \u0022alg\u0022: \u0022HS256\u0022,\n \u0022k\u0022: \u0022R9MyWaEoyiMYViVWo8Fk4TUGWiSoaW6U1nOqXri8_XU\u0022\n}"
}
.
{
"jti": "2e9996df-b8ca-4e7a-a369-8ae1b8d843ff",
"iat": 1588085668,
"exp": 1588087468,
"iss": "sc-eligibilityConfigurator-common-local@dev1-services",
"aud": "sc-eligibilityConfigurator-common-local@dev1-services"
}"
-> writer
-> writing token ...
JWE tokens generated using A128CBC_HS256 by other libraries fail to be decrypted correctly as the authentication fails. This seems to be due wrong length authentication tag being checked. It should only check the first 128 bits.
If you try to decrypt the example token given in RFC7516 the decryption fails.
RFC7518 specifies only the first 128 bits to be used in the tag
Example
using System;
using JsonWebToken;
public class Program
{
public static void Main()
{
// Key and token from https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.3
Jwk encryptionKey = new SymmetricJwk("GawgguFyGrWKav7AX4VKUg");
string token = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ.AxY8DCtDaGlsbGljb3RoZQ.KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.U0m_YmjN04DJvceFICbCVQ";
JwtReader jwtReader = new JwtReader(encryptionKey);
var policy = new TokenValidationPolicyBuilder().AcceptUnsecureToken().Build();
var result = jwtReader.TryReadToken(token, policy);
Console.WriteLine($"Decryption Statue: {result.Status}");
// The token should be decrypted and equal "Live long and prosper." However it fails decryption
}
}
Hello again,
I really tried to figure out this myself, but I couldn't.
I need to add a custom claim to payload:
{
...,
"groups": [
"first",
"second",
"third"
]
}
but so far I can only add it to look like this:
{
...,
"groups": {
"groups": [
"first",
"second",
"third"
]
}
}
This code leads to a "double nested" groups:
let array =
["first"; "second"; "third"]
|> List.map (fun v -> JwtValue(v))
|> System.Linq.Enumerable.ToList // convert to Collection.Generic.List
|> JwtArray
let property = JwtProperty("groups", array)
descriptor.AddClaim("groups", property)
I'd like to just add an array as claim
descriptor.AddClaim("groups", array)
but there is no such option.
I can create a JwtObject
and add an array there, but it also needs a key
and it has List<JwtProperty> _properties
inside. JwtProperty
AFAIK needs to have both name
and value
.
But if I create a token elsewhere, I can read value exactly the way I want to:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0MTg1YzQ0NC03OWNkLTRmNmUtODYwOC01N2E0NTI1YWFjNWYiLCJleHAiOjE2MTg3NDkyOTYsImlhdCI6MTU4NzY0NTI5NiwiaXNzIjoic2MtZWxpZ2liaWxpdHlDb25maWd1cmF0b3IiLCJncm91cHMiOlsiZmlyc3QiLCJzZWNvbmQiLCJ0aGlyZCJdfQ.gKXcjZkXLOI8MlImxiTRC4TuYa6UBvqw_Gb2ltJdZ4c
match result.Token.Payload.TryGetValue("groups") with
| true, groups -> printfn "groups <%A>:\n%A" (groups.Value.GetType()) groups.Value
| _ -> ()
Output:
groups <JsonWebToken.JwtArray>:
seq [JsonWebToken.JwtValue; JsonWebToken.JwtValue; JsonWebToken.JwtValue]
I even tried to fork it and "fix" it myself to the PR, but I don't know how to create a nameless property or claim.
This unit test fails:
[Fact]
public void DescriptorPayloadIsAStandardReferenceObject()
{
var descriptor = new JwsDescriptor(Jwk.None, SignatureAlgorithm.None);
var p1 = new JwtPayload { { "One", "Member" } };
var p1Content = p1.ToString();
descriptor.Payload = p1;
var p2 = new JwtPayload { { "Something", "else" } };
var p2Content = p2.ToString();
descriptor.Payload = p2;
Assert.Equal(p1.ToString(),p1Content);
Assert.Equal(p2.ToString(),p2Content);
}
With this:
Assert.Equal() Failure
↓ (pos 24)
Expected: ··· "Something": "else",\r\n "One": "Member"\r\n}
Actual: ··· "Something": "else"\r\n}
↑ (pos 24)
This is a rather surprising side effect. Considering the implementation (see below _payload.CopyTo(value);
), I'm wondering if this is intentional or not...
public override JwtPayload? Payload
{
get => _payload;
set
{
if (value is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
}
_payload.CopyTo(value);
_payload = value;
}
}
If its not, I can make a PR to fix this (including the above test).
See #498
Using a custom encoder will allow to avoid the escaping of some characters escaped by the default encoder like '+', '/', ...
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.