I generate a jwt token and after I try to Read it again, i get followed error message:
IDX12709: CanReadToken() returned false. JWT is not well formed: '[PII of type 'System.String' is hidden
GenerateToken:
private string GenerateJwtToken(string username)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes("$x3H*aG*?yKfh]Z/");
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] { new Claim("username", username) }),
Expires = DateTime.UtcNow.AddMinutes(30),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
and the Fetch Header:
headers: {
'Content-type': 'application/json',
'Authorization': `Bearer ${sessionStorage.getItem("token")}`,
},
Here I read the token: "(backslash)"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImRldl9lZCIsIm5iZiI6MTY0MzQwMTc2OSwiZXhwIjoxNjQzNDAzNTY5LCJpYXQiOjE2NDM0MDE3Njl9.EjE9Va6v7XwQka4UH0y_2dC1eqpfUWAGs2Ipoq9LoGE(backslash)""
public async Task Invoke(HttpContext context, IAuthService authService)
{
string token = string token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
//...
private void attachUserToContext(HttpContext context, IAuthService authService, string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes("$x3H*aG*?yKfh]Z/");
tokenHandler.ValidateToken(token, new TokenValidationParameters //<- IDX12709
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
}, out SecurityToken validatedToken);
var jwtToken = (JwtSecurityToken)validatedToken;
var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "username").Value);
context.Items["User"] = userId;
}
catch
{
// do nothing if jwt validation fails
// user is not attached to context so request won't have access to secure routes
}
}
like #jps said, it was because of the quotation mark.
new fetch call:
'Authorization': 'Bearer ' + token.replace(/"/g, ""),
Related
After login, I have jwt token and can login. But when I called other api in fetch function with jwt token, my api call got unauthorized. I cannot call api.
NET 6 Api
Program.cs
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = builder.Configuration["JWT:ValidAudience"],
ValidIssuer = builder.Configuration["JWT:ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:SecretKey"]))
};
});
LoginController.cs
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(_configuration["JWT:SecretKey"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(SessionConstant.UserName, user.UserName!),
new Claim(ClaimTypes.Role, userRole)
}),
Issuer = _configuration["JWT:ValidIssuer"],
Audience = _configuration["JWT:ValidAudience"],
Expires = DateTime.Now.AddMinutes(20),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
UserController.cs
[Authorize(Roles = RoleConstant.Developer)]
[HttpGet("GetUsers")]
public async Task<JsonResult> GetUsers(User user)
Sveltekit
index.svelte
// get = fetch with 'get' method.
let result = await get(`api/User/GetUsers?id=${id}`, accesstoken);
utils.ts
export async function get(endpoint, token) {
let headers = {};
if (token)
{
headers = {
'Authorization': `JWT ${token}`
};
}
return await fetch(`${baseApiUrl}/${endpoint}`, {
method: 'GET',
headers
}).then(r => r.json())
.then(x => x);
}
I have jwt token. Inside the jwt token, it shows my role is developer which matches with NET Api's [Authorize(Roles = RoleConstant.Developer)]. But I still cannot call GetUsers and it returns 'Unauthorization'. I tried 'Bearer ${token}' in 'Authorization' header too and it didn't work.
Anything I miss in code?
You should use this;
headers = new Headers({ 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' });
I am trying to use jwt as my token and use it to access an authorized API. I use Postman to test my API and add Authorization header with the value Bearer MyToken but the server response is 401 UnAuthorized.
here is how I create my token:
in my startUp:
services.AddAuthentication (JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer (options => {
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey (Encoding.ASCII
.GetBytes (Configuration.GetSection ("AppSettings:Token").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
});
app.UseAuthentication ();
I have put the [Authorize]on the top of my Controller. and here is the part where I create my token:
class JWTToken {
public static object CreateToken (string Guid) {
var claims = new [] { new Claim (ClaimTypes.NameIdentifier, Guid) };
var key = new SymmetricSecurityKey (Encoding.UTF8.GetBytes ("Super secret key"));
var creds = new SigningCredentials (key, SecurityAlgorithms.HmacSha512Signature);
var tokenDescriptor = new SecurityTokenDescriptor {
Subject = new ClaimsIdentity (claims), Expires = DateTime.Now.AddYears (2), SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler ();
var token = tokenHandler.CreateToken (tokenDescriptor);
return tokenHandler.WriteToken(token);
}
You use different encodings:
// Here you use ASCII
IssuerSigningKey = new SymmetricSecurityKey (Encoding.ASCII
.GetBytes (Configuration.GetSection ("AppSettings:Token").Value))
// Here you use UTF8
var key = new SymmetricSecurityKey (Encoding.UTF8.GetBytes ("Super secret key"));
Also make sure that yours Configuration.GetSection ("AppSettings:Token").Value is same as "Super secret key" that you use to create JWT.
EDIT:
This is my configuration that works:
// In ConfigureServices
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Token:SigningKey"]));
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(config =>
{
config.RequireHttpsMetadata = false;
config.SaveToken = true;
config.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = signingKey,
ValidateAudience = true,
ValidAudience = this.Configuration["Token:Audience"],
ValidateIssuer = true,
ValidIssuer = this.Configuration["Token:Issuer"],
RequireExpirationTime = true,
RequireSignedTokens = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.FromMinutes(3)
};
});
// In token controller
private string GetToken(AppUser user)
{
var utcNow = DateTime.UtcNow;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Id),
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, utcNow.ToString())
};
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Token:SigningKey"]));
var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
var jwt = new JwtSecurityToken(
signingCredentials: signingCredentials,
claims: claims,
notBefore: utcNow,
expires: utcNow.AddSeconds(_configuration.GetValue<int>("Token:Lifetime")),
audience: _configuration["Token:Audience"],
issuer: _configuration["Token:Issuer"]
);
return new JwtSecurityTokenHandler().WriteToken(jwt);
}
Maybe it will help you.
You need to validate the token signature. You set ValidateIssuersigningKey to true, but did you assign proper key to validate it? You could also try to implement custom validation method.
Middleware, if it considers that token is not valid, will respond with 401 as it cannot be authorized (Unauthotized)
I have a JsonWebTokenFormat class which creates a JWT token and signs it with a X.509 RSA SSH 256 certificate.
internal class JsonWebTokenFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string _issuer;
private readonly ICertificateStore _store;
public JsonWebTokenFormat(string issuer, ICertificateStore store)
{
_issuer = issuer;
_store = store;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
RSA rsaPrivateKey = _store.GetCurrentUserPrivateCertificate(_issuer);
SigningCredentials signingCredentials = new SigningCredentials(new RsaSecurityKey(rsaPrivateKey), SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest);
DateTimeOffset? issued = data.Properties.IssuedUtc;
DateTimeOffset? expires = data.Properties.ExpiresUtc;
JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(
issuer: _issuer,
claims: data.Identity.Claims,
notBefore: issued.Value.UtcDateTime,
expires: expires.Value.UtcDateTime,
signingCredentials: signingCredentials);
JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
string jwtAuthToken = jwtSecurityTokenHandler.WriteToken(jwtSecurityToken);
return jwtAuthToken;
}
public AuthenticationTicket Unprotect(string jwtToken)
{
// read the issuer from the token
JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(jwtToken);
RSA rsaPublicKey = _store.GetPublicCertificateForClient(jwtSecurityToken.Issuer);
TokenValidationParameters tokenValidationParams = new TokenValidationParameters
{
ValidIssuer = _issuer,
RequireExpirationTime = true,
ValidateIssuer = true,
RequireSignedTokens = true,
ValidateLifetime = true,
ValidateAudience = false,
IssuerSigningKey = new RsaSecurityKey(rsaPublicKey),
ValidateIssuerSigningKey = true
};
JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
SecurityToken tempToken;
ClaimsPrincipal principal = jwtSecurityTokenHandler.ValidateToken(jwtToken, tokenValidationParams, out tempToken);
AuthenticationTicket authenticationTicket = new AuthenticationTicket(new ClaimsIdentity(principal.Identity), new AuthenticationProperties());
return authenticationTicket;
}
}
And the ICertificateStore implementation looks like this:
class MockCertificateStore : ICertificateStore
{
private readonly X509Certificate2 _certificate;
public MockCertificateStore()
{
_certificate = new X509Certificate2(
#"C:\certs\test-client.pfx",
"12345",
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.Exportable);
}
public RSA GetCurrentUserPrivateCertificate(string subject)
{
return _certificate.GetRSAPrivateKey();
}
public RSA GetPublicCertificateForClient(string clientId)
{
return _certificate.GetRSAPublicKey();
}
}
So I have this unit test that tests this class and it works fine on my local machine (and other developers' local machines) but it fails on our Jenkins build environment.
It fails with the following exception:
Test method AuthCore.Tests.Token.JsonWebTokenFormatTests.EnsureProtectGeneratesCorrectAuthToken threw exception:
System.NotSupportedException: Method is not supported.
Stack Trace:
at System.Security.Cryptography.RSA.DecryptValue(Byte[] rgb)
at System.Security.Cryptography.RSAPKCS1SignatureFormatter.CreateSignature(Byte[] rgbHash)
at System.IdentityModel.Tokens.AsymmetricSignatureProvider.Sign(Byte[] input) in c:\workspace\WilsonForDotNet45Release\src\System.IdentityModel.Tokens.Jwt\AsymmetricSignatureProvider.cs:line 224
at System.IdentityModel.Tokens.JwtSecurityTokenHandler.CreateSignature(String inputString, SecurityKey key, String algorithm, SignatureProvider signatureProvider) in c:\workspace\WilsonForDotNet45Release\src\System.IdentityModel.Tokens.Jwt\JwtSecurityTokenHandler.cs:line 854
at System.IdentityModel.Tokens.JwtSecurityTokenHandler.WriteToken(SecurityToken token) in c:\workspace\WilsonForDotNet45Release\src\System.IdentityModel.Tokens.Jwt\JwtSecurityTokenHandler.cs:line 815
at AuthCore.Token.JsonWebTokenFormat.Protect(AuthenticationTicket data) in C:\Jenkins\workspace\AuthCore\Token\JsonWebTokenFormat.cs:line 38
at AuthCore.Tests.Token.JsonWebTokenFormatTests.EnsureProtectGeneratesCorrectAuthToken() in C:\Jenkins\workspace\AuthCore.Tests\Token\JsonWebTokenFormatTests.cs:line 34
Any help is appreciated. I've looked at a bunch of SO questions and none of them helped.
Solved!
The problem was that the RsaSecurityKey class has been deprecated since .NET 4.6.0. For some reason, this class is throwing when used in a computer that doesn't have older version of .NET installed, but it was fine on computer with older versions of .NET. Instead, just use X509SecurityKey class.
Refer to the following article for potential solution:
https://q-a-assistant.com/computer-internet-technology/338482_jwt-generation-and-validation-in-net-throws-key-is-not-supported.html
static string GenerateToken()
{
var tokenHandler = new JwtSecurityTokenHandler();
var certificate = new X509Certificate2(#"Test.pfx", "123");
var securityKey = new X509SecurityKey(certificate);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(),
Issuer = "Self",
IssuedAt = DateTime.Now,
Audience = "Others",
Expires = DateTime.MaxValue,
SigningCredentials = new SigningCredentials(
securityKey,
SecurityAlgorithms.RsaSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
static bool ValidateToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
var certificate = new X509Certificate2(#"Test.cer");
var securityKey = new X509SecurityKey(certificate);
var validationParameters = new TokenValidationParameters
{
ValidAudience = "Others",
ValidIssuer = "Self",
IssuerSigningKey = securityKey
};
var principal = tokenHandler.ValidateToken(token, validationParameters, out SecurityToken securityToken);
if (principal == null)
return false;
if (securityToken == null)
return false;
return true;
}
Background:
I am attempting to support an authorization code flow to enable SSO from my app to a third party application.
Now I am stuck on the implementation of the Unprotect method which is supposed to return an AuthenticationTicket.
OAuth2 Server Configuration:
var allowInsecureHttp = bool.Parse(ConfigurationManager.AppSettings["AllowInsecureHttp"]);
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = allowInsecureHttp,
TokenEndpointPath = new PathString("/oauth2/token"),
AuthorizeEndpointPath = new PathString("/oauth2/authorize"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CustomOAuthProvider(HlGlobals.Kernel),
AccessTokenFormat = new CustomJwtFormat(_baseUrl, HlGlobals.Kernel),
AuthorizationCodeProvider = new SimpleAuthenticationTokenProvider(),
AuthorizationCodeFormat = new CustomJwtFormat(_baseUrl, HlGlobals.Kernel)
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
JWT Token Generation / Protect method:
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
// Get the client and assign to GUID -- the audience is api this token will be valid against
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
Guid clientId;
bool isValidAudience = Guid.TryParse(audienceId, out clientId);
// Check for a valid Client Guid in the Auth ticket properties
if (!isValidAudience)
{
throw new InvalidOperationException("AuthenticationTicket.Properties does not include audience");
}
// Create the JWT token
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 the JWT Token
return jwt;
}
Finally, the 'Unprotect' method which is responsible for validation of the JWT and returning and authentication ticket:
public AuthenticationTicket Unprotect(string protectedText)
{
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
Guid clientId;
bool isValidAudience = Guid.TryParse(audienceId, out clientId);
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
var tokenValidationParameters = new TokenValidationParameters
{
ValidAudience = audienceId,
ValidIssuer = _issuer,
IssuerSigningKey = signingKey // Cannot convert HMAC Signing Credentials to System.IdentityModel.Tokens.SecurityKey
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
RequireSignedTokens = true,
RequireExpirationTime = true,
ValidateIssuerSigningKey = true
};
var handler = new JwtSecurityTokenHandler();
SecurityToken token = null;
var principal = handler.ValidateToken(protectedText, tokenValidationParameters, out token);
var identity = principal.Identities;
return new AuthenticationTicket(identity.First(), new AuthenticationProperties());
}
One issue right off the jump is the issuer signing key. I am having trouble coming up with an acceptable parameter. I am seeing the error message:
Cannot convert HMAC Signing Credentials to
System.IdentityModel.Tokens.SecurityKey
To be honest, I am unsure why the Protect method needs to fire. I thought the flow would end with the returning of the JWT token, but apparently not. Now I am struggling with the implementation of the Unprotect method as it is something I have never had to struggle with previously.
Even if I set all of the options to 'false' on the tokenValidationParamters I am still getting the following error on validation:
An exception of type
'System.IdentityModel.SignatureVerificationFailedException' occurred
in System.IdentityModel.Tokens.Jwt.dll but was not handled in user
code
Additional information: IDX10503: Signature validation failed. Keys
tried: ''.
Exceptions caught:
''.
token:
'{"typ":"JWT","alg":"HS256"}.{"iss":"http://myissuer.com","aud":"346e886acabf457cb9f721f615ff996c","exp":1510925372,"nbf":1510925072}'
When I compare the values to the decrypted token using JWT.IO all of the values match as expected.
Any guidance on what I may be doing wrong here, or how to call validateToken with a valid signing key on the tokenvalidationparamters would be most helpful and appreciated.
Thanks in advance!
EDIT:
I am curious why the Unprotect is firing at all... I thought I should be returning a JWT token to the client. The method to return a JWT fires before the Unprotect method and the JWT is never returned to the client.
Do I have something configured incorrectly?
In your Startup.cs file call the following methods. The Protect method in your customJwtFormat is called when the user actually tries to sign in to the authentication server endpoint. The unprotect method is called when the user tries to access a protected api url via the "Bearer [token]" authentication model. If you don't say for example app.UseOAuthBearerAuthentication in your Startup.cs file you'll end up getting errors such as
Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationMiddleware
Warning: 0 : invalid bearer token received
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
AccessTokenFormat = _tokenFormat
});
Create your customjwtFormat like below, you can alter the implementation as required.
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 securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(keyByteArray);
securityKey.KeyId = ConfigurationManager.AppSettings["as:AudienceId"];
var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
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, signingCredentials);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
public AuthenticationTicket Unprotect(string protectedText)
{
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
string authority = ConfigurationManager.AppSettings["Authority"];
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(keyByteArray);
var tokenValidationParameters = new TokenValidationParameters
{
ValidAudience = audienceId,
ValidIssuer = _issuer,
IssuerSigningKey = signingKey,
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
RequireSignedTokens = true,
RequireExpirationTime = true,
ValidateIssuerSigningKey = true
};
var handler = new JwtSecurityTokenHandler();
SecurityToken token = null;
// Unpack token
var pt = handler.ReadJwtToken(protectedText);
string t = pt.RawData;
var principal = handler.ValidateToken(t, tokenValidationParameters, out token);
var identity = principal.Identities;
return new AuthenticationTicket(identity.First(), new AuthenticationProperties());
}
you should use something like this
public AuthenticationTicket Unprotect(string protectedText)
{
if (string.IsNullOrWhiteSpace(protectedText))
{
throw new ArgumentNullException("protectedText");
}
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"];
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadToken(protectedText);
SecurityToken validatedToken;
string t = ((JwtSecurityToken)token).RawData;
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
//var signingKey = new HmacSigningCredentials(keyByteArray);
var validationParams = new TokenValidationParameters()
{
ValidateLifetime = false,
ValidAudience = audienceId,
ValidIssuer = audienceId,
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningToken = new BinarySecretSecurityToken(keyByteArray)
};
var principal = handler.ValidateToken(t, validationParams, out validatedToken);
var identity = principal.Identities;
return new AuthenticationTicket(identity.First(), new AuthenticationProperties());
}
I get the following error on the token consumer. Any help resolving this will be most appreciated. Thanks.
"IDX10503: Signature validation failed.
Keys tried:
'System.IdentityModel.Tokens.SymmetricSecurityKey '. Exceptions
caught: 'System.InvalidOperationException: IDX10636:
SignatureProviderFactory.CreateForVerifying returned null for key:
'System.IdentityModel.Tokens.SymmetricSecurityKey',
signatureAlgorithm:
'http://www.w3.org/2001/04/xmldsig-more#hmac-sha256'. at
Microsoft.IdentityModel.Logging.LogHelper.Throw(String message, Type
exceptionType, EventLevel logLevel, Exception innerException) at
System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateSignature(Byte[]
encodedBytes, Byte[] signature, SecurityKey key, String algorithm) at
System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateSignature(String
token, TokenValidationParameters validationParameters) '. token:
'token info was here'"
Token Generation Code on OAuth server
using (var ctlr = new EntityController())
{
var authRepo = ctlr.GetAuthModelRepository();
string clientId;
ticket.Properties.Dictionary.TryGetValue(WebConstants.OwinContextProps.OAuthClientIdPropertyKey, out clientId);
if (string.IsNullOrWhiteSpace(clientId))
{
throw new InvalidOperationException("AuthenticationTicket.Properties does not include audience");
}
//audience record
var client = authRepo.FindAuthClientByOAuthClientID(clientId);
var issued = ticket.Properties.IssuedUtc;
var expires = ticket.Properties.ExpiresUtc;
var hmac = new HMACSHA256(Convert.FromBase64String(client.Secret));
var signingCredentials = new SigningCredentials(
new InMemorySymmetricSecurityKey(hmac.Key),
Algorithms.HmacSha256Signature, Algorithms.Sha256Digest);
TokenValidationParameters validationParams =
new TokenValidationParameters()
{
ValidAudience = clientId,
ValidIssuer = _issuer,
ValidateLifetime = true,
ValidateAudience = true,
ValidateIssuer = true,
RequireSignedTokens = true,
RequireExpirationTime = true,
ValidateIssuerSigningKey = true,
IssuerSigningToken = new BinarySecretSecurityToken(hmac.Key)
};
var jwtHandler = new JwtSecurityTokenHandler();
var jwt = new JwtSecurityToken(_issuer, clientId, ticket.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingCredentials);
jwtOnTheWire = jwtHandler.WriteToken(jwt);
SecurityToken validatedToken = null;
jwtHandler.ValidateToken(jwtOnTheWire, validationParams,out validatedToken);
if (validatedToken == null)
return "token_validation_failed";
}
return jwtOnTheWire;
Token Consumption\validation ASP.Net 5 vNext site within Owin Startup.cs
public void ConfigureServices(IServiceCollection services)
services.ConfigureOAuthBearerAuthentication(config =>
{
//oauth validation
var clientSecret = "not the real secret";
var hmac = new HMACSHA256(Convert.FromBase64String(clientSecret));
var signingCredentials = new SigningCredentials(
new SymmetricSecurityKey(hmac.Key), Algorithms.HmacSha256Signature, Algorithms.Sha256Digest);
config.TokenValidationParameters.ValidAudience = "myappname";
config.TokenValidationParameters.ValidIssuer = "mydomain.com";
config.TokenValidationParameters.RequireSignedTokens = true;
config.TokenValidationParameters.RequireExpirationTime = true;
config.TokenValidationParameters.ValidateLifetime = true;
config.TokenValidationParameters.ValidateIssuerSigningKey = true;
config.TokenValidationParameters.ValidateSignature = true;
config.TokenValidationParameters.ValidateAudience = true;
config.TokenValidationParameters.IssuerSigningKey = signingCredentials.SigningKey;
});
public void Configure(IApplicationBuilder app)
app.UseOAuthBearerAuthentication(config =>
{
config.AuthenticationScheme = "Bearer";
config.AutomaticAuthentication = true;
});
I was able to add my own signature validation to the TokenValidationParameters Then I compared the incoming Raw signature of the JWT to the compiled signature in this code and if it matches the signature is valid.
Why this didn't happen using the builtin signature validation is beyond me, maybe it's a possible bug in beta 6 of the vNext Identity token framework.
public void ConfigureServices(IServiceCollection services)
config.TokenValidationParameters.SignatureValidator =
delegate (string token, TokenValidationParameters parameters)
{
var clientSecret = "not the real secret";
var jwt = new JwtSecurityToken(token);
var hmac = new HMACSHA256(Convert.FromBase64String(clientSecret));
var signingCredentials = new SigningCredentials(
new SymmetricSecurityKey(hmac.Key), SecurityAlgorithms.HmacSha256Signature, SecurityAlgorithms.Sha256Digest);
var signKey = signingCredentials.SigningKey as SymmetricSecurityKey;
var encodedData = jwt.EncodedHeader + "." + jwt.EncodedPayload;
var compiledSignature = Encode(encodedData, signKey.Key);
//Validate the incoming jwt signature against the header and payload of the token
if (compiledSignature != jwt.RawSignature)
{
throw new Exception("Token signature validation failed.");
}
return jwt;
};
Encode helper method
public string Encode(string input, byte[] key)
{
HMACSHA256 myhmacsha = new HMACSHA256(key);
byte[] byteArray = Encoding.UTF8.GetBytes(input);
MemoryStream stream = new MemoryStream(byteArray);
byte[] hashValue = myhmacsha.ComputeHash(stream);
return Base64UrlEncoder.Encode(hashValue);
}