.NET Core Authorization - Constant 403 on JWT Bearer - c#

I'm attempting to authorize requests to my API which bear a JWT token attached to it, however, none of the tutorials, blog posts, and documentation have helped avoiding a constant 403 - Unauthorized error.
This is the -skimmed- current configuration:
Class which generates the token: TokenManagement.cs:
// Add the claims to the token
var claims = new[] {
new Claim(JwtRegisteredClaimNames.Sub, credentials.Username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim("claimName", "claimValue")
};
Configuring the services: Startup.cs - ConfigureServices():
services.Configure<GzipCompressionProviderOptions>(options => options.Level = System.IO.Compression.CompressionLevel.Optimal);
services.AddResponseCompression();
services.AddAuthentication()
.AddJwtBearer(config => {
config.RequireHttpsMetadata = false;
config.SaveToken = true;
config.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = "Issuer",
ValidAudience = "Audience",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(symmetricKey))
};
});
services.AddAuthorization(options => {
options.AddPolicy("myCustomPolicy", policy => {
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
policy.RequireClaim("claimName", "claimValue");
});
});
services.AddMvc();
General Configuration: Startup.cs - Configure():
app.UseAuthentication();
app.Use(async (context, next) => {
await next();
if (context.Response.StatusCode == 404 &&
!Path.HasExtension(context.Request.Path.Value)) {
context.Request.Path = "/index.html";
await next();
}
});
app.UseMvc();
app.UseResponseCompression();
app.UseDefaultFiles();
app.UseStaticFiles();
Controller which should be authorized: ActionsController.cs:
[Authorize(Policy = "myCustomPolicy")]
[Route("api/[controller]")]
public class ActionsController : Controller
Any request I send to the server (which carries a JWT token with the proper claim), returns as a 403.
Any methods which have the [AllowAnonymous] attribute, work just fine.
Is there a way to -at least- debug and see what's going on?

I found out that some claim types changed to different values from my identity server config.
for example , In my Identity Server i am using role claim type:
UserClaims = new []
{
JwtClaimTypes.Role , user.role // "JwtClaimTypes.Role" yield "role"
};
But when i debuged my web api , the role claim type has changed to (see my snapshot below, under watch section):
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
Solution:
To "workaround" (is this desired behavior?) the issue, you need to check your claim type
value are planning use in web api, and use the correct claim type value in your policy.
services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdmin", policy =>
{
//policy.RequireClaim(IdentityModel.JwtClaimTypes.Role, "Admin"); // this doesn't work
policy.RequireClaim(ClaimTypes.Role, "Admin"); // this work
});
});
my web api debug snapshot:

Try to enable CORS in Startup.cs File
public void ConfigureAuth(IAppBuilder app) {
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
// Rest of code
}

Related

OAuth Implementation in ASP.NET Core using Swagger

I want to implement OAuth in my web application and for that I added the following code in my startup.cs
public static IServiceCollection AddSwaggerDocumentation(this IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "CombiTime API v1.0", Version = "v1" });
c.AddSecurityDefinition("OAuth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri("http://localhost:4200/login"),
TokenUrl = new Uri("http://localhost:4200/connect/token")
}
}
});
c.OperationFilter<AuthorizeOperationFilter>();
c.AddSecurityRequirement(new OpenApiSecurityRequirement{
{
new OpenApiSecurityScheme{
Reference = new OpenApiReference{
Id = "Bearer", //The name of the previously defined security scheme.
Type = ReferenceType.SecurityScheme
}
},new List<string>()
}
});
});
return services;
}
public static IApplicationBuilder UseSwaggerDocumentation(this IApplicationBuilder app)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Versioned API v1.0");
c.DocumentTitle = "Title Documentation";
c.DocExpansion(DocExpansion.None);
c.RoutePrefix = string.Empty;
c.OAuthClientId("combitimeapi_swagger");
c.OAuthAppName("Combitime API");
c.OAuthUsePkce();
});
return app;
}
and the AuthorizeOperationFilter Code is as follows :
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
// Since all the operations in our api are protected, we need not
// check separately if the operation has Authorize attribute
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[
new OpenApiSecurityScheme
{
Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "oauth2"}
}
] = new[] {"combitimeapi"}
}
};
}
By using this code, I get an "Authorize" button on my swagger UI and when I click that button I am redirecting to my login page(front end based on angular). So I gave my AuthorizationUrl as http://localhost:4200/login and then when I am redirected to login page, I login with valid credentials, I have used jwt token for login and for that I added the following code in my startup.cs
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
I want to redirect back to the swagger UI after I login with valid credentials but the problem is that I am being redirected to the dashboard after I login. Please help me or let me know what I am doing wrong.
The url that is being formed after I am redirected to login page from swagger is :
http://localhost:4200/login?response_type=code&client_id=combitimeapi_swagger&redirect_uri=http:%2F%2Flocalhost:61574%2Foauth2-redirect.html&state=V2VkIEZlYiAxNyAyMDIxIDIyOjU3OjQ2IEdNVCswNTMwIChJbmRpYSBTdGFuZGFyZCBUaW1lKQ%3D%3D&code_challenge=mT0amBTJgczCZmNSZAYVfjzzpaTiGb68XlyR3RNHuas&code_challenge_method=S256
My front-end is running on port 4200.
My swagger is running on port 61574.
But I am not being redirected to swagger UI after putting in valid credentials
Please help me.
First, let me add some details to your picture:
You have two applications, one with API (based on ASP.NET Core) and one with frontend UI (Angular, but it doesn't matter), and, it's important, with authorization/authentication functions.
You use .NETCore 3.1
You configure an authorization for swagger that means any call from swagger UI page will use given authorization parameters.
So, for API application we have to add a class that has helper methods configuring our swagger:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddSwaggerDocumentation(this IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "CombiTime API v1.0", Version = "v1" });
c.AddSecurityDefinition(
"oauth2",
new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri("https://lvh.me:4201/connect/authorize"),
TokenUrl = new Uri("https://lvh.me:4201/connect/token"),
Scopes = new Dictionary<string, string> {
{ "combitimeapi", "Demo API" }
}
}
}
});
c.OperationFilter<AuthorizeOperationFilter>();
c.AddSecurityRequirement(
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme{
Reference = new OpenApiReference{
Id = "oauth2", //The name of the previously defined security scheme.
Type = ReferenceType.SecurityScheme
}
},
new List<string>()
}
});
});
return services;
}
public static IApplicationBuilder UseSwaggerDocumentation(this IApplicationBuilder app)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Versioned API v1.0");
c.DocumentTitle = "Title Documentation";
c.DocExpansion(DocExpansion.None);
c.RoutePrefix = string.Empty;
c.OAuthClientId("combitimeapi_swagger");
c.OAuthAppName("Combitime API");
c.OAuthScopeSeparator(",");
c.OAuthUsePkce();
});
return app;
}
}
Please, pay attention to the AuthorizationUrl property and to the TokenUrl property. The AuthorizationUrl property should be pointed to our OAuth2 server authorization endpoint. Please, keep in mind that authorization endpoint and logon page are different endpoints. We could get all-known endpoints for our frontend application by visiting the url: https://lvh.me:4201/.well-known/openid-configuration in case our application uses ASP.NET Core with IdentityServer.
Next, Startup.cs of our API application should contain:
public void ConfigureServices(IServiceCollection services)
{
// ... some your code ...
services.AddSwaggerDocumentation();
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication("Bearer", options =>
{
options.ApiName = "combitimeapi";
options.Authority = "https://lvh.me:4201";
});
// ... some your code ...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... some your code ...
app.UseSwaggerDocumentation();
app.UseRouting();
app.UseAuthorization();
// ... some your code ...
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Please, do not forget to add attribute [Authorize] to all your controllers, because your AuthorizeOperationFilter assumes that's done.
Let's look for required changes for our frontend & authorize part. You should configure some certain things, like:
CORS policy
Awailable API clients (one is your Angular UI and another one is API application)
Awailable API resources
Authentication & authorization methods
The class Startup.cs should contain:
public void ConfigureServices(IServiceCollection services)
{
// ... some your code ...
services.AddCors(policies => {
policies.AddDefaultPolicy(builder => {
builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin();
});
});
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
options.Clients.AddIdentityServerSPA("forntend", cfg => {});
options.Clients.AddNativeApp("combitimeapi_swagger", cfg => {
cfg
.WithRedirectUri("https://lvh.me:5001/oauth2-redirect.html")
.WithScopes("combitimeapi");
});
options.ApiResources.AddApiResource("combitimeapi", cfg => {
cfg.WithScopes("combitimeapi");
});
})
.AddApiResources();
services
.AddAuthentication(
x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddIdentityServerJwt();
// ... some your code ...
}
I use here .AddIdentityServerJwt() instead of your's .AddJwtBearer(...) because I don't have your keys and other specific options.
The frontend application is configured to use ports 4201 for HTTPS and 4200 for HTTP, the API application is configured to use ports 5001 for HTTPS and 5000 for HTTP.
Now you can run both applications and go to the page https://lvh.me:5001/index.html and press the button 'Authorize' to get a dialog like:
Enter you secret, mark scope and press 'Authorize' and, after you authenticate yourself you will get:
If you do not get a successful result, please check log of the frontend application, usually it contains error that could help you to find out a problem.
Hope text above will help you.
If you look at the OAuth Web-site the case is described as Per-Request Customization
Per-Request Customization
Often times a developer will think that they need to be able to use a
different redirect URL on each authorization request, and will try to
change the query string parameters per request. This is not the
intended use of the redirect URL, and should not be allowed by the
authorization server. The server should reject any authorization
requests with redirect URLs that are not an exact match of a
registered URL.
If a client wishes to include request-specific data in the redirect URL, it can > instead use the “state” parameter to store data that will be included after the > user is redirected. It can either encode the data in the state parameter itself, or use the state parameter as a session ID to store the state on the server.
I hope that helps you in your quest.
Source: https://www.oauth.com/oauth2-servers/redirect-uris/redirect-uri-registration/
There may be more than one problem with the Startup code, more properly in the AddSwaggerGen.
Configuration of the Identity Provider:
Independently of the redirect, are you able to get an access token, or are you getting some kind of error, for example in the request or in the Identity Provider itself?
Please note that the client configuration that you provide in Swagger must match the configuration in the Identity Provider. You seem to be following Scott Brady's example; we can observe that all his Swagger's startup configuration follows the information he has in the Identity Server (here).
Set the token in the calls to the API:
Moreover, even if you are getting the token, I think you are not setting it in the subsequent calls from Swagger to the API itself.
The AddSecurityDefinition and the AddSecurityRequirement or the AuthorizeOperationFilter typically mention at least one scheme with the same identifier, since the first method defines the way that Swagger is authenticating and the second/third define the way that the calls to the API are authenticated (so, they must reference each other). However, you are using different IDs in all the three methods - "OAuth2", "Bearer" and "oauth2" -, so none of them is linked.
I don't fully know your application, but I believe you could actually be using only one of the AddSecurityRequirement or the AuthorizeOperationFilter, since they are both specifying security requirements. The most important would be to reference the ID of the SecurityDefinition (in your case, "OAuth2").
Scott's example, in fact, only uses the AuthorizeCheckOperationFilter and uses the same ID for the OpenApiSecurityScheme that was previously registered in the AddSecurityDefinition - in his case, "oauth2", but any name/string could be used.

Blazor WebAssembly 401 Unauthorized even when I am authorized

I am using Blazor WebAssembly Asp.Net Core hosted PWAand integrated the AspNetCore.Identity into it. I created the AuthenticationStateProvider in the Client-Side and now I want to allow the user access to a controller where he needs to be authorized.
I have tested via postman, the users were been created and stored in DB as aspnetusers with the right credentials. The Login/Account Controller work as I wanted it.
When the user is authorized it tells this exception in the browser when accessing the authorized controller request:
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Response status code does not indicate success: 401 (Unauthorized).
System.Net.Http.HttpRequestException: Response status code does not
indicate success: 401 (Unauthorized).
Startup.cs (ConfigureServices-Method):
...
serviceCollection.AddDbContext<SQLiteTestDbContext>(options =>
{
options.UseSqlite(config["ConnectionStrings:SQLiteTestConnection"]);
});
serviceCollection.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<SQLiteTestDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["JwtIssuer"],
ValidAudience = Configuration["JwtAudience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtSecurityKey"]))
};
});
services.AddHttpContextAccessor();
services.Configure<IdentityOptions>(options =>
options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);
...
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseAuthentication();
app.UseAuthorization();
...
}
Program.cs Client-Side
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Logging.SetMinimumLevel(LogLevel.Warning);
//Registering Shared-Library models
builder.Services.AddScoped<ObjectModel>();
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, ApiAuthenticationStateProvider>();
builder.Services.AddScoped<IAuthService, AuthService>();
//Registered BlazorContextMenu Service
builder.Services.AddBlazorContextMenu();
//Registering FileReader service, for image upload -> Azure
builder.Services.AddFileReaderService(options => options.UseWasmSharedBuffer = true);
builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();
}
My Controller with authorize attribute:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Route("api/[controller]")]
[ApiController]
public class ObjectController : ControllerBase
{
....
Note:
When your user tries to access a protected (annotated with the Authorize attribute) page on the client he should login first or register.
In order to register, he should be redirected to an Account Controller where you should create a new user, and add it to the database (You said you " integrated the AspNetCore.Identity into it"), which is fine...and should be used to authenticate and verify the user's identity. You account controller should also produce a Jwt Token that should be passed to the client app, and stored in the local storage.
Now, whenever your user tries to access protected resources on your Web Api endpoints, you should retrieve the Jwt Token from the local storage, and add it to the request header. If you do so, the Unauthorized response would be something of the past.
Custom AuthenticationStateProvider can be a good place from which you can manage storing the Jwt Token in the local storage and retrieving it for outbound HTTP request calls.
Here's some sample code to clarify what you should do:
#code {
WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
var token = await TokenProvider.GetTokenAsync();
forecasts = await Http.GetJsonAsync<WeatherForecast[]>(
"api/WeatherForecast",
new AuthenticationHeaderValue("Bearer", token));
}
}
Note: TokenProvider is a custom AuthenticationStateProvider that defines a method called GetTokenAsync that provides (reading the Jwt Token from the local storage and passing it to the calling code) the Jwt Token
Hope this helps...
In case of Linux App Service in combination with ID Server the Authority needs to be set according to Microsoft documentation: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity-api-authorization?view=aspnetcore-5.0#azure-app-service-on-linux-1
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
options.Authority = "{AUTHORITY}";
});
Example: options.Authority = "https://contoso-service.azurewebsites.net";

ASP.NET Core 3.1: Web API identity sign in

I am creating CookieAutentecation signin for my Web API.
I have read and followed the official article here and I have done everything correctly as far as I am concerned.
But when I put breakpoints in my controllers and inspect HttpContext.User, everything is always null, no Username, no claims, nothing.
What else do I need to make this work? Are additional steps needed for Web API vs MVC app?
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, act => {
act.LoginPath = "/api/login";
act.AccessDeniedPath = "/api/login";
act.SlidingExpiration = true;
});
services.AddControllers();
services.AddServices(); // <- Own app domain services
services.AddDataAccess(); // <- Own app domain data access
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors(
options => options.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
api/login
var user = new SecurityUser()
{
UserID = 123,
CompleteName = "Test user",
FirstName = "Test",
Email = "test.user#123.com"
};
var identity = user.ToClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, 123);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties()
{
AllowRefresh = true,
ExpiresUtc = DateTime.UtcNow.AddDays(7),
IsPersistent = true,
});
ToClaimsIdentity extension method:
public static ClaimsIdentity ToClaimsIdentity(this SecurityUser user, string authenticantionType, int auditUserID)
{
var claims = new List<Claim>()
{
new Claim(ClaimTypes.NameIdentifier, user.UserID.ToString()),
new Claim(ClaimTypes.Email, user.Email),
new Claim(ClaimTypes.Name, user.FirstName),
new Claim(SecurityUserClaimTypes.AuditUserID, auditUserID.ToString())
};
var identity = new ClaimsIdentity(claims, authenticantionType);
return identity;
}
Any help would be greatly appreciated.
Edit - This is what I am taking about 👇
Thanks for your help guys!
I finally realised it was a client thing, I did three things:
CORS was an issue, in my .UseCors method call my my Api I allowed credentials:
.AllowCredentials()
My client app in using Blazor, I found this article here which told me I needed to set the http request configuration to include credentials, so in my client side app startup.cs:
WebAssemblyHttpMessageHandlerOptions.DefaultCredentials = FetchCredentialsOption.Include;
I am using Http not Https on my local, and Chrome was complaining about SameSite, so im my Api StartUp.cs, where I call AddAuthentication...AddCookie I added this:
options.Cookie.SameSite = SameSiteMode.Unspecified;
I don't fully understand the SameSite... and I have also come across JSON Web Tokens (JWT).
But I'm not interested, as long as it's working. ;-)

Accessing dotnetcore middleware AFTER a JWT Token is validated

I am using JWT bearer authentication, configured as follows.
My problem is that the middleware is executing before the token is validated.
How do I configure the middleware to run afterwards?
services.AddAuthentication()
.AddCookie(_ => _.SlidingExpiration = true)
.AddJwtBearer(
_ =>
{
_.Events = new JwtBearerEvents
{
// THIS CODE EXECUTES AFTER THE MIDDLEWARE????
OnTokenValidated = context =>
{
context.Principal = new ClaimsPrincipal(
new ClaimsIdentity(context.Principal.Claims, "local"));
return Task.CompletedTask;
}
};
_.RequireHttpsMetadata = false;
_.SaveToken = false;
_.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = this.Configuration["Tokens:Issuer"],
ValidAudience = this.Configuration["Tokens:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this.Configuration["Tokens:Key"])),
};
});
I am attempting to add middleware into the pipeline that accesses the current user. This code unfortunately executes BEFORE the token is validated. How do I make it execute afterwards?
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentityServer();
app.UseAuthentication();
app.Use(async (httpContext, next) =>
{
// THIS CODE EXECUTES BEFORE THE TOKEN IS VALIDATED IN OnTokenValidated.
var userName = httpContext.User.Identity.IsAuthenticated
? httpContext.User.GetClaim("email")
: "(unknown)";
LogContext.PushProperty("ActiveUser", !string.IsNullOrWhiteSpace(userName) ? userName : "(unknown)");
await next.Invoke();
});
It looks like you've found a good solution to your problem but I thought I'd add an answer to explain the behavior you're seeing.
Since you have multiple authentication schemes registered and none is the default, authentication does not happen automatically as the request goes through the pipeline. That's why the HttpContext.User was empty/unauthenticated when it went through your custom middleware. In this "passive" mode, the authentication scheme won't be invoked until it is requested. In your example, this happens when the request passes through your AuthorizeFilter. This triggers the JWT authentication handler, which validates the token, authenticates and sets the Identity, etc. That's why (as in your other question) the User is populated correctly by the time it gets to your controller action.
It probably doesn't make sense for your scenario (since you're using both cookies and jwt)... however, if you did want the Jwt authentication to happen automatically, setting HttpContext.User for other middleware in the pipeline, you just need to register it as the default scheme when configuring authentication:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
based on #leppie's comment, here is a solution that works.
public class ActiveUserFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
var userName = context.HttpContext.User.Identity.IsAuthenticated
? context.HttpContext.User.GetClaim("email")
: "(unknown)";
using (LogContext.PushProperty("ActiveUser", !string.IsNullOrWhiteSpace(userName) ? userName : "(unknown)"))
await next();
}
}
Inserted as follows...
services.AddMvc(
_ =>
{
_.Filters.Add(
new AuthorizeFilter(
new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme,
IdentityConstants.ApplicationScheme)
.RequireAuthenticatedUser()
.Build()));
_.Filters.Add(new ActiveUserFilter());
...

Asp.net Core Authorize user with Policy

I am trying to authorize an user with a bearer token send from the request header.
I added this code in startup file of resource server.
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
Here is my method in controller.
[Authorize("Bearer")]
[HttpGet]
[Route("list")]
public IEnumerable<Products> List()
{
string Authorization = Request.Headers["Authorization"];
}
Application showing me error 401 Unauthorized even if i had token
I am sending this Token in the header request
Authorization:Bearer "xyz"
To work with Bearer token you have to add the following code on your Configure method
public void Configure(IApplicationBuilder app) {
app.UseJwtBearerAuthentication(options => {
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
options.Audience = "OAuth:Audience";
options.Authority = "OAuth:Authority";
options.ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
metadataAddress: options.Authority + ".well-known/openid-configuration",
configRetriever: new OpenIdConnectConfigurationRetriever(),
docRetriever: new HttpDocumentRetriever() { RequireHttps = false });
});
}
You will also need a middleware to handle the authorization process.
Have a look at AspNet.Security.OpenIdConnect.Server and OpenIddict

Categories

Resources