IDX10501: Signature validation failed. Unable to match keys - c#

Please help me to understand the difference between JWT token validation from the ASP netcore application and the netcore Kestrel hosted application.
There are two applications that verifies token using the source code like below:
public static IServiceCollection AddJwtToken(this IServiceCollection services, OAuthConfig config)
{
services.AddMvc();
services.AddAuthorization();
Logger.DebugFormat("AddJwtBearer authority:{0} audience:{1}", config.GetAuthority(), config.Resource);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => new JwtBearerOptions
{
Authority = config.GetAuthority(),
Audience = config.Resource,
});
return services;
}
it is pretty simple and it works well if token is being validated from the asp net core 2.2 application
// in the asp.net core
var builder = WebHost.CreateDefaultBuilder(args);
builder
.UseStartup<Startup>()
.ConfigureKestrel(_ => _.ConfigureEndpoints())
.UseSerilog();
And there is another application (console) that starts the same rest service host using the UseKestrel
//in the console app
var builder = WebHost.CreateDefaultBuilder()
.UseNLog()
.UseKestrel(_ => _.ConfigureEndpoints())
.UseStartup<Startup>();
the only one significant difference is that there is UseKestrel in the console via ConfigureKestrel for asp.net core.
The same source code (and configuration) is used to get token from the Azure AD.
Please find it as the gist here.
It is configured to get token from the https://login.microsoftonline.com/{tenant}/v2.0 provider. The same token endpoint, clientid, secret and scope values are used for both cases.
The problem is that AddJwtBearer validates the token in the asp.net core and does not in the console app.
the error is
Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10501: Signature validation failed. Unable to match keys:
kid: 'BB8CeFVqyaGrGNuehJIiL4dfjzw',
token: '{"typ":"JWT","alg":"RS256","kid":"BB8CeFVqyaGrGNuehJIiL4dfjzw"}.{"aud":"2c163c99-935b-4362-ae0d-657f589f5565","iss":"https://login.microsoftonline.com/{tenantidhere}/v2.0
Why asp.net core host validates the token (for the first AddJwtBearer implementation) and console host fails?
Thank you

to solve this error I've to load keys from the openid provider as below:
Logger.DebugFormat("AddJwtBearer authority:{0} audience:{1}", config.GetAuthority(), config.Resource);
IList<string> validissuers = new List<string>()
{
config.GetAuthority(),
};
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{validissuers.Last()}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
var openidconfig = configManager.GetConfigurationAsync().Result;
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, _ =>
{
_.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateAudience = true,
ValidAudience = config.Resource,
ValidateIssuer = true,
ValidIssuers = new[] { config.GetAuthority() },
ValidateIssuerSigningKey = true,
IssuerSigningKeys = openidconfig.SigningKeys,
RequireExpirationTime = true,
ValidateLifetime = true,
RequireSignedTokens = true,
};
_.RequireHttpsMetadata = false;
});
And it started to work for both cases. But what is the difference with the old AddJwtBearer implementation and the new one (related to the keys validation)? Keys where downloaded and supplied using the IssuerSigningKeys = openidconfig.SigningKeys but why it is not loaded automatically using the .well-known/openid-configuration by the AddJwtBearer middleware ?

In my case, the same error was because of inadvertent use of the token received from one environment (https://dev/identity) and validated in another environment (i.e. http://local/identity).

Related

How to specify client certificate when connecting to ASP.NET Core SignalR hub

I have a .NET SignalR client (in an ASP.NET Web API 2 project hosted under www.domain1.com) that connects to a SignalR hub which is hosted in a .NET Core API project (www.domain2.com). Both web projects are hosted under IIS. The API is set up to use JWT Bearer as its default authentication scheme. The .NET client can authenticate successfully with the hub by specifying the AccessTokenProvider delegate which essentially calls a login method on the API and returns a JWT token. I'd like to add an extra layer of security by adding a client certificate to the SignalR requests using ClientCertificates.Add on the HttpConnectionOptions object, but no matter what I try, I can't access the certificate on the receiving end (API). The certificate being retrieved from the store is a full certificate (.pfx). The API is set up to use CORS and I do allow www.domain1.com.
Things I've tried:
Access certificate via HttpContext.Connection.ClientCertificate on the .NET Core API side - This always returns null.
Using a WebRequestHandler with a certificate attached on the client side to make a call to the .NET Core API, just to see if this actually sends through the certificate - This also returns null on the receiving end.
Configuring the API to use certificates using the steps outlined in Configure certificate authentication in ASP.NET Core
Specifying the "Require SSL" option in IIS for the API site
Could it be the fact that I am trying to send a certificate cross domain? Is using a certificate for this even something worth doing security-wise? Any help would be much appreciated.
Here is the SignalR client set up code:
private static HubConnection CreateHubConnection()
{
var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
var certificateCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, "mythumbprint", false);
X509Certificate2 cert = certificateCollection[0];
_hubConnection = new HubConnectionBuilder()
.WithUrl($"{_url}/signalR", (HttpConnectionOptions options) =>
{
// This is what I am trying to get to work
options.ClientCertificates.Add(cert);
// This works fine, but I'd like to add an extra layer of security by using a certificate
options.AccessTokenProvider = async () => await TryGetAccessTokenAsync();
})
.ConfigureLogging(builder => builder.AddDebug())
.WithAutomaticReconnect(new MyHubClientRetryPolicy())
.Build();
}
private static async Task<string> TryGetAccessTokenAsync()
{
string signalRLoginDetails = JsonConvert.SerializeObject(new { Username = "username", Password = "password" });
var content = new StringContent(signalRLoginDetails, Encoding.UTF8, MimeMediaTypeNames.Json);
HttpResponseMessage response = await _httpClient.PostAsync(_loginUrl, content);
response.EnsureSuccessStatusCode();
string token = await response.Content.ReadAsStringAsync();
return token;
}
This is how my API is set up:
public void ConfigureServices(IServiceCollection services)
{
// Other code omitted for brevity
var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(appSettings.Secret));
services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = appSettings.Issuer,
ValidAudience = appSettings.Audience,
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = key,
ClockSkew = TimeSpan.FromSeconds(5),
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrWhiteSpace(accessToken) && path.StartsWithSegments(Constants.SignalR.Endpoint))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
}

ASP.NET Core authentication using Cognito User Pools on the ALB

I am porting a legacy ASP.NET MVC application to .NET Core, I am also trying to replace the existing Identity framework to use AWS Cognito User Pools which are handled by the application load balancer.
I have the majority setup and working, when requests hit the ALB and unauthorised user is redirected to the Cognito hosted login page where they can signin. This all works OK but I want to hook up the information in the JWT (passed back from the ALB) to the ASP.NET Identity framework so I can access the populated "User" object.
The response header header containing the JWT is also non-stadard "X-Amzn-Oidc-Data" rather than in the standard Authorization. I have looked at using the Microsoft.AspNetCore.Authentication.JwtBearer but I beleive this expects the standard Authorization header.
I did come across this project https://github.com/awslabs/aws-alb-identity-aspnetcore which appears to be exactly the kind of thing I need but it appears to have been abandoned. Does anyone have any similar experience in acheiving the same thing which they could share?
UPDATE: this is our updated Startup code based on the comment from Mickaël:
services.AddAuthentication("Cognito")
.AddJwtBearer("Cognito", options =>
{
options.Events = options.Events ?? new JwtBearerEvents();
options.Events.OnMessageReceived = context =>
{
string amazonOidcDataHeader = context.Request.Headers["X-Amzn-Oidc-Data"];
if (!string.IsNullOrEmpty(amazonOidcDataHeader))
{
context.Token = amazonOidcDataHeader.Trim();
}
return Task.CompletedTask;
};
options.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
// get JsonWebKeySet from AWS
var json = new WebClient().DownloadString(parameters.ValidIssuer + "/.well-known/jwks.json");
// serialize the result
var keys = JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
// cast the result to be the type expected by IssuerSigningKeyResolver
return (IEnumerable<SecurityKey>)keys;
},
ValidIssuer = this.Configuration["Authentication:Cognito:ValidIssuer"],
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidAudience = this.Configuration["Authentication:Cognito:ClientId"],
ValidateAudience = true
};
});
The token is now retrieved from the header and pushed into the context.Token property, the Identity User object still contains no claims.
The built-in JWT provider has an extensibility point to source the token from virtually anywhere through the JwtBearerEvents.OnMessageReceived event.
You could do something like this:
services
.AddAuthentication("Cognito")
.AddJwtBearer("Cognito", options =>
{
options.Events ??= new JwtBearerEvents();
options.Events.OnMessageReceived = context =>
{
const string bearerPrefix = "Bearer ";
string amazonOidcDataHeader = context.Request.Headers["X-Amzn-Oidc-Data"];
if (!string.IsNullOrEmpty(amazonOidcDataHeader) && amazonOidcDataHeader.StartsWith(bearerPrefix, StringComparison.OrdinalIgnoreCase))
{
context.Token = amazonOidcDataHeader.Substring(bearerPrefix.Length).Trim();
}
return Task.CompletedTask;
};
});
For reference, see this extract of code on GitHub which shows the implementation of the handler, and how it uses the token assigned during the MessageReceived event if there is one, or tries to extract the one from the standard Authorization header otherwise: https://github.com/dotnet/aspnetcore/blob/20f5f6fbc3db4a66faeb941e56db6ace333c1817/src/Security/Authentication/JwtBearer/src/JwtBearerHandler.cs#L58-L91

Multiple Authentication types in ASP.NET Core 3

For Web Users, I am doing Cookie based authentication and for IOT devices, JWT Auth. But I also have several APIs which are used by BOTH Web and IOT users.
If a Cookie exists, I am expecting it to use Cookie/Identity Auth and if bearer exists, JWT Auth. Effectively it may have to try BOTH.
I can get them to work individually i.e., EITHER Cookie Auth or JWT Auth, but not both together!
Following is my code snippet from .Net Core 2.2 application which works exactly the way I need it.
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie()
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = Configuration["JwtIssuer"],
ValidAudience = Configuration["JwtIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])),
};
});
And I also have this in .Net Core 2.2.
services.AddMvc(config =>
{
var defaultPolicy = new AuthorizationPolicyBuilder(new[] { JwtBearerDefaults.AuthenticationScheme, IdentityConstants.ApplicationScheme, CookieAuthenticationDefaults.AuthenticationScheme })
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(defaultPolicy));
});
Can someone help we with what changed in .Net Core 3 and how I can resolve this issue.

Asp.Net Core 2 Validating bearer token

I am struggling to find an exact way of validating my OAuth bearer token which is passed when a request is sent to the API am working on which is a Asp.Net core project.
The purpose here is to extract the bearer token and Validate it and if all is fine then continue with the request.
So far my findings have come across the following
JWT bear token authorization which mostly talks about access_token
Asp.Net core security middleware
Custom Authorize attribute which handle this.
I am not really sure how I can achieve my validation? Should I extract the bearer token and then create a custom validating method?
Ideally would like the [Authorize] attribute to handle this.
Suggestions please?
Well finally after more research I finally found that custom AuthorizationHandler is a more suitable solution as suppose to using custom Authorize attributes which is not suggested in Asp.Net Core.
It was simple to setup and I am able to extract my Bearer token from the header for further authorization with OAuth.
Here is a my approach:
public class CustomAuthorizationHandler: IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
var authFilterCtx = (Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext)context.Resource;
string authHeader = authFilterCtx.HttpContext.Request.Headers["Authorization"];
if (authHeader != null && authHeader.Contains("Bearer"))
{
var token = authHeader.Replace("Bearer", "");
// Now token can be used for further authorization
}
throw new NotImplementedException();
}
}
Lastly registering the handler in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IAuthorizationHandler, CustomAuthorizationHandler>();
}
I think to put the following code snippet inside ConfigureServices() should be able to validate your access_token after installing Microsoft.AspNetCore.Authentication.JwtBearer NuGet package:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
//options.SaveToken = true;
options.MetadataAddress = ValidationEndPoint;
options.RequireHttpsMetadata = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidIssuer = tokenIssuer,
ValidAudiences = new[] { clientId },
ValidAudience = null
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("MyPolicy", policy =>
{
policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
});
});
remember to put app.UseAuthentication() and app.UseAuthorization() in the Configure() method. And add [authorize] to your controller API.

JWTAuthentication not working in asp.net core 2.0 after migrate from 1.1 to 2.0 with System.IdentityModel.Tokens.Jwt - 5.1.4 update

Error
Error CS0619 'JwtBearerAppBuilderExtensions.UseJwtBearerAuthentication(IApplicationBuilder, JwtBearerOptions)' is obsolete: 'See https://go.microsoft.com/fwlink/?linkid=845470'
Here is the code for the JWT authentication
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = key,
ValidAudience = tokenOptions.Audience,
ValidIssuer = tokenOptions.Issuer,
// When receiving a token, check that it is still valid.
ValidateLifetime = true,
// This defines the maximum allowable clock skew - i.e. provides a tolerance on the token expiry time
// when validating the lifetime. As we're creating the tokens locally and validating them on the same
// machines which should have synchronised time, this can be set to zero. Where external tokens are
// used, some leeway here could be useful.
ClockSkew = TimeSpan.FromMinutes(0)
}
});
The same code was working fine in asp.net core 1.1 I, have just migrate from core 1.1 to core 2.0 and updated the System.IdentityModel.Tokens.Jwt(5.1.1) to (5.1.4)
ConfigureServices
RSAParameters keyParams = RsaKeyUtils.GetRandomKey();
key = new RsaSecurityKey(keyParams);
tokenOptions = new TokenAuthOptions()
{
Audience = TokenAudience,
Issuer = TokenIssuer,
SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature)
};
services.AddSingleton<TokenAuthOptions>(tokenOptions);
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌​)
.RequireAuthenticatedUser().Build());
});
I tried the solution posted in this question Authentication in dot net core preview-2.0 . But the solution is not working getting an error as IServiceCollection doesn't contain the definition for AddJwtBearerAuthentication
Tried to implement https://github.com/aspnet/Security/blob/c5b566ed4abffac4cd7011e0465e012cf503c871/samples/JwtBearerSample/Startup.cs#L47-L53
https://github.com/IdentityServer/IdentityServer4/issues/1055
https://blogs.msdn.microsoft.com/webdev/2017/04/06/jwt-validation-and-authorization-in-asp-net-core/
500 internal server error.
The same code was working perfectly on asp.net core 1.1. When upgrade to 2.0 the issue occur.
Please anyone let me know how can I resolve this issue.
The method for adding Jwt Bearer Authentication has changed in ASP.NET Core 2.0. See the latest version of the sample you linked.
You need to register the Jwt Bearer Authentication process as a service, rather than a middleware component. See the relevant code in ConfigureServices:
services.AddAuthentication(...)
.AddJwtBearer(...);
Try this in ConfigureServices:
services.AddAuthentication()
.AddJwtBearer(jwt =>
{
jwt.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = key,
ValidAudience = tokenOptions.Audience,
ValidIssuer = tokenOptions.Issuer,
// When receiving a token, check that it is still valid.
ValidateLifetime = true,
// This defines the maximum allowable clock skew - i.e. provides a tolerance on the token expiry time
// when validating the lifetime. As we're creating the tokens locally and validating them on the same
// machines which should have synchronised time, this can be set to zero. Where external tokens are
// used, some leeway here could be useful.
ClockSkew = TimeSpan.FromMinutes(0)
};
});
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build());
});
And make sure you have this in Configure:
app.UseAuthentication();
As I can't test this against your scenario, there's every chance it won't work first time. Be sure to be specific when you include any further information that comes from this.

Categories

Resources