Empty claims from JwtBearer middleware - c#

Using ASP.NET Core 3.1. I want to validate a JWT (and the roles in it).
Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Configuration);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.MetadataAddress = "https://example.com/.well-known/openid-configuration";
});
services.AddScoped<IAuthorizationHandler, MyRoleHandler>();
}
MyRoleHandler
public class MyRoleHandler : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
var claims = context.User.Claims;
return Task.CompletedTask;
}
}
https://example.com/.well-known/openid-configuration
{
"issuer": "https://example.com",
"jwks_uri": "https://example.com/.well-known/openid-configuration/jwks",
"authorization_endpoint": "https://example.com/connect/authorize",
"token_endpoint": "https://example.com/connect/token",
"end_session_endpoint": "https://example.com/connect/endsession",
"check_session_iframe": "https://example.com/connect/checksession",
"device_authorization_endpoint": "https://example.com/connect/deviceauthorization",
"frontchannel_logout_supported": true,
"frontchannel_logout_session_supported": true,
"backchannel_logout_supported": true,
"backchannel_logout_session_supported": true,
"response_types_supported": ["code", "token", "id_token", "id_token token", "code id_token", "code token", "code id_token token"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"code_challenge_methods_supported": ["plain", "S256"],
"upgrade_endpoint": "https://example.com/connect/upgrade",
"end_session_accesstoken_endpoint": "https://example.com/connect/endsession/accesstoken",
"active_session_endpoint": "https://example.com/connect/activesession"
}
claims is empty. What am I doing wrong here? I was under the impression the JwtBearer middleware will configure itself when given the openid-configuration?

I figured out now that the error was in fact that the TokenValidationParameters must be set correctly. I don't exactly know why, but as soon as I add them in any capacity, the claims are correctly decoded.
So now I added them as follows:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opts =>
{
opts.Authority = Env.Get("AUTHORITY_BASE_URL");
opts.TokenValidationParameters = new TokenValidationParameters
{
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidIssuer = Env.Get("ALLOWED_ISSUER"),
ValidateAudience = true,
ValidAudience = Env.Get("ALLOWED_AUDIENCE"),
ClockSkew = TimeSpan.FromSeconds(Env.GetDouble("CLOCK_SKEW")),
RoleClaimType = "authorities"
};
});
And now, in my MyRoleHandler, the claims are correctly decoded an I can check them.

Related

Trying to use JWT Authorization in dotnet 6 Minimal API. What am I doing wrong?

I have a JWT, returned from my authentication API which has the following filled out:
{
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": "sysadmin",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname": "",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname": "",
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "Administrators",
"exp": 1667578481,
"iss": "ISSUER",
"aud": "AUDIENCE"
}
I've validated the JWT at jwt.io, and everything checks out.
In my setup I have :
builder.Services.AddAuthorization();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, opts =>
{
opts.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "ISSUER",
ValidAudience = "AUDIENCE",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(key))
};
});
...
app.UseAuthentication();
app.UseAuthorization();
And I have API endpoints decorated with [Authorize]:
[Authorize]
IResult GetForcast()
{
...
}
When I test it I just get a 401 error. What am I doing wrong?
When you call your service make sure you have a correct Authorization header.
Authorization: Bearer <token>

Fail to validate issuer signing key for JWT

I have an end point which is protected by the [Authorize] attribute. I want to allow access to it, when a token is provided that is signed with the phrase "Super-Secret-Key" For now, I just generate the token with Jwt.io (you can see a screenshot at the end of the post). However, when I visit the end point, just get a 401 Unauthorized, even if I send the encoded token along as a bearer token (using Postman).
What am I missing?
Program.cs
var tokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Super-Secret-Key"))
};
var builder = WebApplication.CreateBuilder(args);
{
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => options.TokenValidationParameters = tokenValidationParameters);
builder.Services.AddAuthorization();
builder.Services.AddControllers();
}
(...)
the issue is that asp.net core tries to validate the token issuer by default that you did not set up on jwt.io. Set ValidateIssuer = false inside TokenValidationParameters:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
ValidateIssuer = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Super-Secret-Key"))
};
});
If you want to specify an issuer to your JWT token you have to add the iss standard claim (Payload data) as specified inside official docs: https://jwt.io/introduction.
Here's an example:
Validation setup:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
ValidateIssuer = true, //Validate issuer
ValidIssuer = "MyIssuer", //Issuer to validate
//ValidIssuers = new List<string>() { "MyIssuer", "MySecondIssuer" }, //You can specify multiple issuers
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Super-Secret-Key"))
};
});
There also are other "standard" (registered) claims that you can use like:
exp: timestamp that identifies the expiration time of the token.
nbf: timestamp that identifies the time before which the JWT
must not be accepted for processing.
aud: audience identifies recipients that the JWT is
intended for.
that you can validate with standard validation options or with custom-written validation logic.
See: https://datatracker.ietf.org/doc/html/rfc7519#section-4.1

Retrieve claims from JWT authentication on ASP.NET Core MVC

I have inherited an ASP.NET Core 5 MVC application that has integrated the authentication of the users with a third-party identity server, WSO2.
The authentication is working fine, but I'm not able to understand how to retrieve the username of the user that has logged in.
Here is the code:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSession();
services.AddControllersWithViews();
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<ITokenService, TokenService>();
services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
//auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
services.AddHttpClient();
}
in the controller I have added:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Administrator,Supervisor")]
Is the name and roles found in the JWT token?
If, so then the issue is that OpenIDConnect and Microsoft do not agree about what the name of the 'name' claim should be.
To address this, you need to tell AddJwtBearer, which claim is the 'name' and 'role' claim using:
.AddMyJwtBearer(opt =>
{
...
opt.TokenValidationParameters.RoleClaimType = "roles";
opt.TokenValidationParameters.NameClaimType = "name";

not check Token validation in Asp core 3

i need to check token Validation with this :
public static void AddJWTAuthnticationInjection(this IServiceCollection services,SiteSetting siteSetting)
{
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
var securityKey = Encoding.UTF8.GetBytes(siteSetting.JwtSetting.SecretKey);
var ValidatePrameters = new TokenValidationParameters
{
//Tlorance for Expire Time and Befor Time of Token .
ClockSkew = TimeSpan.Zero,
RequireSignedTokens = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(securityKey),
// I Need Check Expire Token or Not
RequireExpirationTime = true,
ValidateLifetime = true,
ValidateAudience = true,
ValidAudience = siteSetting.JwtSetting.Audience,
ValidateIssuer = true,
ValidIssuer = siteSetting.JwtSetting.Issuer
};
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = ValidatePrameters;
});
}
and i use this middlware in project :
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseCors(builder => builder
.AllowAnyHeader()
.AllowAnyMethod()
.SetIsOriginAllowed((host) => true)
.AllowCredentials()
);
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
and this is my services :
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddFluentValidation(cfg => cfg.RegisterValidatorsFromAssemblyContaining<CreateRoleValidator>());
services.Configure<SiteSetting>(Configuration.GetSection(nameof(SiteSetting)));
services.AddControllers().AddControllersAsServices();
services.AddContext(Configuration);
services.AddLoginngBehavior();
services.RegisterRedis(Configuration);
services.AddMediatR();
services.AddCors();
services.Injection();
**services.AddJWTAuthnticationInjection(_siteSetting);**
}
but when i send the request with tokenin this controller :
[Authorize]
[Pemission("مدیریت نقش ها")]
public class RoleController : BaseController
{
[HttpGet]
[Authorize]
[Pemission("لیست نقش ها")]
public async Task<ReturnResult<IList<Role>>> GetRoles()
{
var result = await mediator.Send(new GetAllRoleQuery());
if (result.Success)
{
return Ok(result.Result);
}
return BadRequest(result.ErrorMessage);
}
}
when i start the project it got this this service AddJWTAuthnticationInjection but when i send a request it not checked it .
it not checked the token Validation . and show me UnAuthorize . whats the problem ? how can i solve the problem ???
Nothing in your code looks to jump out as misconfigured, however there are a few things that I check when I have tried to troubleshoot similar issues in the past:
Check WWW-Authenticate Response Header
By default asp.net will add a WWW-Authenticate header which can reveal what is failing. It can help you track down the issues (e.g. is the key invalid? Or the audience?). The header value will be something like Bearer error="invalid_token", error_description="The token is expired".
Is the token valid?
Copy and paste your token into jwt.io. Is the expiry what you expect? Check issuer/audience etc.
Check Authentication Events
JwtBearerOptions has an Events property that can be used to hook into different events and can help track down issues. Below is an example of wiring these up, adding a breakpoint or logging from within each event can be very handy.
.AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents {
OnChallenge = context => {
Console.WriteLine("OnChallenge:");
return Task.CompletedTask;
},
OnAuthenticationFailed = context => {
Console.WriteLine("OnAuthenticationFailed:");
return Task.CompletedTask;
},
OnMessageReceived = context => {
Console.WriteLine("OnMessageReceived:");
return Task.CompletedTask;
},
OnTokenValidated = context => {
Console.WriteLine("OnTokenValidated:");
return Task.CompletedTask;
},
};
Turn Off Validation
You have true for all validation events for TokenValidationParameters. Set these to false and then enable each one individually to see which one is causing the issue.
Your code should work It seems your token is invalid. Change some validation parameter values to false like the following code:
var ValidatePrameters = new TokenValidationParameters
{
//Tlorance for Expire Time and Befor Time of Token .
ClockSkew = TimeSpan.Zero,
RequireSignedTokens = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(securityKey),
// I Need Check Expire Token or Not
RequireExpirationTime = true,
ValidateLifetime = false,
ValidateAudience = false,
ValidAudience = siteSetting.JwtSetting.Audience,
ValidateIssuer = false,
ValidIssuer = siteSetting.JwtSetting.Issuer
};
Then check the token's content with the SecurityKey on "jwt.io".
Moreover, if you're using policy-based auth, you should register the "مدیریت نقش ها" policy.

JwtToken AudienceValidator missing audience in IEnumerable<string> audiences

I have an asp net core api where I need a custom validation for the "audience" claim in a JWT token. This is possible via the AudienceValidator delegate (Microsoft.IdentityModel.Tokens) with signature:
bool CustumValidator(IEnumerable<string> audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
The audiences should, as I understand it, contain a list of audiences in the token. The validationParameters contains all the TokenValidationParameters you register at startup. However, for me this parameter is empty.
In the configure method of my startup class if have the following:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime applicationLifetime)
{
//...
// Setup JWT for the application.
var jwtAppSettingOptions = ConfigurationRoot.GetSection(nameof(JwtIssuerOptions));
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
ValidateAudience = true,
//ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)], -- I have tried with both this property and the ValidAudiences one below
ValidAudiences = new[] { jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)] },
ValidateIssuerSigningKey = true,
IssuerSigningKey = _jwtSigningKey,
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
AudienceValidator = DoValidation // <-- my custom validation method, described below
};
//....
}
And then I have this little test method, that for now, does nothing but return true.
private bool DoValidation(IEnumerable<string> audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
var castedToken = securityToken as JwtSecurityToken;
// Do Nothing
return true;
}
As said, this token in the piece above has no value in its audience property, and the IEnumerable<string> audiences is empty as well. I do not understand why. If I let the default validator method be (by not setting a delegate), it is able to see the token's audience and Forbids access if the audience is incorrect. So why does my custom method not get the audience(s) passed in?
Here's how I make the token:
var jwt = new JwtSecurityToken(
_jwtIssuerOptions.Issuer,
String.IsNullOrWhiteSpace(_aud) ? _jwtIssuerOptions.Audience : _aud,
claims,
_jwtIssuerOptions.NotBefore,
_jwtIssuerOptions.Expiration,
_jwtIssuerOptions.SigningCredentials);
Thanks in advance.
I came across this question while trying to find the signature for the AudienceValidator delegate. (Thanks for that!)
The audiences enumerable is populated for me when I handle that delegate. One difference I see is that I do not specify the ValidAudiences parameter. You might try excluding that (assuming you haven't already solved this). Here is an example of my working code:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "MyIssuer",
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(Configuration["SecurityKey"])),
AudienceValidator = validateAudience
};
});

Categories