How to use .UseJwtBearerAuthentication in a OWIN asp.net project? - asp.net

I'm converting a ASP.NetCore project to a stateless Web Api one. I was using the following to authenticate my JWT:
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = key,
ValidIssuer = "http://localhost:5000",
ValidAudience = "http://localhost:5000/resources",
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(0)
}
});
And then Authorizing a controller using [Authorize("Bearer")]
However, I've noticed the OWIN project uses a different Authorize and Filters? I've not been able to successfully authorize my JWT using this and HostAuthentication.
Is this possible?

Related

Asp Net ABP: how to map claims from JWT token to ABP User?

We are using ASP.NET Zero based on ASP.NET Boilerplate (.NET Core). We have a Mobile App that authenticates with AWS Cognito and needs to consume some API's and Application Services on our application. (We already make sure that each user in Cognito also has a corresponding user in AbpUsers with permissions.)
The Mobile App will authenticate with a JWT Bearer Token from Cognito. We have added JWT Validation to our Startup.cs as follows:
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options => {
options.ClaimsIssuer = CognitoIssuer;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
var json = new WebClient().DownloadString(CognitoIssuer + "/.well-known/jwks.json");
var keys = JsonConvert.DeserializeObject<JsonWebKeySet>(json)!.Keys;
return keys;
},
RoleClaimType = "cognito:groups",
NameClaimType = "username",
ValidIssuer = CognitoIssuer,
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidateAudience = false
};
});
This validation works in so far as when I decorate an API with the [Authorize] attribute, the API only executes if a valid JWT token is provided. (With an invalid token, a 401 error is returned as expected.)
The problem we have is that the user identity is not being set. I can see that the Claims Principal contains the claims from Cognito. But inside the API the current user and tenant appears as NULL. I assume that after JWT validation I'm missing steps to "map" the claims/user from Cognito to an actual user that exists in the ABP database so that the AbpSession can reflect this user. But how do I do that? How do I set the Identity/User/Tenant/AbpSession so that it can correctly and seamlessly be picked up in my Application Services and Controllers after I've validated the JWT token? (So that Application Services and Controllers can work just as seamlessly as if the user had actually logged on in the ABP app?)

asp.net core OAuth access_token verification fails with "IDX10609: Decryption failed. No Keys tried: token: 'System.String'.:"

I have a single page application in Blazor WASM that connects to OAuth0 and receives JWT token when a user get authenticated via OAuth.
My OAuth application uses symmetric signing with HS256.
I am trying to authenticate the returned tokens using the "Client Secret" from my OAuth app but whereas the id_token gets verified, the access_token always fails with the error:
"IDX10609: Decryption failed. No Keys tried: token: 'System.String'.:"
To perform the verification I use the method found here.
My code looks like that:
var mySecret = "####";
var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret));
var tokenHandler = new JwtSecurityTokenHandler();
try
{
IConfigurationManager<OpenIdConnectConfiguration> configurationManager =
new ConfigurationManager<OpenIdConnectConfiguration>(
$"{my_oauth_app_domain}.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None);
var keys = openIdConfig.SigningKeys;
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateActor = false,
ValidateLifetime = false,
ValidateTokenReplay = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = mySecurityKey,
IssuerSigningKeys = openIdConfig.SigningKeys,
}, out SecurityToken validatedToken);
}
catch(Exception ex)
{
Console.WriteLine($"{ex.Message}: {ex.StackTrace}");
return false;
}
return true;
What I find strange is that the access_token appears to be malformed. I know that it doesn't have to follow the JWT specs but puting it in jwt.io causes an error and fields like "kid" which I have seen in other tokens on the web, are missing.
Here is the token:
eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwiaXNzIjoiaHR0cHM6Ly9nYW50b25vcG91bG9zLmV1LmF1dGgwLmNvbS8ifQ..MQnqWWfe3mBCVeKQ.y8e77jf3VwJNRSoDWjB3v05WrT9IJPL_kdqhxlQFnfMOAyqQJOD1ttl1muYlCJJVwAskaAeBr4FgkcwjiL1s4eS9gcWK7yq-2PPbLkDzXtBjA4kgMGdGyMURl-F2jYBNwxbCuXyBKcxJVwzE4-aluYCAOZ8QaXzqKgmQJdpxIdBluVux7nK49uhvEJ5Pv7ueh7eGcm9AAmHm__TYKPPcpPutHNiuD6J8xoptHFLPjKakECE6ZXgD-xLNp4BHwe_DmW6UDPuZ_OD9G8D-hwayz8l--zZdICEnFywUzSWXFiVPUvn4DszDhzWbJsBNf3dnl2cnKel3EYsB.NvsUTcP9v_iicpQ5AkaC4w
Am I doing something wrong?
I found the problem here. In my request to OAuth0 I did not add the "audience" parameter. This led to an opaque token being issued.

validate JWT without public key (RSA) .net core webapi

I am new JWT in DOTNet core web Api. In our application, we are getting access_token from the Microsoft site. https://login.microsoftonline.com/
We would like to validate the token (RS256 algo) in the .net core (Api) but we don't have the PUBLIC KEY.
Note: I already have a token. How can we validate JWT with our public key and any other thing? I only have an access token.
With JWT you basically have two scenarios:
you created your JWT yourself and you know the keys used for it. Than you can write the validation, or pass the parameters to .net core pipeline.
you got the JWT from external authority. In this case the authority (in your particular case - Microsoft) knows how to validate the JWT.
Authority will implement the JWT protocol and expose it via a URL. Normally you need to know two things: authority and audience (recipient of the token).
Now good news is that .net core handles the protocol details for you, all you need to do is to set up the authentication pipeline. This is what it boils down to:
services.AddAuthentication()
.AddJwtBearer("schemeName", options =>
{
options.Audience = "your audience";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "your domain",
ValidateAudience = true,
ValidAudience = "your audience",
ValidateIssuerSigningKey = true,
IssuerSigningKeys = jwks, // use "Keys" as JsonWebKeySet or "Key" (below), just one of them
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)), // your encoding etc may differ
RequireSignedTokens = true,
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
ValidAlgorithms = new[] { SecurityAlgorithms.EcdsaSha256, }, // your algorithm may differ
};
})
For details, do some reading on JWT authentication in .net core, e.g. JWT Validation and Authorization in ASP.NET Core. There are a lot of articles on the topic.

ASP .Net Core Access the DistributedRedisCache in the JWTBearerOptions.Events OnTokenValidated Event

I have an ASP .Net Core API Project. In this project I am using JWTBearer Authentication. I am also using the AddDistributedRedisCache feature of the .Net Core Dependency Injection. (Both shown below)
We have a need to blacklist the tokens on occasion (Admin user removing rights, logout, etc) so that these can take immediate effect. Essentially forcing a user to log back in before the next call can be made.
We are adding the JWT Tokens to the redis cache as well as removing them from the client side cache on logout. But a user could (in theory) store the JWT token, and still gain access until the token expires, unless we intercept the call and check it against the blacklist.
How can I access the distributed cache object in the "OnTokenValidated" event in the code below? Do I have to manually create a new connection each time? We are only checking valid tokens, as that will stop invalid requests from even being checked against the blacklist.
Bearer Token Config:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "localhost:5000",
ValidAudience = "localhost:5000",
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(Configuration.GetValue<string>("SigningKey"))),
};
options.Events = new JwtBearerEvents
{
OnTokenValidated = context => {
//context.Fail("User has been logged out");
return Task.CompletedTask;
}
};
});
Redis Cache Config:
services.AddDistributedRedisCache(option =>
{
option.Configuration = Configuration.GetValue<string>("RedisCacheAddress");
option.InstanceName = Configuration.GetValue<string>("RedisCacheInstance");
});
You can access services in DI utilizing the HttpContext available there:
OnTokenValidated = ctx =>
{
var cache = ctx.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
return Task.CompletedTask;
}
GetRequiredService will throw an exception if the service is not found. You can use GetService<T>() if you want the service to be optional.

Multiple Authentication Middlewares ASP.NET Core

I am relatively new to the concept of middlewares. I am aware that a middleware calls the next middleware when it completes.
I am trying to authenticate a request using either Google or my Identity Server. The user can login on my mobile app with google or a local account. However, I can't figure out how to use both authentication middlewares. If I pass the id_token for google, it passes on the first middleware (UseJwtBearerAuthentication) but fails on the second one (UseIdentityServerAuthentication). How can I make it so that it doesn't throw error when it actually passes on at least 1 authentication middleware? For example, if it passes on the first middleware, the second middleware is ignored?
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
Authority = "https://accounts.google.com",
Audience = "secret.apps.googleusercontent.com",
TokenValidationParameters = new TokenValidationParameters()
{
ValidateAudience = true,
ValidIssuer = "accounts.google.com"
},
RequireHttpsMetadata = false
});
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:1000/",
RequireHttpsMetadata = false,
ScopeName = "MyApp.Api"
});
Normally, when an authentication middleware is failed(i don't mean throwing exception), this doesn't affect another successful authentication middleware. Probably your second middleware throws an exception(not a validation failure). First check error message and try to resolve it. If you can't, use AuthenticationFailed event to handle error. In this case your code should be something like below:
app.UseJwtBearerAuthentication(new JwtBearerOptions()
{
// ...
Events = new JwtBearerEvents()
{
OnAuthenticationFailed = async (context) =>
{
if (context.Exception is your exception)
{
context.SkipToNextMiddleware();
}
}
}
});
However, for your scenerio i wouldn't choose your way. I would use only identity server endpoint. For signing with google you can configure identity server like below:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
AutomaticAuthenticate = false,
AutomaticChallenge = false
});
app.UseGoogleAuthentication(new GoogleOptions
{
AuthenticationScheme = "Google",
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
ClientId = "",
ClientSecret = ""
});
app.UseIdentityServer();
Edit
It seems AuthenticationFailed event couldn't be used for IdentityServer4.AccessTokenValidation. I am not sure but if you will use identity server for only jwt token, you can use UseJwtBearerAuthentication for validation.

Resources