Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added token validation parameters #376

Merged
merged 4 commits into from
Apr 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,38 @@ var alg = header.Algorithm; // RS256
var kid = header.KeyId; // CFAEAE2D650A6CA9862575DE54371EA980643849
```

### Turning off parts of token validation

If you wish to validate a token but ignore certain parts of the validation (such as the lifetime of the token when refreshing the token), you can pass a `ValidateParameters` object to the constructor of the `JwtValidator` class.

```c#
var validationParameters = new ValidationParameters
{
ValidateSignature = true,
ValidateExpirationTime = true,
ValidateIssuedTime = true
};
IJwtValidator validator = new JwtValidator(serializer, provider, validationParameters);
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
var json = decoder.Decode(expiredToken, secret, verify: true); // will not throw because of expired token
```

#### Or using the fluent builder API

```c#
var json = JwtBuilder.Create()
.WithAlgorithm(new HMACSHA256Algorithm())
.WithSecret(secret)
.WithValidationParameters(
new ValidationParameters
{
ValidateSignature = true,
ValidateExpirationTime = true,
ValidateIssuedTime = true
})
.Decode(expiredToken);
```

### Custom JSON serializer

By default JSON serialization is performed by JsonNetSerializer implemented using [Json.Net](https://www.json.net). To use a different one, implement the `IJsonSerializer` interface:
Expand Down
2 changes: 1 addition & 1 deletion src/JWT/Algorithms/ECDSAAlgorithmFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,4 @@ private IJwtAlgorithm CreateES512Algorithm()
}
#endif
}
}
}
2 changes: 1 addition & 1 deletion src/JWT/Algorithms/IJwtAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ public static class JwtAlgorithmExtensions
public static bool IsAsymmetric(this IJwtAlgorithm alg) =>
alg is IAsymmetricAlgorithm;
}
}
}
6 changes: 3 additions & 3 deletions src/JWT/Algorithms/NoneAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ namespace JWT.Algorithms
/// Implements the "None" algorithm.
/// </summary>
/// <see href="https://datatracker.ietf.org/doc/html/rfc7519#section-6">RFC-7519</see>
public class NoneAlgorithm : IJwtAlgorithm
public sealed class NoneAlgorithm : IJwtAlgorithm
{
/// <inheritdoc />
public string Name => "none";

/// <inheritdoc />
public HashAlgorithmName HashAlgorithmName =>
public HashAlgorithmName HashAlgorithmName =>
throw new NotSupportedException("The None algorithm doesn't have any hash algorithm.");

/// <inheritdoc />
public byte[] Sign(byte[] key, byte[] bytesToSign) =>
throw new NotSupportedException("The None algorithm doesn't support signing.");
}
}
}
2 changes: 1 addition & 1 deletion src/JWT/Algorithms/RS1024Algorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ public RS1024Algorithm(X509Certificate2 cert)
/// <inheritdoc />
public override HashAlgorithmName HashAlgorithmName => HashAlgorithmName.SHA512;
}
}
}
2 changes: 1 addition & 1 deletion src/JWT/Algorithms/RS2048Algorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ public RS2048Algorithm(X509Certificate2 cert)
/// <inheritdoc />
public override HashAlgorithmName HashAlgorithmName => HashAlgorithmName.SHA512;
}
}
}
2 changes: 1 addition & 1 deletion src/JWT/Algorithms/RS384Algorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ public RS384Algorithm(X509Certificate2 cert)
/// <inheritdoc />
public override HashAlgorithmName HashAlgorithmName => HashAlgorithmName.SHA384;
}
}
}
2 changes: 1 addition & 1 deletion src/JWT/Algorithms/RS4096Algorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ public RS4096Algorithm(X509Certificate2 cert)
/// <inheritdoc />
public override HashAlgorithmName HashAlgorithmName => HashAlgorithmName.SHA512;
}
}
}
8 changes: 4 additions & 4 deletions src/JWT/Algorithms/RSAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public abstract class RSAlgorithm : IAsymmetricAlgorithm
/// </summary>
/// <param name="publicKey">The public key for verifying the data.</param>
/// <param name="privateKey">The private key for signing the data.</param>
public RSAlgorithm(RSA publicKey, RSA privateKey)
protected RSAlgorithm(RSA publicKey, RSA privateKey)
{
_publicKey = publicKey ?? throw new ArgumentNullException(nameof(publicKey));
_privateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey));
Expand All @@ -30,7 +30,7 @@ public RSAlgorithm(RSA publicKey, RSA privateKey)
/// An instance created using this constructor can only be used for verifying the data, not for signing it.
/// </remarks>
/// <param name="publicKey">The public key for verifying the data.</param>
public RSAlgorithm(RSA publicKey)
protected RSAlgorithm(RSA publicKey)
{
_publicKey = publicKey ?? throw new ArgumentNullException(nameof(publicKey));
_privateKey = null;
Expand All @@ -40,7 +40,7 @@ public RSAlgorithm(RSA publicKey)
/// Creates an instance using the provided certificate.
/// </summary>
/// <param name="cert">The certificate having a public key and an optional private key.</param>
public RSAlgorithm(X509Certificate2 cert)
protected RSAlgorithm(X509Certificate2 cert)
{
_publicKey = GetPublicKey(cert) ?? throw new Exception("Certificate's PublicKey cannot be null.");
_privateKey = GetPrivateKey(cert);
Expand Down Expand Up @@ -105,4 +105,4 @@ private static RSA GetPublicKey(X509Certificate2 cert)
#endif
}
}
}
}
4 changes: 3 additions & 1 deletion src/JWT/Algorithms/RSAlgorithmFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ private RS512Algorithm CreateRS512Algorithm()

throw new InvalidOperationException("Can't create a new algorithm without a certificate factory, private key or public key");
}

private RS1024Algorithm CreateRS1024Algorithm()
{
if (_certFactory is object)
Expand All @@ -133,6 +134,7 @@ private RS1024Algorithm CreateRS1024Algorithm()

throw new InvalidOperationException("Can't create a new algorithm without a certificate factory, private key or public key");
}

private RS2048Algorithm CreateRS2048Algorithm()
{
if (_certFactory is object)
Expand Down Expand Up @@ -169,4 +171,4 @@ private RS4096Algorithm CreateRS4096Algorithm()
throw new InvalidOperationException("Can't create a new algorithm without a certificate factory, private key or public key");
}
}
}
}
65 changes: 56 additions & 9 deletions src/JWT/Builder/JwtBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ public sealed class JwtBuilder
private IJsonSerializer _serializer = new JsonNetSerializer();
private IBase64UrlEncoder _urlEncoder = new JwtBase64UrlEncoder();
private IDateTimeProvider _dateTimeProvider = new UtcDateTimeProvider();
private ValidationParameters _valParams = ValidationParameters.Default;

private IJwtAlgorithm _algorithm;
private IAlgorithmFactory _algFactory;
private byte[][] _secrets;
private bool _verify;

/// <summary>
/// Creates a new instance of instance <see cref="JwtBuilder" />
Expand All @@ -44,7 +44,7 @@ public JwtBuilder AddHeader(HeaderName name, object value)
_jwt.Header.Add(name.GetHeaderName(), value);
return this;
}

/// <summary>
/// Add header to the JWT.
/// </summary>
Expand Down Expand Up @@ -208,7 +208,19 @@ public JwtBuilder DoNotVerifySignature() =>
/// <returns>Current builder instance</returns>
public JwtBuilder WithVerifySignature(bool verify)
{
_verify = verify;
_valParams = _valParams.With(p => p.ValidateSignature = verify);

return this;
}

/// <summary>
/// Instructs whether to verify the JWT signature and what parts of the validation to perform.
/// </summary>
/// <param name="valParams">Parameters to be used for validation</param>
/// <returns>Current builder instance</returns>
public JwtBuilder WithValidationParameters(ValidationParameters valParams)
{
_valParams = valParams;
return this;
}

Expand All @@ -233,7 +245,7 @@ public string Decode(string token)
{
EnsureCanDecode();

return _verify ? _decoder.Decode(token, _secrets, _verify) : _decoder.Decode(token);
return _decoder.Decode(token, _secrets, _valParams.ValidateSignature);
}

/// <summary>
Expand All @@ -253,7 +265,7 @@ public string DecodeHeader(string token)
/// <param name="token">The JWT</param>
public T DecodeHeader<T>(string token)
{
EnsureCanDecode();
EnsureCanDecodeHeader();

return _decoder.DecodeHeader<T>(token);
}
Expand All @@ -267,7 +279,7 @@ public T Decode<T>(string token)
{
EnsureCanDecode();

return _verify ? _decoder.DecodeToObject<T>(token, _secrets, _verify) : _decoder.DecodeToObject<T>(token);
return _decoder.DecodeToObject<T>(token, _secrets, _valParams.ValidateSignature);
}

private void TryCreateEncoder()
Expand Down Expand Up @@ -295,10 +307,20 @@ private void TryCreateDecoder()
_decoder = new JwtDecoder(_serializer, _validator, _urlEncoder, _algorithm);
else if (_algFactory is object)
_decoder = new JwtDecoder(_serializer, _validator, _urlEncoder, _algFactory);
else if (!_verify)
else if (!_valParams.ValidateSignature)
_decoder = new JwtDecoder(_serializer, _urlEncoder);
}

private void TryCreateDecoderForHeader()
{
if (_serializer is null)
throw new InvalidOperationException($"Can't instantiate {nameof(JwtDecoder)}. Call {nameof(WithSerializer)}.");
if (_urlEncoder is null)
throw new InvalidOperationException($"Can't instantiate {nameof(JwtDecoder)}. Call {nameof(WithUrlEncoder)}.");

_decoder = new JwtDecoder(_serializer, _urlEncoder);
}

private void TryCreateValidator()
{
if (_validator is object)
Expand All @@ -309,7 +331,7 @@ private void TryCreateValidator()
if (_dateTimeProvider is null)
throw new InvalidOperationException($"Can't instantiate {nameof(JwtValidator)}. Call {nameof(WithDateTimeProvider)}.");

_validator = new JwtValidator(_serializer, _dateTimeProvider);
_validator = new JwtValidator(_serializer, _dateTimeProvider, _valParams);
}

private void EnsureCanEncode()
Expand Down Expand Up @@ -342,6 +364,20 @@ private void EnsureCanDecode()
}
}

private void EnsureCanDecodeHeader()
{
if (_decoder is null)
TryCreateDecoderForHeader();

if (!CanDecodeHeader())
{
throw new InvalidOperationException(
"Can't decode a token header. Check if you have call all of the following methods:" + Environment.NewLine +
$"-{nameof(WithSerializer)}" + Environment.NewLine +
$"-{nameof(WithUrlEncoder)}.");
}
}

/// <summary>
/// Checks whether enough dependencies were supplied to encode a new token.
/// </summary>
Expand All @@ -359,10 +395,21 @@ private bool CanDecode()
if (_urlEncoder is null)
return false;

if (_verify)
if (_valParams.ValidateSignature)
return _validator is object && (_algorithm is object || _algFactory is object);

return true;
}

private bool CanDecodeHeader()
{
if (_urlEncoder is null)
return false;

if (_serializer is null)
return false;

return true;
}
}
}
4 changes: 2 additions & 2 deletions src/JWT/JWT.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<Authors>Alexander Batishchev, John Sheehan, Michael Lehenbauer</Authors>
<PackageTags>jwt;json;authorization</PackageTags>
<PackageLicense>CC0-1.0</PackageLicense>
<Version>9.0.0-beta1</Version>
<Version>9.0.0-beta2</Version>
<FileVersion>9.0.0.0</FileVersion>
<AssemblyVersion>9.0.0.0</AssemblyVersion>
<RootNamespace>JWT</RootNamespace>
Expand Down Expand Up @@ -56,4 +56,4 @@
<PackageReference Include="System.Security.Cryptography.Csp" Version="4.3.0" />
</ItemGroup>

</Project>
</Project>
2 changes: 1 addition & 1 deletion src/JWT/JwtDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ public void Validate(JwtParts jwt, params byte[][] keys)
private static bool AllKeysHaveValues(byte[][] keys)
{
if (keys is null)
return true;
return false;

if (keys.Length == 0)
return false;
Expand Down
Loading