I don't understand how this library works. Could you help me please ?
Here is my simple code :
public void TestJwtSecurityTokenHandler()
{
var stream =
"eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJJU1MiLCJzY29wZSI6Imh0dHBzOi8vbGFyaW0uZG5zY2UuZG91YW5lL2NpZWxzZXJ2aWNlL3dzIiwiYXVkIjoiaHR0cHM6Ly9kb3VhbmUuZmluYW5jZXMuZ291di5mci9vYXV0aDIvdjEiLCJpYXQiOiJcL0RhdGUoMTQ2ODM2MjU5Mzc4NClcLyJ9";
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(stream);
}
This is the error :
The string needs to be in compact JSON format, which is of the form: Base64UrlEncodedHeader.Base64UrlEndcodedPayload.OPTIONAL,Base64UrlEncodedSignature'.
If you copy the stream in jwt.io website, it works fine :)
I found the solution, I just forgot to Cast the result:
var stream = "[encoded jwt]";
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(stream);
var tokenS = jsonToken as JwtSecurityToken;
Or, without the cast:
var token = "[encoded jwt]";
var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.ReadJwtToken(token);
I can get Claims using:
var jti = tokenS.Claims.First(claim => claim.Type == "jti").Value;
new JwtSecurityTokenHandler().ReadToken("") will return a SecurityToken
new JwtSecurityTokenHandler().ReadJwtToken("") will return a JwtSecurityToken
If you just change the method you are using you can avoid the cast in the above answer
You need the secret string which was used to generate encrypt token.
This code works for me:
protected string GetName(string token)
{
string secret = "this is a string used for encrypt and decrypt token";
var key = Encoding.ASCII.GetBytes(secret);
var handler = new JwtSecurityTokenHandler();
var validations = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
var claims = handler.ValidateToken(token, validations, out var tokenSecure);
return claims.Identity.Name;
}
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Email, model.UserName),
new Claim(JwtRegisteredClaimNames.NameId, model.Id.ToString()),
};
var token = new JwtSecurityToken(_config["Jwt:Issuer"],
_config["Jwt:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
Then extract content
var handler = new JwtSecurityTokenHandler();
string authHeader = Request.Headers["Authorization"];
authHeader = authHeader.Replace("Bearer ", "");
var jsonToken = handler.ReadToken(authHeader);
var tokenS = handler.ReadToken(authHeader) as JwtSecurityToken;
var id = tokenS.Claims.First(claim => claim.Type == "nameid").Value;
Using .net core jwt packages, the Claims are available:
[Route("api/[controller]")]
[ApiController]
[Authorize(Policy = "Bearer")]
public class AbstractController: ControllerBase
{
protected string UserId()
{
var principal = HttpContext.User;
if (principal?.Claims != null)
{
foreach (var claim in principal.Claims)
{
log.Debug($"CLAIM TYPE: {claim.Type}; CLAIM VALUE: {claim.Value}");
}
}
return principal?.Claims?.SingleOrDefault(p => p.Type == "username")?.Value;
}
}
I write this solution and it's work for me
protected Dictionary<string, string> GetTokenInfo(string token)
{
var TokenInfo = new Dictionary<string, string>();
var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.ReadJwtToken(token);
var claims = jwtSecurityToken.Claims.ToList();
foreach (var claim in claims)
{
TokenInfo.Add(claim.Type, claim.Value);
}
return TokenInfo;
}
Extending on cooxkie answer, and dpix answer, when you are reading a jwt token (such as an access_token received from AD FS), you can merge the claims in the jwt token with the claims from "context.AuthenticationTicket.Identity" that might not have the same set of claims as the jwt token.
To Illustrate, in an Authentication Code flow using OpenID Connect,after a user is authenticated, you can handle the event SecurityTokenValidated which provides you with an authentication context, then you can use it to read the access_token as a jwt token, then you can "merge" tokens that are in the access_token with the standard list of claims received as part of the user identity:
private Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage,OpenIdConnectAuthenticationOptions> context)
{
//get the current user identity
ClaimsIdentity claimsIdentity = (ClaimsIdentity)context.AuthenticationTicket.Identity;
/*read access token from the current context*/
string access_token = context.ProtocolMessage.AccessToken;
JwtSecurityTokenHandler hand = new JwtSecurityTokenHandler();
//read the token as recommended by Coxkie and dpix
var tokenS = hand.ReadJwtToken(access_token);
//here, you read the claims from the access token which might have
//additional claims needed by your application
foreach (var claim in tokenS.Claims)
{
if (!claimsIdentity.HasClaim(claim.Type, claim.Value))
claimsIdentity.AddClaim(claim);
}
return Task.FromResult(0);
}
Use this:
public static string Get_Payload_JWTToken(string token)
{
var handler = new JwtSecurityTokenHandler();
var DecodedJWT = handler.ReadJwtToken(token);
string payload = DecodedJWT.EncodedPayload; // Gives Payload
return Encoding.UTF8.GetString(FromBase64Url(payload));
}
static byte[] FromBase64Url(string base64Url)
{
string padded = base64Url.Length % 4 == 0
? base64Url : base64Url + "====".Substring(base64Url.Length % 4);
string base64 = padded.Replace("_", "/").Replace("-", "+");
return Convert.FromBase64String(base64);
}
Though this answer is not answering the original question but its a really very useful feature for C# developers, so adding it as the answer.
Visual Studio 2022 has added a feature to decode the value of a token at runtime.
You can check the feature in Visual Studio 2022 preview (version 17.5.0 preview 2.0)
Mouse over the variable containing the JWT and then select the string manipulation as JWT Decode, and you can see the token value.
Related
EDIT: Issue solved.I am able to validate jwts signature against the public key of the x5c cert chain. Thank you, Topaco, your comment lead me to check my data once again. Solution on the bottom.
Im new on this topic, but Im trying to implement a jwt signature verification and dont want to use bouncy castle. The SigningCert(Chain) lies in the JWT's 'x5c' header. jwt-alg header says "bp256r1".
My problem is Im getting following ArgumentException as Validation Result:
IDX10645: Elliptical Curve not supported for curveId: 'brainpoolP256r1'
To be fair: That message doesnt leave much room for interpretation. But I thought, that with Win10 and Ongoing, BP256R1 algorithm is supported?.
What am i Missing?
thats my Code, I'd wish validation result is true, but it inst:
public async Task<JwtSecurityToken> GetDiscoveryDocument()
{
//Get Token
var response = await _client.GetAsync(".well-known/openid-configuration");
response.EnsureSuccessStatusCode();
//parse Token
string token = await response.Content.ReadAsStringAsync();
var jwtHandler = new JwtSecurityTokenHandler();
var jwt = jwtHandler.ReadJwtToken(token);
//Get SigningCert
var x5cCerts = JsonSerializer.Deserialize<object[]>(jwt.Header["x5c"].ToString());
X509Certificate2 cert = new X509Certificate2(Encoding.UTF8.GetBytes(x5cCerts.First().ToString()));
//Get PublicKey and Set validationParameters, ValidateSignature
var ecdsa = cert.GetECDsaPublicKey();
var vaidationResult = await jwtHandler.ValidateTokenAsync(token, new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new ECDsaSecurityKey(ecdsa),
});
return jwt;
}
I also tried to use:
var ecdsa = cert.GetECDsaPublicKey();
byte[] data = ....;
byte[] signature = ....;
ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256);
Solution:
public async Task<JwtSecurityToken> GetDiscoveryDocument()
{
//Get Token
var response = await _client.GetAsync(".well-known/openid-configuration");
response.EnsureSuccessStatusCode();
//parse Token
string token = await response.Content.ReadAsStringAsync();
var jwtHandler = new JwtSecurityTokenHandler();
var jwt = jwtHandler.ReadJwtToken(token);
//Get SigningCert
var x5cCerts = JsonSerializer.Deserialize<object[]>(jwt.Header["x5c"].ToString());
X509Certificate2 cert = new X509Certificate2(Encoding.UTF8.GetBytes(x5cCerts.First().ToString()));
//Get PublicKey and Set validationParameters, ValidateSignature
var ecdsa = cert.GetECDsaPublicKey();
var valid = ecdsa.VerifyData(Encoding.UTF8.GetBytes(jwt.EncodedHeader + "." + jwt.EncodedPayload), Base64UrlEncoder.DecodeBytes(jwt.RawSignature), HashAlgorithmName.SHA256);
return jwt;
}
I've a code using JWT Bearer to authenticate some tokens as below.
private string GetToken()
{
var issuer = _factory.Configuration.GetValue<string>("JwtSettings:Issuer");
DateTime expiryDate = DateTime.UtcNow.Add(TimeSpan.FromMinutes(1));
var jwtSecurityToken = new JwtSecurityToken(
issuer: issuer,
claims: new List<Claim>()
{
new Claim("a claim", "a claim value")
},
expires: expiryDate,
signingCredentials: new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_factory.Configuration.GetValue<string>
("JwtSettings:Secret"))),
SecurityAlgorithms.RsaSha256Signature
)
);
return new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
}
private HttpClient GetClient()
{
var client = _factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
});
var token = GetToken();
client.DefaultRequestHeaders.Add(HttpRequestHeader.Authorization.ToString(), $"Bearer {token}");
return client;
}
But it doesn't work and I don't have any idea cuz I'm new to C-sharp and .Net
It gives me the error of couldn't create the signature
Please Help.
In your code, you have specified SecurityAlgorithms.RsaSha256Signature which is asymmetric key algorithm which requires two keys (private and public key)
For code to work, change SecurityAlgorithms.RsaSha256Signature to SecurityAlgorithms.HmacSha256Signature
A penetration test has recommended that we change our JWT implementation to use asymmetric signing instead of symmetric signing, which is working well.
The current (perfectly working) code to Create the symmetric token is below: (inspiration originally taken from How to encrypt JWT security token?)
private string CreateToken(string Username)
{
//Set issued at date
DateTime issuedAt = DateTime.UtcNow;
//set the time when it expires
DateTime expires = DateTime.UtcNow.AddHours(1);
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
//create a identity and add claims to the user which we want to log in
ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, Username)
});
DateTime now = DateTime.UtcNow;
SymmetricSecurityKey securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(SecurityConstants.ConstSecurityEncryptionKey));
SigningCredentials signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
//create the jwt
JwtSecurityToken token = tokenHandler.CreateJwtSecurityToken(issuer: "issuer",
audience: "audience",
subject: claimsIdentity,
notBefore: issuedAt,
expires: expires,
signingCredentials: signingCredentials);
return tokenHandler.WriteToken(token);
}
}
The code to check the request of any API calls looks similar to the below:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpStatusCode StatusCode;
string token;
//determine whether a jwt exists or not
if (!TryRetrieveToken(request, out token))
{
StatusCode = HttpStatusCode.Unauthorized;
//allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
return base.SendAsync(request, cancellationToken);
}
try
{
DateTime now = DateTime.UtcNow;
SymmetricSecurityKey SecurityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(SecurityConstants.ConstSecurityEncryptionKey));
SecurityToken securityToken;
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
TokenValidationParameters validationParameters = new TokenValidationParameters()
{
ValidAudience = "audience",
ValidIssuer = "issuer",
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
LifetimeValidator = this.LifetimeValidator,
IssuerSigningKey = SecurityKey
};
//extract and assign the user of the jwt
Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);
return base.SendAsync(request, cancellationToken);
}
catch (SecurityTokenValidationException e)
{
StatusCode = HttpStatusCode.Forbidden;
}
catch (Exception ex)
{
StatusCode = HttpStatusCode.Forbidden;
}
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(StatusCode) { });
}
I've tried quite a few examples online and read quite a few posts, but haven't gotten anything working as yet, each with a whole host of different issues. Probably the closed I've got is following some of the examples at: RS256 vs HS256: What's the difference?
One example that seemed to work in creating the code is below, but this wouldn't have worked when comparing the token with incoming API calls.
RSA _rsaa;
_rsaa = new RSACryptoServiceProvider(2048);
var r = _rsaa.ExportParameters(true);
SigningCredentials signingCredentials3a = new SigningCredentials(new RsaSecurityKey(_rsaa), SecurityAlgorithms.RsaSha256Signature);
Finally the option below felt right, but was getting a 'Bad Version of provider.' error due to the encryption key contents held in the string.
byte[] Key256Bytes = Encoding.ASCII.GetBytes(Key256);
rsa.ImportCspBlob(Key256Bytes);
SigningCredentials signingCredentials5 = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256Signature);
I'm having difficulty getting the various options working.
Found the solution, to convert the above into Assymmetric encryption, swap:
SymmetricSecurityKey securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(SecurityConstants.ConstSecurityEncryptionKey));
SigningCredentials signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
with
var rsa = new RSACryptoServiceProvider();
rsa.ImportCspBlob(Convert.FromBase64String(SecurityConstants.ConstAsyncSecurityEncryptionKey));
SigningCredentials signingCredentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha512Signature);
The encryption key was generated by using the below.
var rsa = new RSACryptoServiceProvider(2048);
var key = Convert.ToBase64String(rsa.ExportCspBlob(true));
Then when checking the incoming JWT:
SymmetricSecurityKey SecurityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(SecurityConstants.ConstSecurityEncryptionKey));
With
var rsa = new RSACryptoServiceProvider();
rsa.ImportCspBlob(Convert.FromBase64String(SecurityConstants.ConstAsyncSecurityEncryptionKey));
RsaSecurityKey SecurityKey = new RsaSecurityKey(rsa);
Hope that helps someone in the future.
The following code gives me Azure AD security token, I need to validate that token is valid or not. How to achieve this?
// Get OAuth token using client credentials
string tenantName = "mytest.onmicrosoft.com";
string authString = "https://login.microsoftonline.com/" + tenantName;
AuthenticationContext authenticationContext = new AuthenticationContext(authString, false);
// Config for OAuth client credentials
string clientId = "fffff33-6666-4888-a4tt-fbttt44444";
string key = "123v47o=";
ClientCredential clientCred = new ClientCredential(clientId, key);
string resource = "http://mytest.westus.cloudapp.azure.com";
string token;
Task<AuthenticationResult> authenticationResult = authenticationContext.AcquireTokenAsync(resource, clientCred);
token = authenticationResult.Result.AccessToken;
Console.WriteLine(token);
// How can I validate this token inside my service?
There are two steps to verify the token. First, verify the signature of the token to ensure the token was issued by Azure Active Directory. Second, verify the claims in the token based on the business logic.
For example, we need to verify the iss and aud claim if you were developing a single tenant app. And you also need to verify the nbf to ensure the token is not expired. For more claims you can refer here.
Below description is from here about the detail of signature verifying. (Note: The example below uses the Azure AD v2 endpoint. You should use the endpoint that corresponds to the endpoint the client app is using.)
The access token from the Azure AD is a JSON Web Token(JWT) which is signed by Security Token Service in private key.
The JWT includes 3 parts: header, data, and signature. Technically, we can use the public key to validate the access token.
First step – retrieve and cache the signing tokens (public key)
Endpoint: https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
Then we can use the JwtSecurityTokenHandler to verify the token using the sample code below:
public JwtSecurityToken Validate(string token)
{
string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint);
OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningTokens = config.SigningTokens,
ValidateLifetime = false
};
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
SecurityToken jwt;
var result = tokenHandler.ValidateToken(token, validationParameters, out jwt);
return jwt as JwtSecurityToken;
}
And if you were using the OWIN components in your project, it is more easy to verify the token. We can use the code below to verify the token:
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
});
Then we can use the code below to verify the ‘scope’ in the token:
public IEnumerable<TodoItem> Get()
{
// user_impersonation is the default permission exposed by applications in AAD
if (ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value != "user_impersonation")
{
throw new HttpResponseException(new HttpResponseMessage {
StatusCode = HttpStatusCode.Unauthorized,
ReasonPhrase = "The Scope claim does not contain 'user_impersonation' or scope claim not found"
});
}
...
}
And here is a code sample which protected the web API with Azure AD:
Protect a Web API using Bearer tokens from Azure AD
Just wanted to add to Fei's answer for people using .net Core 2.0
You'll have to modify 2 lines of the Validate(string token) method.
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
stsDiscoveryEndpoint,
new OpenIdConnectConfigurationRetriever()); //1. need the 'new OpenIdConnect...'
OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;
TokenValidationParameters validationParameters = new TokenValidationParameters
{
//decode the JWT to see what these values should be
ValidAudience = "some audience",
ValidIssuer = "some issuer",
ValidateAudience = true,
ValidateIssuer = true,
IssuerSigningKeys = config.SigningKeys, //2. .NET Core equivalent is "IssuerSigningKeys" and "SigningKeys"
ValidateLifetime = true
};
But if you are not using OWIN in your projects, it is going to be a little hard or at least time consuming..
This articleHere is great resource.
And because I do not have much to add on the above, except the detailed code.. Here is something that can be useful to you:
public async Task<ClaimsPrincipal> CreatePrincipleAsync()
{
AzureActiveDirectoryToken azureToken = Token.FromJsonString<AzureActiveDirectoryToken>();
var allParts = azureToken.IdToken.Split(".");
var header = allParts[0];
var payload = allParts[1];
var idToken = payload.ToBytesFromBase64URLString().ToAscii().FromJsonString<AzureActiveDirectoryIdToken>();
allParts = azureToken.AccessToken.Split(".");
header = allParts[0];
payload = allParts[1];
var signature = allParts[2];
var accessToken = payload.ToBytesFromBase64URLString().ToAscii().FromJsonString<AzureActiveDirectoryAccessToken>();
var accessTokenHeader = header.ToBytesFromBase64URLString().ToAscii().FromJsonString<AzureTokenHeader>();
var isValid = await ValidateToken(accessTokenHeader.kid, header, payload, signature);
if (!isValid)
{
throw new SecurityException("Token can not be validated");
}
var principal = await CreatePrincipalAsync(accessToken, idToken);
return principal;
}
private async Task<bool> ValidateToken(string kid, string header, string payload, string signature)
{
string keysAsString = null;
const string microsoftKeysUrl = "https://login.microsoftonline.com/common/discovery/keys";
using (var client = new HttpClient())
{
keysAsString = await client.GetStringAsync(microsoftKeysUrl);
}
var azureKeys = keysAsString.FromJsonString<MicrosoftConfigurationKeys>();
var signatureKeyIdentifier = azureKeys.Keys.FirstOrDefault(key => key.kid.Equals(kid));
if (signatureKeyIdentifier.IsNotNull())
{
var signatureKey = signatureKeyIdentifier.x5c.First();
var certificate = new X509Certificate2(signatureKey.ToBytesFromBase64URLString());
var rsa = certificate.GetRSAPublicKey();
var data = string.Format("{0}.{1}", header, payload).ToBytes();
var isValidSignature = rsa.VerifyData(data, signature.ToBytesFromBase64URLString(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return isValidSignature;
}
return false;
}
There are some functions that I use in here that are not available for you, they are self descriptive.
I'm implmenting my own oauth authtentication system and want to use JWT tokens.
The ms implementation is a little confusing. I see the following hashing algorithum all over the net
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
but can't seem to find out how I can then extract the user info out of the token
what you are showing is a JWT protect implementation that does not come out of the box. This is for serializing & encrypting tokens not the oposite. If you have already managed that part (that's the difficult one) then reading the user claims out of a JWT token should be easy. This is usually done by the Microsoft.Owin.Security.Jwt middleware
var issuer = "http://myidentityserverurl.com"
var audience = ConfigurationManager.AppSettings["as:AudienceId"];
var symmetricKey = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["as:AudienceSecret"]);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = "JWT",
AllowedAudiences = new string[] { audience } ,
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, symmetricKey)
}
});
Check out this github repo and the accompanying article here for a complete example end to end.