Hi,
I am quite unsure where to resolve this. Basically, when I call my web api authorized via Auth0, it is showing me this error:
For algorithm RS256 please create custom factory by implementing IAlgorithmFactory
“Message”: “An error has occurred.”,
“ExceptionMessage”: “For algorithm RS256 please create custom factory by
implementing IAlgorithmFactory”,
“ExceptionType”: “System.NotSupportedException”,
“StackTrace”: " at JWT.Algorithms.HMACSHAAlgorithmFactory.Create(JwtHashAlgorithm
algorithm)\r\n at
JWT.JwtDecoder.Validate(String
payload, String payloadJson, String]
parts, Byte] key)\r\n at
JWT.JwtDecoder.Decode(String token,
Byte] key, Boolean verify)\r\n at
Auth0API.App_Start.JsonWebToken.ValidateToken(String
token, String secretKey, String
audience, Boolean checkExpiration,
String issuer, Boolean
isSecretBase64Encoded) in
C:\Auth0TestProject\Auth0API\App_Start\JsonWebToken.cs:line
33\r\n at
Auth0API.App_Start.JsonWebTokenValidationHandler.SendAsync(HttpRequestMessage
request, CancellationToken
cancellationToken) in
C:\Auth0TestProject\Auth0API\App_Start\JsonWebTokenValidationHandler.cs:line
59"
Here’s my JsonWebTokenValidationHandler.cs class
namespace Auth0API.App_Start
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
public class JsonWebTokenValidationHandler : DelegatingHandler
{
public string SymmetricKey { get; set; }
public string Audience { get; set; }
public string Issuer { get; set; }
public bool IsSecretBase64Encoded { get; set; }
public JsonWebTokenValidationHandler()
{
IsSecretBase64Encoded = true;
}
private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
{
token = null;
IEnumerable<string> authzHeaders;
if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
{
// Fail if no Authorization header or more than one Authorization headers
// are found in the HTTP request
return false;
}
// Remove the bearer token scheme prefix and return the rest as ACS token
var bearerToken = authzHeaders.ElementAt(0);
token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
return true;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
string token;
HttpResponseMessage errorResponse = null;
if (TryRetrieveToken(request, out token))
{
try
{
var secret = this.SymmetricKey;
if (IsSecretBase64Encoded)
secret = secret.Replace('-', '+').Replace('_', '/');
Thread.CurrentPrincipal = JsonWebToken.ValidateToken(
token,
secret,
audience: this.Audience,
checkExpiration: true,
issuer: this.Issuer,
isSecretBase64Encoded: IsSecretBase64Encoded);
if (HttpContext.Current != null)
{
HttpContext.Current.User = Thread.CurrentPrincipal;
}
}
catch (JWT.SignatureVerificationException ex)
{
errorResponse = request.CreateErrorResponse(HttpStatusCode.Unauthorized, ex);
}
catch (JsonWebToken.TokenValidationException ex)
{
errorResponse = request.CreateErrorResponse(HttpStatusCode.Unauthorized, ex);
}
catch (Exception ex)
{
errorResponse = request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
}
return errorResponse != null ?
Task.FromResult(errorResponse) :
base.SendAsync(request, cancellationToken);
}
}
}
Here’s my JsonWebToken.cs class
namespace Auth0API.App_Start
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using Newtonsoft.Json.Linq;
using System.Text;
public static class JsonWebToken
{
private const string NameClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name";
private const string RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role";
private const string ActorClaimType = "http://schemas.xmlsoap.org/ws/2009/09/identity/claims/actor";
private const string DefaultIssuer = "LOCAL AUTHORITY";
private const string StringClaimValueType = "http://www.w3.org/2001/XMLSchema#string";
// sort claim types by relevance
private static IEnumerable<string> claimTypesForUserName = new] { "name", "email", "user_id", "sub" };
private static ISet<string> claimsToExclude = new HashSet<string>(new] { "iss", "sub", "aud", "exp", "iat", "identities" });
private static DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public static ClaimsPrincipal ValidateToken(string token, string secretKey, string audience = null, bool checkExpiration = false, string issuer = null, bool isSecretBase64Encoded = true)
{
byte] secret;
if (isSecretBase64Encoded)
secret = Convert.FromBase64String(secretKey);
else
secret = Encoding.UTF8.GetBytes(secretKey);
var payloadJson = JWT.JsonWebToken.Decode(token, secret, verify: true);
var payloadData = JObject.Parse(payloadJson).ToObject<Dictionary<string, object>>();
// audience check
object aud;
if (!string.IsNullOrEmpty(audience) && payloadData.TryGetValue("aud", out aud))
{
if (!aud.ToString().Equals(audience, StringComparison.Ordinal))
{
throw new TokenValidationException(string.Format("Audience mismatch. Expected: '{0}' and got: '{1}'", audience, aud));
}
}
// expiration check
object exp;
if (checkExpiration && payloadData.TryGetValue("exp", out exp))
{
DateTime validTo = FromUnixTime(long.Parse(exp.ToString()));
if (DateTime.Compare(validTo, DateTime.UtcNow) <= 0)
{
throw new TokenValidationException(
string.Format("Token is expired. Expiration: '{0}'. Current: '{1}'", validTo, DateTime.UtcNow));
}
}
// issuer check
object iss;
if (payloadData.TryGetValue("iss", out iss))
{
if (!string.IsNullOrEmpty(issuer))
{
if (!iss.ToString().Equals(issuer, StringComparison.Ordinal))
{
throw new TokenValidationException(string.Format("Token issuer mismatch. Expected: '{0}' and got: '{1}'", issuer, iss));
}
}
else
{
// if issuer is not specified, set issuer with jwt[iss]
issuer = iss.ToString();
}
}
return new ClaimsPrincipal(ClaimsIdentityFromJwt(payloadData, issuer));
}
private static ICollection<Claim> ClaimsFromJwt(IDictionary<string, object> jwtData, string issuer)
{
issuer = issuer ?? DefaultIssuer;
var list = jwtData.Where(p => !claimsToExclude.Contains(p.Key)) // don't include specific claims
.Select(p => new { Key = p.Key, Values = p.Value as JArray ?? new JArray { p.Value } }) // p.Value is either claim value of ArrayList of values
.SelectMany(p => p.Values.Cast<object>()
.Select(v => new Claim(p.Key, v.ToString(), StringClaimValueType, issuer, issuer)))
.ToList();
// set claim for user name
// use original jwtData because claimsToExclude filter has sub and otherwise it wouldn't be used
var userNameClaimType = claimTypesForUserName.FirstOrDefault(ct => jwtData.ContainsKey(ct));
if (userNameClaimType != null)
{
list.Add(new Claim(NameClaimType, jwtData[userNameClaimType].ToString(), StringClaimValueType, issuer, issuer));
}
// set claims for roles array
list.Where(c => c.Type == "roles").ToList().ForEach(r =>
{
list.Add(new Claim(RoleClaimType, r.Value, StringClaimValueType, issuer, issuer));
});
list.RemoveAll(c => c.Type == "roles");
return list;
}
private static ClaimsIdentity ClaimsIdentityFromJwt(IDictionary<string, object> jwtData, string issuer)
{
var subject = new ClaimsIdentity("Federation", NameClaimType, RoleClaimType);
var claims = ClaimsFromJwt(jwtData, issuer);
foreach (Claim claim in claims)
{
var type = claim.Type;
if (type == ActorClaimType)
{
if (subject.Actor != null)
{
throw new InvalidOperationException(string.Format(
"Jwt10401: Only a single 'Actor' is supported. Found second claim of type: '{0}', value: '{1}'", new object] { "actor", claim.Value }));
}
subject.AddClaim(new Claim(type, claim.Value, claim.ValueType, issuer, issuer, subject));
continue;
}
subject.AddClaim(new Claim(type, claim.Value, claim.ValueType, issuer, issuer, subject));
}
return subject;
}
private static DateTime FromUnixTime(long unixTime)
{
return unixEpoch.AddSeconds(unixTime);
}
public class TokenValidationException : Exception
{
public TokenValidationException(string message)
: base(message)
{
}
}
}
}
I also see on this line on my JsonWebToken.cs file:
var payloadJson = JWT.JsonWebToken.Decode(token, secret, verify: true);
JWT.JsonWebToken is underlined and it says:
[Deprecated]: JsonWebToken is
obsolete: Static API is obsolete as of
version 2.0 and will be removed in
future versions.
I am unsure whether I need to simply update my nuget package to something.
Appreciate any input. Thanks.