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

Siimplyfing methods of Decoder and Validator #233

Merged
merged 4 commits into from
Dec 2, 2019
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
22 changes: 14 additions & 8 deletions src/JWT/Builder/JwtBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,13 @@ private void TryCreateValidator()
private void EnsureCanBuild()
{
if (!CanBuild())
throw new InvalidOperationException("Can't build a token. Check if you have call all of the followng methods:\r\n" +
$"-{nameof(WithAlgorithm)}" + Environment.NewLine +
$"-{nameof(WithSerializer)}" + Environment.NewLine +
$"-{nameof(WithUrlEncoder)}.");
{
throw new InvalidOperationException(
"Can't build a token. Check if you have call all of the following methods:" +Environment.NewLine +
$"-{nameof(WithAlgorithm)}" + Environment.NewLine +
$"-{nameof(WithSerializer)}" + Environment.NewLine +
$"-{nameof(WithUrlEncoder)}.");
}

if (!HasOnlyOneSecret())
throw new InvalidOperationException("You can't provide more than one secret to use for encoding.");
Expand All @@ -315,10 +318,13 @@ private void EnsureCanBuild()
private void EnsureCanDecode()
{
if (!CanDecode())
throw new InvalidOperationException("Can't decode a token. Check if you have call all of the following methods:" + Environment.NewLine +
$"-{nameof(WithSerializer)}" + Environment.NewLine +
$"-{nameof(WithValidator)}" + Environment.NewLine +
$"-{nameof(WithUrlEncoder)}.");
{
throw new InvalidOperationException(
"Can't decode a token. Check if you have call all of the following methods:" + Environment.NewLine +
$"-{nameof(WithSerializer)}" + Environment.NewLine +
$"-{nameof(WithValidator)}" + Environment.NewLine +
$"-{nameof(WithUrlEncoder)}.");
}
}

/// <summary>
Expand Down
6 changes: 3 additions & 3 deletions src/JWT/IJwtDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public interface IJwtDecoder
/// <param name="keys">The keys bytes provided which one of them was used to sign the JWT</param>
/// <param name="verify">Whether to verify the signature (default is true)</param>
/// <returns>A string containing the JSON payload</returns>
string Decode(string token, IReadOnlyCollection<byte[]> keys, bool verify);
string Decode(string token, byte[][] keys, bool verify);

#endregion

Expand Down Expand Up @@ -104,7 +104,7 @@ public interface IJwtDecoder
/// <returns>An object representing the payload</returns>
/// <exception cref="SignatureVerificationException">Thrown if the verify parameter was true and the signature was NOT valid or if the JWT was signed with an unsupported algorithm</exception>
/// <exception cref="TokenExpiredException">Thrown if the verify parameter was true and the token has an expired exp claim</exception>
IDictionary<string, object> DecodeToObject(string token, IReadOnlyCollection<byte[]> keys, bool verify);
IDictionary<string, object> DecodeToObject(string token, byte[][] keys, bool verify);

#endregion

Expand Down Expand Up @@ -164,7 +164,7 @@ public interface IJwtDecoder
/// <returns>An object representing the payload</returns>
/// <exception cref="SignatureVerificationException">Thrown if the verify parameter was true and the signature was NOT valid or if the JWT was signed with an unsupported algorithm</exception>
/// <exception cref="TokenExpiredException">Thrown if the verify parameter was true and the token has an expired exp claim</exception>
T DecodeToObject<T>(string token, IReadOnlyCollection<byte[]> keys, bool verify);
T DecodeToObject<T>(string token, byte[][] keys, bool verify);

#endregion
}
Expand Down
12 changes: 1 addition & 11 deletions src/JWT/IJwtValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,6 @@
/// </summary>
public interface IJwtValidator
{
/// <summary>
/// Given the JWT, verifies its signature correctness.
/// </summary>
/// <param name="payloadJson">>An arbitrary payload (already serialized to JSON)</param>
/// <param name="decodedCrypto">Decoded body</param>
/// <param name="decodedSignature">Decoded signature</param>
/// <exception cref="SignatureVerificationException">The signature is invalid</exception>
/// <exception cref="TokenExpiredException">The token has expired</exception>
void Validate(string payloadJson, string decodedCrypto, string decodedSignature);

/// <summary>
/// Given the JWT, verifies its signatures correctness.
/// </summary>
Expand All @@ -23,6 +13,6 @@ public interface IJwtValidator
/// <param name="decodedSignatures">Decoded signatures</param>
/// <exception cref="SignatureVerificationException">The signature is invalid</exception>
/// <exception cref="TokenExpiredException">The token has expired</exception>
void Validate(string payloadJson, string decodedCrypto, string[] decodedSignatures);
void Validate(string payloadJson, string decodedCrypto, params string[] decodedSignatures);
}
}
6 changes: 3 additions & 3 deletions src/JWT/JWT.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
<!-- Include PDB in the built .nupkg -->
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>

<Version>5.3.1</Version>
<FileVersion>5.0.0.0</FileVersion>
<AssemblyVersion>5.0.0.0</AssemblyVersion>
<Version>6.0.0-beta1</Version>
<FileVersion>6.0.0.0</FileVersion>
<AssemblyVersion>6.0.0.0</AssemblyVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
58 changes: 11 additions & 47 deletions src/JWT/JwtDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ public string Decode(string token, byte[] key, bool verify)
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="FormatException" />
public string Decode(string token, IReadOnlyCollection<byte[]> keys, bool verify)
public string Decode(string token, byte[][] keys, bool verify)
{
if (String.IsNullOrWhiteSpace(token))
throw new ArgumentException(nameof(token));
if (keys is null)
throw new ArgumentNullException(nameof(keys));
if (keys.Count == 0 || !AllKeysHaveValues(keys))
if (keys.Length == 0 || !AllKeysHaveValues(keys))
throw new ArgumentOutOfRangeException(nameof(keys));

if (verify)
Expand Down Expand Up @@ -152,7 +152,7 @@ public IDictionary<string, object> DecodeToObject(string token, byte[] key, bool
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="FormatException" />
public IDictionary<string, object> DecodeToObject(string token, IReadOnlyCollection<byte[]> keys, bool verify) =>
public IDictionary<string, object> DecodeToObject(string token, byte[][] keys, bool verify) =>
DecodeToObject<Dictionary<string, object>>(token, keys, verify);

/// <inheritdoc />
Expand Down Expand Up @@ -193,75 +193,38 @@ public T DecodeToObject<T>(string token, byte[] key, bool verify)
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="FormatException" />
public T DecodeToObject<T>(string token, IReadOnlyCollection<byte[]> keys, bool verify)
public T DecodeToObject<T>(string token, byte[][] keys, bool verify)
{
var payload = Decode(token, keys, verify);
return _jsonSerializer.Deserialize<T>(payload);
}

/// <summary>
/// Prepares data before calling <see cref="IJwtValidator.Validate(string,string,string)" />
/// Prepares data before calling <see cref="IJwtValidator.Validate" />
/// </summary>
/// <param name="parts">The array representation of a JWT</param>
/// <param name="key">The key that was used to sign the JWT</param>
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="FormatException" />
public void Validate(string[] parts, byte[] key) =>
public void Validate(string[] parts, params byte[] key) =>
Validate(new JwtParts(parts), key);

/// <summary>
/// Prepares data before calling <see cref="IJwtValidator.Validate(string,string,string)" />
/// </summary>
/// <param name="jwt">The JWT parts</param>
/// <param name="key">The key that was used to sign the JWT</param>
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="FormatException" />
public void Validate(JwtParts jwt, byte[] key)
{
if (jwt is null)
throw new ArgumentNullException(nameof(jwt));
if (key is null)
throw new ArgumentNullException(nameof(key));
if (key.Length == 0)
throw new ArgumentOutOfRangeException(nameof(key));

var crypto = _urlEncoder.Decode(jwt.Signature);
var decodedCrypto = Convert.ToBase64String(crypto);

var headerJson = GetString(_urlEncoder.Decode(jwt.Header));
var headerData = _jsonSerializer.Deserialize<Dictionary<string, object>>(headerJson);

var payload = jwt.Payload;
var payloadJson = GetString(_urlEncoder.Decode(payload));

var bytesToSign = GetBytes(String.Concat(jwt.Header, ".", payload));

var algName = (string)headerData["alg"];
var alg = _algFactory.Create(algName);

var signatureData = alg.Sign(key, bytesToSign);
var decodedSignature = Convert.ToBase64String(signatureData);

_jwtValidator.Validate(payloadJson, decodedCrypto, decodedSignature);
}

/// <summary>
/// Prepares data before calling <see cref="IJwtValidator.Validate(string,string,string[])" />
/// Prepares data before calling <see cref="IJwtValidator.Validate" />
/// </summary>
/// <param name="jwt">The JWT parts</param>
/// <param name="keys">The keys provided which one of them was used to sign the JWT</param>
/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="FormatException" />
public void Validate(JwtParts jwt, IReadOnlyCollection<byte[]> keys)
public void Validate(JwtParts jwt, params byte[][] keys)
{
if (jwt is null)
throw new ArgumentNullException(nameof(jwt));
if (keys is null)
throw new ArgumentNullException(nameof(keys));
if (keys.Count == 0 || !AllKeysHaveValues(keys))
if (keys.Length == 0 || !AllKeysHaveValues(keys))
throw new ArgumentOutOfRangeException(nameof(keys));

var crypto = _urlEncoder.Decode(jwt.Signature);
Expand All @@ -281,10 +244,11 @@ public void Validate(JwtParts jwt, IReadOnlyCollection<byte[]> keys)
var decodedSignatures = keys.Select(key => alg.Sign(key, bytesToSign))
.Select(sd => Convert.ToBase64String(sd))
.ToArray();

_jwtValidator.Validate(payloadJson, decodedCrypto, decodedSignatures);
}

private static bool AllKeysHaveValues(IEnumerable<byte[]> keys) =>
keys.All(key => key.Any());
}
}
}
35 changes: 4 additions & 31 deletions src/JWT/JwtValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,7 @@ public JwtValidator(IJsonSerializer jsonSerializer, IDateTimeProvider dateTimePr
/// <inheritdoc />
/// <exception cref="ArgumentException" />
/// <exception cref="SignatureVerificationException" />
public void Validate(string payloadJson, string decodedCrypto, string decodedSignature)
{
var ex = GetValidationException(payloadJson, decodedCrypto, decodedSignature);
if (ex != null)
throw ex;
}

/// <inheritdoc />
/// <exception cref="ArgumentException" />
/// <exception cref="SignatureVerificationException" />
public void Validate(string payloadJson, string decodedCrypto, string[] decodedSignatures)
public void Validate(string payloadJson, string decodedCrypto, params string[] decodedSignatures)
{
var ex = GetValidationException(payloadJson, decodedCrypto, decodedSignatures);
if (ex != null)
Expand Down Expand Up @@ -73,24 +63,7 @@ public bool TryValidate(string payloadJson, string decodedCrypto, string[] decod
return ex is null;
}

private Exception GetValidationException(string payloadJson, string decodedCrypto, string decodedSignature)
{
if (String.IsNullOrWhiteSpace(payloadJson))
return new ArgumentException(nameof(payloadJson));

if (String.IsNullOrWhiteSpace(decodedCrypto))
return new ArgumentException(nameof(decodedCrypto));

if (String.IsNullOrWhiteSpace(decodedSignature))
return new ArgumentException(nameof(decodedSignature));

if (!CompareCryptoWithSignature(decodedCrypto, decodedSignature))
return new SignatureVerificationException(decodedCrypto, decodedSignature);

return GetValidationException(payloadJson);
}

private Exception GetValidationException(string payloadJson, string decodedCrypto, string[] decodedSignatures)
private Exception GetValidationException(string payloadJson, string decodedCrypto, params string[] decodedSignatures)
{
if (String.IsNullOrWhiteSpace(payloadJson))
return new ArgumentException(nameof(payloadJson));
Expand Down Expand Up @@ -123,7 +96,7 @@ private static bool AreAllDecodedSignaturesNullOrWhiteSpace(IEnumerable<string>
private static bool IsAnySignatureValid(string decodedCrypto, IEnumerable<string> decodedSignatures) =>
decodedSignatures.Any(decodedSignature => CompareCryptoWithSignature(decodedCrypto, decodedSignature));

/// <remarks>In the future this method can be opened for extension so made protected virtual</remarks>
/// <remarks>In the future this method can be opened for extension thus made protected virtual</remarks>
private static bool CompareCryptoWithSignature(string decodedCrypto, string decodedSignature)
{
if (decodedCrypto.Length != decodedSignature.Length)
Expand All @@ -147,7 +120,7 @@ private static bool CompareCryptoWithSignature(string decodedCrypto, string deco
/// <remarks>See https://tools.ietf.org/html/rfc7515#section-4.1.4</remarks>
/// <exception cref="SignatureVerificationException" />
/// <exception cref="TokenExpiredException" />
private static Exception ValidateExpClaim(IDictionary<string, object> payloadData, double secondsSinceEpoch)
private static Exception ValidateExpClaim(IReadOnlyDictionary<string, object> payloadData, double secondsSinceEpoch)
{
if (!payloadData.TryGetValue("exp", out var expObj))
return null;
Expand Down
2 changes: 1 addition & 1 deletion src/JWT/TokenExpiredException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public TokenExpiredException(string message)
/// <summary>
/// The payload.
/// </summary>
public IDictionary<string, object> PayloadData
public IReadOnlyDictionary<string, object> PayloadData
{
get => GetOrDefault<Dictionary<string, object>>(PayloadDataKey);
internal set => this.Data.Add(PayloadDataKey, value);
Expand Down