AutomaticAuthenticate for ASP.NET IdentityServer4 on open routes? - asp.net-core

I have a few endpoints in an ASP.NET core application that's also hosting IdentityServer.
When I issue a JWT token to a client, and that client calls an [AllowAnonymous] endpoint with the JWT as a bearer token, the User principal is empty. It looks like the token isn't being parsed by any middleware, and there doesn't seem to be an option to specify always attempting to parse a JWT.
Is there a way to get this handled automatically or do I need to use the .AddJwtBearer extension? If the latter I can't seem to find an easy way for me to populate the signingkey such that it matches the ones configured for identity server.
var tokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = ?? signing keys from Identity Server,
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = // I guess this is typically the public URL for this instance,
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here:
ClockSkew = TimeSpan.Zero
};
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = tokenValidationParameters
});
I would guess that for the SigningKey I could pass through the X509 that I'm configuring on IdentityServer via the x509SecurityKey class

UseJwtBearerAuthentication is obsolete. Configure JwtBearer authentication with AddAuthentication().AddJwtBearer in ConfigureServices.
Also make sure app.UseAuthentication(); is added in your startup class.
I find a best practice is to always host IdentityServer, client and API on separate ASP.NET Core instances, because otherwise its hard to reason about what is what. IdentityServer issues its own User and cookie so I think your system is confused about what user to use?
Reference:
JwtBearerAppBuilderExtensions.UseJwtBearerAuthentication Method
Auth 2.0 Migration announcement

Related

Generate JWT token on successful authentication with Windows Authentication

I'm having some difficulty understanding how to use Windows Authentication to authenticate a user, but then return a JWT to the client containing that authenticated user's claims. This is using .Net Core 2.0.
I've put the following in Startup.cs.
services.AddAuthentication
(
IISDefaults.AuthenticationScheme
).AddJwtBearer("Bearer", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("someKey")),
ValidateLifetime = true,
};
}
);
However, it doesn't seem that this is sufficient to generate the token. Some other guidance I've read suggests the token should be generate manually as part of an authorization routine but this seems to be tailored for when validating a username/password against a database or other provider. (For instance: https://fullstackmark.com/post/13/jwt-authentication-with-aspnet-core-2-web-api-angular-5-net-core-identity-and-facebook-login.) But with windows auth there does not appear to be an event or other structure that allows me to do this.
Any ideas?

.net Core Api authentication with ADFS 2012

I need to configure my .Net Core Web Api (.Net Framework) to use ADFS 3.0 (2012) to validate the Bearer tokens sent by our mobile clients.
I am able to generate the access_token from the ADFS server, and I pass it in the Authorization header.
My problem is in the API: how do I configure it to validate and autorize the user?
I searched in many places and I could not find a definitive method of doing it.
What I tried so far:
Used IdentityServer4 (Failed because it uses JWT and ADFS doesn't offer OpenID
Tried UseOpenIdConnectAuthentication (found example at IdentityServer4)
a custom Middleware
I can't use another method, I need to support oAuth2.
So, how do I do it?
Here is my latest try:
var connectOptions = new OpenIdConnectOptions
{
AuthenticationScheme = "adfs",
SignInScheme = "idsrv.external", //IdentityServerConstants.ExternalCookieAuthenticationScheme,
SignOutScheme = "idsrv", //IdentityServerConstants.SignoutScheme,
AutomaticChallenge = false,
DisplayName = "ADFS",
Authority = $"https://{options.AdfsHostName}/adfs/oauth2",
ClientId = options.ClientID,
ResponseType = "id_token",
Scope = { "openid profile" },
CallbackPath = new PathString("/signin-adfs"),
SignedOutCallbackPath = new PathString("/signout-callback-adfs"),
RemoteSignOutPath = new PathString("/signout-adfs"),
ClaimsIssuer = $"https://{options.AdfsHostName}/adfs/services/trust",
//TokenValidationParameters = new TokenValidationParameters
//{
// ValidateIssuer = true,
// ValidIssuer = $"https://{options.AdfsHostName}/adfs/services/trust"
//},
};
app.UseOpenIdConnectAuthentication(connectOptions);
I get a very quick 401 on every calls, with a valid token. In fact, while I see the connection in the console window, I don't see any other log in the Roslyn console window regarding the security validation.
I'm currently using ASP.Net Core 1.1.X, and if I can I'd avoid moving to .Net Core 2.0, as we are late in the project and it contains many breaking changes...
Feel free to ask for more info, and I'll appreciate all the good advices!
As it turns out, we can use the JwtBearerAuthentication with ADFS 3.0.
My initial problem with it was that it went to fetch the metadata at /.well-known/openid-configuration, but ADFS 3.0 does not support OpenID and this returns a 404.
I read in another post (I'll update it when I find it) that if with the right configuration, it won't need to fetch the config. But what configuration?
Well I found deep in the (MS) code that if one pass an OpenIdConnectConfiguration object to the Configuration property of the JwtBearerOptions, it wont fetch the metadata.
So here is my code now:
var rawCertData = Convert.FromBase64String(options.X509SigninCertificate);
X509Certificate2 cert = new X509Certificate2(rawCertData);
SecurityKey signingKey = new X509SecurityKey(cert);
The X509 cert data comes from the supported adfs metadata at this url
https://Your.ADFS.Site/FederationMetadata/2007-06/FederationMetadata.xml
It contains this:
<KeyDescriptor use="signing">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>SOMEUUENCDODEDSTRING=</X509Certificate>
</X509Data>
</KeyInfo>
</KeyDescriptor>
I simply copied the UUEncoded string in my settings' X509SigninCertificate property.
var tokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = $"https://{options.AdfsHostName}/adfs/services/trust",
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = options.ClientUri, //"https://YOUR-AUDIENCE/",
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here:
ClockSkew = TimeSpan.Zero
};
var connectOptions = new OpenIdConnectConfiguration
{
Issuer = $"https://{options.AdfsHostName}/adfs/services/trust",
};
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = tokenValidationParameters,
Configuration = connectOptions
});
The important line here is
Configuration = connectOptions
By doing this, you tell the validator to not fetch the metadata. Simple as that.
I was able to validate my token (AUD, ISS and SIGN) and I can use ADFS in my project.
Only ADFS 2016 supports OpenID Connect. If you want to use the OAuth endpoint in 2012, you need to write your own authorisation handler. An example to build on would be ASP.NET Core's own Twitter implementation. Note that these handlers need to be implemented differently in ASP.NET Core 1.* vs 2.0+.

.NETCore - JSON Web Tokens With Multiple Validation Rules

In .NETCore asp net I am using UseJwtBearerAuthentication to protect controllers using access tokens in a manner similar to this:
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "TestIssuer",
ValidateAudience = true,
ValidAudience = "TestAudience",
ValidateLifetime = true,
}
});
However, I want to provide more than one set of options as there is the potential that different audiences will require different levels of access. For instance, access tokens may need to have different lifetimes.
I can achieve this by adding the different options like this:
app.UseJwtBearerAuthentication(jwtBearerOptions1);
app.UseJwtBearerAuthentication(jwtBearerOptions2);
This works fine. And access is granted to the controllers as expected.
However, the problem I've found is that while I'm correctly granted or denied access as expected, the debug console receives a lot of verbose exception messages as the .NET authentication code tries to authenticate against each of the available JwtBearerOptions.
It will record that one has been successful but also every one that has failed (example below).
My question: Is there a way to suppress this message as I expect there to be these failures? Or should I use a different method to allow multiple JwtBearerOptions to be added?
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10503: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey , KeyId:
'.
Exceptions caught:
''.
token: '{"alg":"HS256","typ":"JWT"}.{"sub":"testuser","jti":"c98bb91a-5dcd-4fd7-97ff-c10cddf18989","nbf":1497015309,"exp":1497101709,"iss":"TestIssuer","aud":"TestAudience"}'.
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__1.MoveNext()
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware[7]
Bearer was not authenticated. Failure message: IDX10503: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey , KeyId:
'.

Validating B2C JWT tokens in Asp.Net Core Web Api

I am using B2C to protect a WebApi in Asp.Net Core. My code is below. Do I need to validate the tokens or is the middleware doing it for me? I would think if everyone had to do this, it'd be easier for me to find some sample code, but I can't seem to get any real direction on this.
Yet, this B2C documentation states that my api do the validation.
I found a sample but it's not for Core and they're using CertificateValidator = X509CertificateValidator.None. Doesn't that defeat the purpose? And another sample here where they are doing it.
Don't I have to have the signing key from B2C and all that?
I can cobble together a solution from those, but do I actually need to do this?
Thanks in advance.
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
AuthenticationScheme = Constants.B2CAuthenticationSchemeName,
AutomaticAuthenticate = false,
MetadataAddress = string.Format(
_identityConfig.B2CInfo.AadInstance,
_identityConfig.B2CInfo.Tenant,
_identityConfig.B2CInfo.Policies
.Where(p => p.IsDefaultSignUpSignInPolicy == true)
.First()
.Name),
Audience = _identityConfig.B2CInfo.ClientId,
TokenValidationParameters = new TokenValidationParameters
{
ValidateLifetime = true,
RequireExpirationTime = true,
RequireSignedTokens = true,
},
Events = new JwtBearerEvents
{
OnAuthenticationFailed = B2CAuthenticationFailed
}
});
Do I need to validate the tokens or is the middleware doing it for me?
The JWT bearer middleware does it for you (by default, it will automatically reject unsigned or counterfeit tokens, so you don't need to explicitly set RequireSignedTokens to true).
Doesn't that defeat the purpose?
There's a difference between validating a signature using a public asymmetric key (e.g RSA or ECDSA) embedded in a certificate and validating the certificate itself (and specially its chain). Signature validation is fully supported in ASP.NET Core, but certificate validation is not supported yet.
Don't I have to have the signing key from B2C and all that?
The JWT bearer middleware automatically retrieves it from B2C's discovery endpoint, so there's no need to do that manually. For more information, don't hesitate to read the OIDC discovery specification: https://openid.net/specs/openid-connect-discovery-1_0.html

IdentityServer4 Authorize always gets "The signature key was not found" on Azure AppService

I have an IdentityServer4 app based on the IS4 Identity sample, and an API using bearer tokens for it's Authorization via IS4.AccessTokenValidation. This is working fine on localhost via VisualStudio, and when I deploy to a Windows 2012 VM and hosted via IIS. When I deploy the Identity server to Azure as an App Service website, all is fine too. However when the API is deployed as an App Service using same domain and certificate as the VM, any method with an Authorize attribute (with a policy or none it doesn't matter) always returns a 401 with the header message:
Www-Authenticate: Bearer error="invalid_token", error_description="The signature key was not found"
We're using .NET 4.5.2, with the latest releases of IdentityServer4, and IdentityServer4.AccessTokenValidation packages. I've also pulled the latest of these packages from GitHub from 30/08/16 with no change. I don't think it's a bug is IS4 Validator anyway, but I don't know what might cause this. Any suggestions? Is it an Azure host bug?
I'd love to be able to debug this, but I can't get Remote Debug working to this app even when I rebuilt from scratch, and app logs tell me nothing. I've had a rummage in the ASP.NET Security repo, but without more logging or debug access, I'm pretty clueless how to fix this problem.
API Configure is very basic:
var jwtBearerOptions = new JwtBearerOptions()
{
Authority = Configuration["Authentication:IdentityServer:Server"],
Audience = Configuration["Authentication:IdentityServer:Server"]+"/resources",
RequireHttpsMetadata = false,
AutomaticAuthenticate = true,
AutomaticChallenge = true,
};
app.UseJwtBearerAuthentication(jwtBearerOptions);
and the Identity Server is straight out of the samples, and using a purchased certificate for signing.
Has anyone else got this configuration fully working as 2 Azure App Services? Or what might possibly cause this error given the same bearer token sent to the VM hosted API is acceptable.
It turned out you need to explicitly set the IssuerSigningKey in TokenValidationParameters. So I get the certificate from the App Service store, and add it via JwtBearerOptions.TokenValidationParameters. So Startup config looks like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();
var tokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = new X509SecurityKey(GetSigningCertificate()),
// Validate the JWT Issuer (iss) claim
ValidateIssuer = false,
//ValidIssuer = "local",
// Validate the JWT Audience (aud) claim
ValidateAudience = false,
//ValidAudience = "ExampleAudience",
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here:
ClockSkew = TimeSpan.Zero
};
var jwtBearerOptions = new JwtBearerOptions()
{
Authority = Configuration["Authentication:IdentityServer:Server"],
Audience = Configuration["Authentication:IdentityServer:Server"]+"/resources",
RequireHttpsMetadata = false,
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = tokenValidationParameters
};
app.UseJwtBearerAuthentication(jwtBearerOptions);
app.UseMvc();
...
}
No idea why this is only needed on the Azure App Service and not on a server or development machine. Can anyone else explain it? It would suggest ValidateIssuerSigningKey default to true for App Service and false anywhere else.