ASP.NET JWT: Signature validation failed. No security keys were provided to validate the signature - asp.net-core

I've been making a web api in F#, mostly following this guide: https://www.blinkingcaret.com/2017/09/06/secure-web-api-in-asp-net-core/. However, I've been getting this error whenever I try to hit an authenticated endpoint in my aspnet webapi:
Failed to validate the token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiciIsImV4cCI6IjE1MjI2MzUwNDMiLCJuYmYiOiIxNTIyNTQ4NjQzIn0.VofLygSMitkmEsTBFNG-7-3jMAZYkyvfwc2UIs7AIyw.
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10500: Signature validation failed. No security keys were provided to validate the signature.
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__6.MoveNext()
I've found similar issues linked here, but none whose resolution helped me. My Startup.fs looks like:
type Startup private () =
new (configuration: IConfiguration) as this =
Startup() then
this.Configuration <- configuration
// This method gets called by the runtime. Use this method to add services to the container.
member this.ConfigureServices(services: IServiceCollection) =
// Add framework services
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(fun options ->
options.TokenValidationParameters = TokenValidationParameters (
ValidateAudience = false,
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = SymmetricSecurityKey(Encoding.UTF8.GetBytes("the secret that needs to be at least 16 characeters long for HmacSha256")),
ValidateLifetime = false, //validate the expiration and not before values in the token
ClockSkew = TimeSpan.FromMinutes(5.0) //5 minute tolerance for the expiration date
) |> ignore
) |> ignore
services.AddMvc() |> ignore
services.AddSwaggerGen (fun c -> c.SwaggerDoc("v1", Swagger.Info())) |> ignore
services.AddCors() |> ignore
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
member this.Configure(app: IApplicationBuilder, env: IHostingEnvironment) =
app.UseExceptionHandler(
fun options ->
options.Run(
fun context ->
let ex = context.Features.Get<IExceptionHandlerFeature>()
match ex.Error with
| HttpCodedException (code, message) ->
printfn "code: %i, msg: %s" (int code) message
context.Response.StatusCode <- int code
context.Response.WriteAsync(message)
| exn -> raise (exn)
)
) |> ignore
// let cors = Action<CorsPolicyBuilder> (fun builder -> builder.WithOrigins("http://localhost:3000").AllowAnyHeader().AllowAnyMethod() |> ignore)
app.UseCors(fun policy ->
policy.AllowAnyHeader()
.AllowAnyOrigin()
.AllowCredentials()
.AllowAnyMethod()
.Build() |> ignore
) |> ignore
app.UseAuthentication() |> ignore
app.UseMvc() |> ignore
member val Configuration : IConfiguration = null with get, set
I've tried turning off basically all of the validation, so I'm confused why this is still failing. If it's helpful, the place where I generate the tokens looks like:
let GenerateToken (username) =
let claims = [|
Claim (ClaimTypes.Name, username)
Claim (JwtRegisteredClaimNames.Exp, DateTimeOffset(DateTime.Now.AddDays(1.0)).ToUnixTimeSeconds().ToString())
Claim (JwtRegisteredClaimNames.Nbf, DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString())
|]
let cred =
new SigningCredentials(
SymmetricSecurityKey(Encoding.UTF8.GetBytes("the secret that needs to be at least 16 characeters long for HmacSha256")),
SecurityAlgorithms.HmacSha256
)
let token = JwtSecurityToken(JwtHeader(cred), JwtPayload(claims))
JwtSecurityTokenHandler().WriteToken(token)
Hoping someone can see what I'm doing wrong.

Finally figured this out. F# doesn't use = for assignment, it uses <-. So needed to change my service AddAuthenticaton call to:
services.AddAuthentication(fun options ->
options.DefaultScheme <- JwtBearerDefaults.AuthenticationScheme
options.DefaultAuthenticateScheme <- JwtBearerDefaults.AuthenticationScheme
options.DefaultChallengeScheme <- JwtBearerDefaults.AuthenticationScheme
).AddJwtBearer(fun options ->
options.TokenValidationParameters <- TokenValidationParameters (
ValidateAudience = false,
ValidateIssuer = false,
ValidateIssuerSigningKey = false,
IssuerSigningKey = SymmetricSecurityKey(Encoding.UTF8.GetBytes("the secret that needs to be at least 16 characeters long for HmacSha256")),
ValidateLifetime = false, //validate the expiration and not before values in the token
ClockSkew = TimeSpan.FromMinutes(5.0), //5 minute tolerance for the expiration date
ValidateActor = false,
ValidateTokenReplay = false
)
) |> ignore
Now everything works fine.

This has been working well for me.
JWT Authentication settings
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("thisKeyIs32CharactersLong1234567"))
ValidateIssuer = true,
ValidIssuer = "MyIssuer",
ValidateAudience = true,
ValidAudience = "MyAudience",
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
});
And then to create the actual token
var handler = new JwtSecurityTokenHandler();
var securityToken = handler.CreateToken(
new SecurityTokenDescriptor
{
Issuer = "MyIssuer",
Audience = "MyAudience",
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes("thisKeyIs32CharactersLong1234567")), SecurityAlgorithms.HmacSha512Signature),
Subject = new ClaimsIdentity(
new[] {
new Claim(ClaimTypes.Name, "My Name"),
new Claim(ClaimTypes.Sid, "My UID"),
new Claim(ClaimTypes.GroupSid, "My GID")
},
Expires = DateTime.Now + TimeSpan.FromMinutes("30")
});
// Save token
handler.WriteToken(securityToken);
Hope it helps.

Related

Looping custom claims on login - Asp.net core

Our ASP.NET MVC application connects to IdentityServer 3 with the following config and able to access all the custom claims
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = IdentityServerUrl,
ClientId = IdentityClientId,
ResponseType = "id_token token",
Scope = "openid profile myScope",
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async n =>
{
var newIdentity = new ClaimsIdentity(
n.AuthenticationTicket.Identity.AuthenticationType,
"name",
"myrole");
var userInfoClient = new UserInfoClient(
new Uri(n.Options.Authority + "/connect/userinfo"),
n.ProtocolMessage.AccessToken);
var userInfo = await userInfoClient.GetAsync();
userInfo.Claims.ToList().ForEach(ui => newIdentity.AddClaim(new Claim(ui.Item1, ui.Item2)));
var sid = n.AuthenticationTicket.Identity.Claims.FirstOrDefault(x => x.Type == "sid");
if (sid != null)
{
newIdentity.AddClaim(new Claim("sid", sid.Value));
}
n.AuthenticationTicket = new AuthenticationTicket(
newIdentity,
n.AuthenticationTicket.Properties);
}
}
});
Now we want to upgrade and connect to IdentityServer 3 with .net core
We tried below code but I am not getting the sure how to loop through all the custom claims
.AddOpenIdConnect("oidc", options =>
{
options.Authority = IdentityClientUrl;
options.ClientId = IdentityClientId;
options.ResponseType = OpenIdConnectResponseType.IdTokenToken;
options.Scope.Clear();
options.Scope.Add("profile");
options.Scope.Add("openid");
options.Scope.Add("email");
options.Scope.Add("myScope");
options.GetClaimsFromUserInfoEndpoint = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "myrole"
};
options.SaveTokens = true;
options.ClaimActions.MapUniqueJsonKey("myrole", "myrole", "string");
});
In the existing approach, i am able to get all the claims from userInfo, so I can loop and add everything.
In the asp.net core - however I can map them using ClaimActions, eachone at a time. Is there any way I can loop throug all of them and add all of them - say I don't know the claimType!
Any help please?
You can try this to map all the claims using the MapAllExcept method, like:
options.ClaimActions.MapAllExcept("iss", "nbf", "exp", "aud", "nonce");

Is it necessary to set ValidateIssuerSigningKey to true when using HMAC256 for verifying JWT Token?

I am using AspNet Core to build a web api and JWT tokens to authenticate users.
I see that in TokenValidationParameters the default value of ValidateIssuerSigningKey property is false.
Does it make any difference if we set it to true, when using the HMAC256 Symmetric key to sign and verify tokens (where there is no public-key added to the token like in case of RSA)?
services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
string jwtIssuer = configuration["JwtIssuer"];
SymmetricSecurityKey securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JwtKey"]));
cfg.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = jwtIssuer,
ValidAudience = jwtIssuer,
ValidateIssuerSigningKey = true,
IssuerSigningKey = securityKey,
ClockSkew = TimeSpan.Zero
};
});
Or is it necessary to set ValidateIssuerSigningKey to true only when using RSA keys?
Here is the code level documentation of this property:
//
// Summary:
// Gets or sets a boolean that controls if validation of the Microsoft.IdentityModel.Tokens.SecurityKey
// that signed the securityToken is called.
//
// Remarks:
// It is possible for tokens to contain the public key needed to check the signature.
// For example, X509Data can be hydrated into an X509Certificate, which can be used
// to validate the signature. In these cases it is important to validate the SigningKey
// that was used to validate the signature.
[DefaultValue(false)]
public bool ValidateIssuerSigningKey { get; set; }
Based on looking at the Microsoft.IdentityModel.Tokens source code, I could find only one place where the ValidateIssuerSigningKey boolean property is used, here:
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/src/Microsoft.IdentityModel.Tokens/Validators.cs
Which ultimately causes this code block to be executed:
X509SecurityKey x509SecurityKey = securityKey as X509SecurityKey;
if (x509SecurityKey?.Certificate is X509Certificate2 cert)
{
DateTime utcNow = DateTime.UtcNow;
var notBeforeUtc = cert.NotBefore.ToUniversalTime();
var notAfterUtc = cert.NotAfter.ToUniversalTime();
if (notBeforeUtc > DateTimeUtil.Add(utcNow, validationParameters.ClockSkew))
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10248, notBeforeUtc, utcNow)));
LogHelper.LogInformation(LogMessages.IDX10250, notBeforeUtc, utcNow);
if (notAfterUtc < DateTimeUtil.Add(utcNow, validationParameters.ClockSkew.Negate()))
throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSigningKeyException(LogHelper.FormatInvariant(LogMessages.IDX10249, notAfterUtc, utcNow)));
LogHelper.LogInformation(LogMessages.IDX10251, notAfterUtc, utcNow);
}
I.e. that flag relates to X509 certificates only, and the testing of the time period they are valid for. So I suspect it does not affect tokens validated using HMAC256... unless the HMAC key was obtained from an X509 certificate!
The Azure Active Directory people clarify this on this GitHub Wiki page created in December 2020:
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/wiki/Use-of-TokenValidationParameters.ValidateIssuerSigningKey
I'll quote key parts for convenience:
Normally this [ValidateIssuerSigningKey] is not required because the user / runtime must set IssuerSigningKey or IssuerSigningKeys or in the case of custom security key retrieval the delegate IssuerSigningKeyResolver ( Definition ) for keys to be available for validating the signature on the token. ... It is assumed that only keys from trusted sources are set.
If you need custom validation of the security key that signed the token you can ... set TokenValidationParameters.ValidateIssuerSigningKey to true. ...
The default behavior is applicable to X509SecurityKey and checks that the certificate is not expired. No CRL or other checks are made.

IdentityServer4 client - Refreshing access tokens on CookieAuthenticationEvents

I am trying to use refresh token when the access token expires. A similar so question is answered here. And a sample code to renew token by an action
And i end up with the following code in the startup.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies",
//ExpireTimeSpan = TimeSpan.FromSeconds(100),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Events = new CookieAuthenticationEvents()
{
OnValidatePrincipal = async x =>
{
if (x.Properties?.Items[".Token.expires_at"] == null) return;
var logger = loggerFactory.CreateLogger(this.GetType());
var now = DateTimeOffset.UtcNow;
var tokenExpireTime = DateTime.Parse(x.Properties.Items[".Token.expires_at"]).ToUniversalTime();
var timeElapsed = now.Subtract(x.Properties.IssuedUtc.Value);
var timeRemaining = tokenExpireTime.Subtract(now.DateTime);
if (timeElapsed > timeRemaining)
{
var httpContextAuthentication = x.HttpContext.Authentication;//Donot use the HttpContext.Authentication to retrieve anything, this cause recursive call to this event
var oldAccessToken = await httpContextAuthentication.GetTokenAsync("access_token");
var oldRefreshToken = await httpContextAuthentication.GetTokenAsync("refresh_token");
logger.LogInformation($"Refresh token :{oldRefreshToken}, old access token:{oldAccessToken}");
var disco = await DiscoveryClient.GetAsync(AuthorityServer);
if (disco.IsError) throw new Exception(disco.Error);
var tokenClient = new TokenClient(disco.TokenEndpoint, ApplicationId, "secret");
var tokenResult = await tokenClient.RequestRefreshTokenAsync(oldRefreshToken);
logger.LogInformation("Refresh token requested. " + tokenResult.ErrorDescription);
if (!tokenResult.IsError)
{
var oldIdToken = await httpContextAuthentication.GetTokenAsync("id_token");
var newAccessToken = tokenResult.AccessToken;
var newRefreshToken = tokenResult.RefreshToken;
var tokens = new List<AuthenticationToken>
{
new AuthenticationToken {Name = OpenIdConnectParameterNames.IdToken, Value = oldIdToken},
new AuthenticationToken {Name = OpenIdConnectParameterNames.AccessToken, Value = newAccessToken},
new AuthenticationToken {Name = OpenIdConnectParameterNames.RefreshToken, Value = newRefreshToken}
};
var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn);
tokens.Add(new AuthenticationToken { Name = "expires_at", Value = expiresAt.ToString("o", CultureInfo.InvariantCulture) });
var info = await httpContextAuthentication.GetAuthenticateInfoAsync("Cookies");
info.Properties.StoreTokens(tokens);
await httpContextAuthentication.SignInAsync("Cookies", info.Principal, info.Properties);
}
x.ShouldRenew = true;
}
else
{
logger.LogInformation("Not expired");
}
}
}
});
The client setup is as follows
AllowAccessTokensViaBrowser = true,
RefreshTokenUsage = TokenUsage.ReUse,
RefreshTokenExpiration = TokenExpiration.Sliding,
AbsoluteRefreshTokenLifetime = 86400,
AccessTokenLifetime = 10,
AllowOfflineAccess = true,
AccessTokenType = AccessTokenType.Reference
After successfully login, i am getting a 401 for every other request. And the log says
[Identity Server]2017-07-04 10:15:58.819 +01:00 [Debug]
"TjpIkvHQi../cfivu6Nql5ADJJlZRuoJV1QI=" found in database: True
[Identity Server]2017-07-04 10:15:58.820 +01:00 [Debug]
"reference_token" grant with value:
"..9e64c1235c6675fcef617914911846fecd72f7b372" found in store, but has
expired.
[Identity Server]2017-07-04 10:15:58.821 +01:00 [Error] Invalid
reference token. "{ \"ValidateLifetime\": true,
\"AccessTokenType\": \"Reference\", \"TokenHandle\":
\"..9e64c1235c6675fcef617914911846fecd72f7b372\" }"
[Identity Server]2017-07-04 10:15:58.822 +01:00 [Debug] Token is
invalid.
[Identity Server]2017-07-04 10:15:58.822 +01:00 [Debug] Creating
introspection response for inactive token.
[Identity Server]2017-07-04 10:15:58.822 +01:00 [Information] Success
token introspection. Token status: "inactive", for API name: "api1"
Any help would by highly appreciated
UPDATE:
Basically, when the token expires i get a System.StackOverflowException on the following line
var tokenExpireTime = DateTime.Parse(x.Properties.Items[".Token.expires_at"]).ToUniversalTime();
UPDATE 2:
Do not use HttpContext.Authentication to retrieve anything. Check my answer below to find the working implementaion
I was working on this for last two days and could not make this work. Funnily, after posting the question here, within 2 hours I make it working :)
Events = new CookieAuthenticationEvents()
{
OnValidatePrincipal = async x =>
{
if (x.Properties?.Items[".Token.expires_at"] == null) return;
var now = DateTimeOffset.UtcNow;
var tokenExpireTime = DateTime.Parse(x.Properties.Items[".Token.expires_at"]).ToUniversalTime();
var timeElapsed = now.Subtract(x.Properties.IssuedUtc.Value);
var timeRemaining = tokenExpireTime.Subtract(now.DateTime);
WriteMessage($"{timeRemaining} and elapsed at {timeElapsed}");
if (timeElapsed > timeRemaining)
{
var oldAccessToken = x.Properties.Items[".Token.access_token"];
var oldRefreshToken = x.Properties.Items[".Token.refresh_token"];
WriteMessage($"Refresh token :{oldRefreshToken}, old access token {oldAccessToken}");
var disco = await DiscoveryClient.GetAsync(AuthorityServer);
if (disco.IsError) throw new Exception(disco.Error);
var tokenClient = new TokenClient(disco.TokenEndpoint, ApplicationId, "secret");
var tokenResult = await tokenClient.RequestRefreshTokenAsync(oldRefreshToken);
if (!tokenResult.IsError)
{
var oldIdToken = x.Properties.Items[".Token.id_token"];//tokenResult.IdentityToken
var newAccessToken = tokenResult.AccessToken;
var newRefreshToken = tokenResult.RefreshToken;
var tokens = new List<AuthenticationToken>
{
new AuthenticationToken {Name = OpenIdConnectParameterNames.IdToken, Value = oldIdToken},
new AuthenticationToken {Name = OpenIdConnectParameterNames.AccessToken, Value = newAccessToken},
new AuthenticationToken {Name = OpenIdConnectParameterNames.RefreshToken, Value = newRefreshToken}
};
var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn);
tokens.Add(new AuthenticationToken { Name = "expires_at", Value = expiresAt.ToString("o", CultureInfo.InvariantCulture) });
x.Properties.StoreTokens(tokens);
WriteMessage($"oldAccessToken: {oldAccessToken}{Environment.NewLine} and new access token {newAccessToken}");
}
x.ShouldRenew = true;
}
}
}
Basically httpContextAuthentication.GetTokenAsync make this recursive, for that reason StackOverflowException occured.
Please let me know if this implementation has any issue

Update claims after login with identityserver3 2.1.1

We need to update users claims after they log in to our website. This is caused by changes in the users licenses done by another part of our system.
However I am not able to comprehend how to update the claims without logout/login.
Rigth now this is our client setup
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
//user validation host
Authority = UrlConstants.BaseAddress,
//Client that the user is validating against
ClientId = guid,//if not convertet to Gui the compare from the server fails
RedirectUri = UrlConstants.RedirectUrl,
PostLogoutRedirectUri = UrlConstants.RedirectUrl,
ResponseType = "code id_token token",
Scope = "openid profile email roles licens umbraco_api umbracoaccess",
UseTokenLifetime = false,
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async n =>
{
_logger.Info("ConfigureAuth", "Token valdidated");
var id = n.AuthenticationTicket.Identity;
var nid = new ClaimsIdentity(
id.AuthenticationType,
Constants.ClaimTypes.GivenName,
Constants.ClaimTypes.Role);
// get userinfo data
var uri = new Uri(n.Options.Authority + "/connect/userinfo");
var userInfoClient = new UserInfoClient(uri,n.ProtocolMessage.AccessToken);
var userInfo = await userInfoClient.GetAsync();
userInfo.Claims.ToList().ForEach(ui => nid.AddClaim(new Claim(ui.Item1, ui.Item2)));
var licens = id.FindAll(LicenseScope.Licens);
nid.AddClaims(licens);
// keep the id_token for logout
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
n.AuthenticationTicket = new AuthenticationTicket(
nid,
n.AuthenticationTicket.Properties);
_logger.Info("ConfigureAuth", "AuthenticationTicket created");
},
RedirectToIdentityProvider = async n =>
{
// if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value;
_logger.Debug("ConfigureAuth", "id_token for logout set on request");
_logger.Debug("ConfigureAuth", "Old PostLogoutRedirectUri: {0}", n.ProtocolMessage.PostLogoutRedirectUri.ToString());
n.ProtocolMessage.IdTokenHint = idTokenHint;
var urlReferrer = HttpContext.Current.Request.UrlReferrer.ToString();
if (!urlReferrer.Contains("localhost"))
{
n.ProtocolMessage.PostLogoutRedirectUri = GetRedirectUrl();
}
else
{
n.ProtocolMessage.PostLogoutRedirectUri = urlReferrer;
}
_logger.Debug("ConfigureAuth", string.Format("Setting PostLogoutRedirectUri to: {0}", n.ProtocolMessage.PostLogoutRedirectUri.ToString()));
}
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.AuthenticationRequest)
{
n.ProtocolMessage.RedirectUri = GetRedirectUrl2();
n.ProtocolMessage.AcrValues = GetCurrentUmbracoId();
_logger.Debug("ConfigureAuth", string.Format("Setting RedirectUri to: {0}", n.ProtocolMessage.RedirectUri.ToString()));
}
},
}
});
We get our custom claims in SecurityTokenValidated
var licens = id.FindAll(LicenseScope.Licens);
nid.AddClaims(licens);
I do not follow how to get this without doing a login? Any help is highly appreciated.
That's a reminder that you should not put claims into tokens that might change during the lifetime of the session.
That said - you can set a new cookie at any point in time.
Reach into the OWIN authentication manager and call the SignIn method. Pass the claims identity that you want to serialize into the cookie.
e.g.
Request.GetOwinContext().Authentication.SignIn(newIdentity);

When is JWTSecurityTokenHandler.ValidateToken() actually valid?

I am attempting to create a token validation method that returns true if a JWT token is valid based on the signature. I don't think I really need to validate everything in the token but what actually signifies a token is valid after calling ValidateToken()? The existence of a principle? The out referenced token contains certain values? Not sure when to return true from this method.
public bool ValidateToken(string tokenString)
{
var validationParameters = new TokenValidationParameters()
{
ValidIssuer = "My Company",
ValidAudience = ApplicationId,
IssuerSigningKey = JsonWebTokenSecretKey
};
SecurityToken token = new JwtSecurityToken();
var tokenHandler = new JwtSecurityTokenHandler();
var principal = tokenHandler.ValidateToken(tokenString, validationParameters, out token);
return principal != null;
}
I check all of the claims values manually. I've been searching for a definitive answer to your same question but the only thing I have seen is that the ValidateToken function will throw an Exception if something is wrong, so I begin by wrapping the call in a try-catch and return false from the catch.
That's just my "first-pass" at validating the token, though. Afterwards I do a little more heavy lifting to check certain values manually. For example, I make sure that the unique_name value in the claims section actually exists as a user in my database, that the user has not been deactivated, and other proprietary system stuff like that.
public static bool VerifyToken(string token)
{
var validationParameters = new TokenValidationParameters()
{
IssuerSigningToken = new BinarySecretSecurityToken(_key),
ValidAudience = _audience,
ValidIssuer = _issuer,
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken validatedToken = null;
try
{
tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
}
catch(SecurityTokenException)
{
return false;
}
catch(Exception e)
{
log(e.ToString()); //something else happened
throw;
}
//... manual validations return false if anything untoward is discovered
return validatedToken != null;
}
The last line, return validatedToken != null, is purely superstition on my part. I've never seen the validatedToken be null.