Skip to content

Commit

Permalink
Ordering overloads of Encode() and Decode(). Moving private methods t…
Browse files Browse the repository at this point in the history
…o the bottom. (jwt-dotnet#51)
  • Loading branch information
abatishchev authored Sep 13, 2016
1 parent b8f9747 commit d848369
Showing 1 changed file with 95 additions and 97 deletions.
192 changes: 95 additions & 97 deletions JWT/JWT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace JWT
{
/// <summary>
/// <summary>
/// Provides methods for encoding and decoding JSON Web Tokens.
/// </summary>
public static class JsonWebToken
Expand All @@ -30,35 +30,15 @@ static JsonWebToken()
}

/// <summary>
/// Creates a JWT given a header, a payload, the signing key, and the algorithm to use.
/// Creates a JWT given a payload, the signing key, and the algorithm to use.
/// </summary>
/// <param name="extraHeaders">An arbitrary set of extra headers. Will be augmented with the standard "typ" and "alg" headers.</param>
/// <param name="payload">An arbitrary payload (must be serializable to JSON via <see cref="System.Web.Script.Serialization.JavaScriptSerializer"/>).</param>
/// <param name="key">The key bytes used to sign the token.</param>
/// <param name="key">The key used to sign the token.</param>
/// <param name="algorithm">The hash algorithm to use.</param>
/// <returns>The generated JWT.</returns>
public static string Encode(IDictionary<string, object> extraHeaders, object payload, byte[] key, JwtHashAlgorithm algorithm)
public static string Encode(object payload, string key, JwtHashAlgorithm algorithm)
{
var segments = new List<string>();
var header = new Dictionary<string, object>(extraHeaders)
{
{ "typ", "JWT" },
{ "alg", algorithm.ToString() }
};

var headerBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(header));
var payloadBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(payload));

segments.Add(Base64UrlEncode(headerBytes));
segments.Add(Base64UrlEncode(payloadBytes));

var stringToSign = string.Join(".", segments.ToArray());
var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);

var signature = HashAlgorithms[algorithm](key, bytesToSign);
segments.Add(Base64UrlEncode(signature));

return string.Join(".", segments.ToArray());
return Encode(new Dictionary<string, object>(), payload, Encoding.UTF8.GetBytes(key), algorithm);
}

/// <summary>
Expand Down Expand Up @@ -87,15 +67,49 @@ public static string Encode(IDictionary<string, object> extraHeaders, object pay
}

/// <summary>
/// Creates a JWT given a payload, the signing key, and the algorithm to use.
/// Creates a JWT given a header, a payload, the signing key, and the algorithm to use.
/// </summary>
/// <param name="extraHeaders">An arbitrary set of extra headers. Will be augmented with the standard "typ" and "alg" headers.</param>
/// <param name="payload">An arbitrary payload (must be serializable to JSON via <see cref="System.Web.Script.Serialization.JavaScriptSerializer"/>).</param>
/// <param name="key">The key used to sign the token.</param>
/// <param name="key">The key bytes used to sign the token.</param>
/// <param name="algorithm">The hash algorithm to use.</param>
/// <returns>The generated JWT.</returns>
public static string Encode(object payload, string key, JwtHashAlgorithm algorithm)
public static string Encode(IDictionary<string, object> extraHeaders, object payload, byte[] key, JwtHashAlgorithm algorithm)
{
return Encode(new Dictionary<string, object>(), payload, Encoding.UTF8.GetBytes(key), algorithm);
var segments = new List<string>();
var header = new Dictionary<string, object>(extraHeaders)
{
{ "typ", "JWT" },
{ "alg", algorithm.ToString() }
};

var headerBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(header));
var payloadBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(payload));

segments.Add(Base64UrlEncode(headerBytes));
segments.Add(Base64UrlEncode(payloadBytes));

var stringToSign = string.Join(".", segments.ToArray());
var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);

var signature = HashAlgorithms[algorithm](key, bytesToSign);
segments.Add(Base64UrlEncode(signature));

return string.Join(".", segments.ToArray());
}

/// <summary>
/// Given a JWT, decode it and return the JSON payload.
/// </summary>
/// <param name="token">The JWT.</param>
/// <param name="key">The key that 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>
/// <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>
public static string Decode(string token, string key, bool verify = true)
{
return Decode(token, Encoding.UTF8.GetBytes(key), verify);
}

/// <summary>
Expand Down Expand Up @@ -138,48 +152,18 @@ public static string Decode(string token, byte[] key, bool verify = true)
return payloadJson;
}

private static void Verify(string decodedCrypto, string decodedSignature, string payloadJson)
{
if (decodedCrypto != decodedSignature)
{
throw new SignatureVerificationException(string.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature));
}

// verify exp claim https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.4
var payloadData = JsonSerializer.Deserialize<Dictionary<string, object>>(payloadJson);
object expObj;
if (!payloadData.TryGetValue("exp", out expObj) || expObj == null)
{
return;
}
int expInt;
try
{
expInt = Convert.ToInt32(expObj);
}
catch (FormatException)
{
throw new SignatureVerificationException("Claim 'exp' must be an integer.");
}
var secondsSinceEpoch = Math.Round((DateTime.UtcNow - UnixEpoch).TotalSeconds);
if (secondsSinceEpoch >= expInt)
{
throw new TokenExpiredException("Token has expired.");
}
}

/// <summary>
/// Given a JWT, decode it and return the JSON payload.
/// Given a JWT, decode it and return the payload as an object (by deserializing it with <see cref="System.Web.Script.Serialization.JavaScriptSerializer"/>).
/// </summary>
/// <param name="token">The JWT.</param>
/// <param name="key">The key that 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>
/// <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>
public static string Decode(string token, string key, bool verify = true)
public static object DecodeToObject(string token, string key, bool verify = true)
{
return Decode(token, Encoding.UTF8.GetBytes(key), verify);
return DecodeToObject(token, Encoding.UTF8.GetBytes(key), verify);
}

/// <summary>
Expand All @@ -194,22 +178,22 @@ public static string Decode(string token, string key, bool verify = true)
public static object DecodeToObject(string token, byte[] key, bool verify = true)
{
var payloadJson = Decode(token, key, verify);
var payloadData = JsonSerializer.Deserialize<Dictionary<string, object>>(payloadJson);
return payloadData;
return JsonSerializer.Deserialize<Dictionary<string, object>>(payloadJson);
}

/// <summary>
/// Given a JWT, decode it and return the payload as an object (by deserializing it with <see cref="System.Web.Script.Serialization.JavaScriptSerializer"/>).
/// </summary>
/// <typeparam name="T">The <see cref="Type"/> to return</typeparam>
/// <param name="token">The JWT.</param>
/// <param name="key">The key that was used to sign the JWT.</param>
/// <param name="verify">Whether to verify the signature (default is true).</param>
/// <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>
public static object DecodeToObject(string token, string key, bool verify = true)
public static T DecodeToObject<T>(string token, string key, bool verify = true)
{
return DecodeToObject(token, Encoding.UTF8.GetBytes(key), verify);
return DecodeToObject<T>(token, Encoding.UTF8.GetBytes(key), verify);
}

/// <summary>
Expand All @@ -225,37 +209,10 @@ public static object DecodeToObject(string token, string key, bool verify = true
public static T DecodeToObject<T>(string token, byte[] key, bool verify = true)
{
var payloadJson = Decode(token, key, verify);
var payloadData = JsonSerializer.Deserialize<T>(payloadJson);
return payloadData;
}

/// <summary>
/// Given a JWT, decode it and return the payload as an object (by deserializing it with <see cref="System.Web.Script.Serialization.JavaScriptSerializer"/>).
/// </summary>
/// <typeparam name="T">The <see cref="Type"/> to return</typeparam>
/// <param name="token">The JWT.</param>
/// <param name="key">The key that was used to sign the JWT.</param>
/// <param name="verify">Whether to verify the signature (default is true).</param>
/// <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>
public static T DecodeToObject<T>(string token, string key, bool verify = true)
{
return DecodeToObject<T>(token, Encoding.UTF8.GetBytes(key), verify);
}

private static JwtHashAlgorithm GetHashAlgorithm(string algorithm)
{
switch (algorithm)
{
case "HS256": return JwtHashAlgorithm.HS256;
case "HS384": return JwtHashAlgorithm.HS384;
case "HS512": return JwtHashAlgorithm.HS512;
default: throw new SignatureVerificationException("Algorithm not supported.");
}
return JsonSerializer.Deserialize<T>(payloadJson);
}

// from JWT spec
/// <remarks>From JWT spec</remarks>
public static string Base64UrlEncode(byte[] input)
{
var output = Convert.ToBase64String(input);
Expand All @@ -265,7 +222,7 @@ public static string Base64UrlEncode(byte[] input)
return output;
}

// from JWT spec
/// <remarks>From JWT spec</remarks>
public static byte[] Base64UrlDecode(string input)
{
var output = input;
Expand All @@ -281,5 +238,46 @@ public static byte[] Base64UrlDecode(string input)
var converted = Convert.FromBase64String(output); // Standard base64 decoder
return converted;
}

private static JwtHashAlgorithm GetHashAlgorithm(string algorithm)
{
switch (algorithm)
{
case "HS256": return JwtHashAlgorithm.HS256;
case "HS384": return JwtHashAlgorithm.HS384;
case "HS512": return JwtHashAlgorithm.HS512;
default: throw new SignatureVerificationException("Algorithm not supported.");
}
}

private static void Verify(string decodedCrypto, string decodedSignature, string payloadJson)
{
if (decodedCrypto != decodedSignature)
{
throw new SignatureVerificationException(string.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature));
}

// verify exp claim https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.4
var payloadData = JsonSerializer.Deserialize<Dictionary<string, object>>(payloadJson);
object expObj;
if (!payloadData.TryGetValue("exp", out expObj) || expObj == null)
{
return;
}
int expInt;
try
{
expInt = Convert.ToInt32(expObj);
}
catch (FormatException)
{
throw new SignatureVerificationException("Claim 'exp' must be an integer.");
}
var secondsSinceEpoch = Math.Round((DateTime.UtcNow - UnixEpoch).TotalSeconds);
if (secondsSinceEpoch >= expInt)
{
throw new TokenExpiredException("Token has expired.");
}
}
}
}

0 comments on commit d848369

Please sign in to comment.