My task is to do JWT authentication. My application is using .NET 6 and ASP NET MVC. I obtain a JWT access token during server-side authentication using OpenID Connect and then have it automatically added to the request headers for your authorized API calls.
My code in Program.cs:
const string AUTH_COOKIE_NAME = "access_token";
const string AUTH_SCHEMA_NAME = "OPENIDC_JWT";
const string BEARER_AUTH_SCHEMA_NAME = "bearer";
const string OIDC_AUTH_SCHEMA_NAME = "oidc";
builder.Services.AddAuthentication(options => {
options.DefaultScheme = AUTH_SCHEMA_NAME;
options.DefaultAuthenticateScheme = OIDC_AUTH_SCHEMA_NAME;
options.DefaultChallengeScheme = OIDC_AUTH_SCHEMA_NAME;
})
.AddOpenIdConnect(OIDC_AUTH_SCHEMA_NAME, o =>
{
o.MetadataAddress = builder.Configuration["SingleSignOn:MetaData"];
o.Authority = builder.Configuration["SingleSignOn:Authority"];
o.ClientId = builder.Configuration["SingleSignOn:ClientId"];
o.ClientSecret = builder.Configuration["SingleSignOn:ClientSecret"];
o.ResponseType = OpenIdConnectResponseType.Code;
o.SaveTokens = true;
o.Scope.Clear();
o.Scope.Add("openid");
o.Scope.Add("email");
o.CallbackPath = "/signin-oidc";
o.AccessDeniedPath = "/Account/AccessDenied";
o.SignInScheme = BEARER_AUTH_SCHEMA_NAME;
o.Events = new OpenIdConnectEvents
{
OnTokenValidated = async context =>
{
var accessToken = context.SecurityToken as JwtSecurityToken;
if (accessToken != null)
{
// Store the access token in the authentication properties
context.Properties.StoreTokens(new[]
{
new AuthenticationToken { Name = OpenIdConnectParameterNames.AccessToken, Value = accessToken.RawData },
});
}
context.Request.Headers.Append("Authorization", $"Bearer {accessToken.RawData}");
var roleService = context.HttpContext.RequestServices.GetRequiredService<IUserRolesService>();
roleService.ManageUserRoles(context);
},
};
})
.AddJwtBearer(BEARER_AUTH_SCHEMA_NAME, options =>
{
options.MetadataAddress = "https://access-staging.epam.com/auth/realms/plusx/.well-known/openid-configuration";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true, // You shoud set ValidateAudience = true and specify ValidAudience for your application.
ValidAudience = builder.Configuration["SingleSignOn:ClientId"],
ValidateIssuerSigningKey = true,
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
string authCookie = context.Request.Cookies[AUTH_COOKIE_NAME];
// Token will be taken from Authorization header or if header is not set from authentication cookie.
if (!string.IsNullOrEmpty(authCookie))
{
context.Token = authCookie;
}
return Task.CompletedTask;
}
};
})
.AddPolicyScheme(AUTH_SCHEMA_NAME, AUTH_SCHEMA_NAME, options =>
{
options.ForwardDefaultSelector = context =>
{
string authHeader = context.Request.Headers[HeaderNames.Authorization];
string authCookie = context.Request.Cookies[AUTH_COOKIE_NAME];
// If Token presents in header or cookie choose "bearer" schema, in other case use "oidc" schema.
if (!string.IsNullOrEmpty(authHeader) || !string.IsNullOrEmpty(authCookie))
return BEARER_AUTH_SCHEMA_NAME;
return OIDC_AUTH_SCHEMA_NAME;
};
});
builder.Services.AddAuthorization();
// more services
var app = builder.Build();
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();//HTTP Strict Transport Security Protocol is used to be sure that none of your content is still server over HTTP
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseMiddleware<SecurityHeadersMiddleware>();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Admin}/{action=Index}/{id?}");
app.Run();
As you can see from the code, I authenticate to my company via OpenIdConnect, receive a JWT access token and insert it into the headers for further communication with other services in my company via this JWT token. When a client needs to call protected API endpoints, it must include the JWT token in the Authorization header with the Bearer scheme, like this:
Authorization: Bearer your_jwt_token
Trace of the error: the context goes into the services, where, depending on the user’s email, they are assigned claims. Example of the service:
public void ManageUserRoles(TokenValidatedContext context)
{
var user = context.Principal;
if (user.Identity.IsAuthenticated)
{
if (!user.HasClaim(c => c.Type == ClaimTypes.Role))
{
if (!string.IsNullOrEmpty(userEmail) && isAdmin)
{
_logger.LogInformation($"Set the '{RoleNames.Admin}' role to user with email: '{userEmail}'");
identity.AddClaim(new Claim(ClaimTypes.Role, RoleNames.Admin));
}
}
}
My error occurs just after I leave the event OnTokenValidated:InvalidOperationException: The authentication handler registered for scheme 'bearer' is 'JwtBearerHandler' which cannot be used for SignInAsync. The registered sign-in schemes are: OPENIDC_JWT.
am using AddPolicyScheme like handler in case if I have JWT token in header, I am using JWTBearer authentication, validate the token and go into my application on localhost; else if JWT token is null in the header, I must authenticate through my company's Identity provider, receive token, add it to the header, then validate it in AddJwtBearer and authorize into my application.
Also to mention, I have the attribute [Authorize] on all my controllers. And do not offer me to use .AddCookie in authentication service or use cookies-based authentication schemes.
I tried using only Bearer authentication scheme as default:
builder.Services.AddAuthentication(options => {
options.DefaultScheme = BEARER_AUTH_SCHEMA_NAME;
})
after that nothing is happening.
I tried to use different variations of schemes in authentication schemes:
options.DefaultScheme = AUTH_SCHEMA_NAME;
options.DefaultAuthenticateScheme = AUTH_SCHEMA_NAME;
options.DefaultChallengeScheme = AUTH_SCHEMA_NAME;
-- the same error after that: InvalidOperationException: The authentication handler registered for scheme 'bearer' is 'JwtBearerHandler' which cannot be used for SignInAsync. The registered sign-in schemes are: OPENIDC_JWT.
Tried to use only one Default Scheme:
`options.DefaultScheme = AUTH_SCHEMA_NAME;`
After that the same error: InvalidOperationException: The authentication handler registered for scheme 'bearer' is 'JwtBearerHandler' which cannot be used for SignInAsync. The registered sign-in schemes are: OPENIDC_JWT.
Tried also this variant:
builder.Services.AddAuthentication(options => {
options.DefaultScheme = BEARER_AUTH_SCHEMA_NAME;
options.DefaultChallengeScheme = OIDC_AUTH_SCHEMA_NAME;
})
After that the same error: InvalidOperationException: The authentication handler registered for scheme 'bearer' is 'JwtBearerHandler' which cannot be used for SignInAsync. The registered sign-in schemes are: OPENIDC_JWT.
Stack trace:
System.InvalidOperationException: The authentication handler registered for scheme 'bearer' is 'JwtBearerHandler' which cannot be used for SignInAsync. The registered sign-in schemes are: OPENIDC_JWT.
at Microsoft.AspNetCore.Authentication.AuthenticationService.SignInAsync(HttpContext context, String scheme, ClaimsPrincipal principal, AuthenticationProperties properties)
at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Middlewares.SecurityHeadersMiddleware.InvokeAsync(HttpContext context) in Middlewares\SecurityHeadersMiddleware.cs:line 23
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
My task is to do JWT authentication. My application is using .NET 6 and ASP NET MVC. I obtain a JWT access token during server-side authentication using OpenID Connect and then have it automatically added to the request headers for your authorized API calls.
My code in
Program.cs:As you can see from the code, I authenticate to my company via
OpenIdConnect, receive a JWT access token and insert it into the headers for further communication with other services in my company via this JWT token. When a client needs to call protected API endpoints, it must include the JWT token in theAuthorizationheader with the Bearer scheme, like this:Authorization: Bearer your_jwt_tokenTrace of the error: the context goes into the services, where, depending on the user’s email, they are assigned claims. Example of the service:
My error occurs just after I leave the event
OnTokenValidated:InvalidOperationException: The authentication handler registered for scheme 'bearer' is 'JwtBearerHandler' which cannot be used for SignInAsync. The registered sign-in schemes are: OPENIDC_JWT.am using AddPolicyScheme like handler in case if I have JWT token in header, I am using JWTBearer authentication, validate the token and go into my application on localhost; else if JWT token is null in the header, I must authenticate through my company's Identity provider, receive token, add it to the header, then validate it in
AddJwtBearerand authorize into my application.Also to mention, I have the attribute
[Authorize]on all my controllers. And do not offer me to use.AddCookiein authentication service or use cookies-based authentication schemes.I tried using only Bearer authentication scheme as default:
after that nothing is happening.
I tried to use different variations of schemes in authentication schemes:
-- the same error after that: InvalidOperationException:
The authentication handler registered for scheme 'bearer' is 'JwtBearerHandler' which cannot be used for SignInAsync. The registered sign-in schemes are: OPENIDC_JWT.Tried to use only one Default Scheme:
After that the same error:
InvalidOperationException: The authentication handler registered for scheme 'bearer' is 'JwtBearerHandler' which cannot be used for SignInAsync. The registered sign-in schemes are: OPENIDC_JWT.Tried also this variant:
After that the same error:
InvalidOperationException: The authentication handler registered for scheme 'bearer' is 'JwtBearerHandler' which cannot be used for SignInAsync. The registered sign-in schemes are: OPENIDC_JWT.Stack trace: