Answered by:
Provided token was meant for different claims-based user. 400 bad request error in WebAPI Asp.Net core 3.1 project.

Question
-
User613219449 posted
I tried to create a defense against CSRF attacks based on Microsoft's tutorial. Since I have a Web API project, I use JWT authentication, dividing the level of user access based on their role. This way, I have an error when I try to access a secure controller:
Antiforgery token validation failed. The provided antiforgery token was meant for a different claims-based user than the current user. Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: The provided antiforgery token was meant for a different claims-based user than the current user. at Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet) at Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext) at Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter.OnAuthorizationAsync(AuthorizationFilterContext context)
and 400 Bad Request in Postman where I test my API.
My sample controller:
[Authorize(AuthenticationSchemes = "Bearer", Roles = "Admin")] [ValidateAntiForgeryToken] [HttpPost(Name = "CreatePost")] public async Task<ActionResult<Unit>> Post([FromBody] CreatePost.Command command) { return await _mediator.Send(command); }
My Startup class:
public class Startup { public IConfigurationRoot Configuration { get; private set; } public Startup(IWebHostEnvironment env, IConfiguration configuration) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); if (env.IsDevelopment()) { builder.AddUserSecrets<Program>(); } Configuration = builder.Build(); } public ILifetimeScope AutofacContainer { get; private set; } public void ConfigureServices(IServiceCollection services) { services.AddOptions(); // Add Authentication and configure it var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["TokenKey"])); var jwtAppSettingsOptions = Configuration.GetSection(nameof(JwtIssuerOptions)); services.Configure<JwtIssuerOptions>(options => { options.Issuer = jwtAppSettingsOptions[nameof(JwtIssuerOptions.Issuer)]; options.Audience = jwtAppSettingsOptions[nameof(JwtIssuerOptions.Audience)]; options.SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); }); services.AddAuthentication(opt => { opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; opt.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme; opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(opt => { opt.RequireHttpsMetadata = false; opt.SaveToken = true; opt.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters() { LifetimeValidator = (before, expires, token, param) => { return expires > DateTime.UtcNow; }, ValidateIssuerSigningKey = true, IssuerSigningKey = key, ValidateAudience = false, ValidateIssuer = false, ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; opt.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; var path = context.HttpContext.Request.Path; if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/hubs/chat"))) { context.Token = accessToken; } return Task.CompletedTask; } }; }); services.AddAuthorization(); // Register SignalR services.AddSignalR(); // Data protection services.AddDataProtection() .PersistKeysToDbContext<KeysDataContext>(); // Add asp.net identity services.AddIdentity<AppUser, IdentityRole>((options) => { options.User.RequireUniqueEmail = true; }) .AddEntityFrameworkStores<DataContext>() .AddDefaultTokenProviders(); // Configure asp.net identity services.Configure<IdentityOptions>(opt => { opt.Password.RequireDigit = true; opt.Password.RequiredLength = 6; opt.Password.RequireNonAlphanumeric = false; opt.Password.RequireUppercase = true; opt.Password.RequireLowercase = false; }); // Add fluent validation services.AddControllers() .AddFluentValidation(cfg => { cfg.RegisterValidatorsFromAssemblyContaining<CreatePost>(); }); // CORS policy services.AddCors(opt => { opt.AddPolicy("CorsPolicy", policy => { policy.WithOrigins("https://localhost:5000/") .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); }); }); services.AddAntiforgery(options => { options.HeaderName = "X-XSRF-TOKEN"; options.SuppressXFrameOptionsHeader = false; }); // Swagger config services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); c.CustomSchemaIds(type => { if (type.FullName.EndsWith("+Command") || type.FullName.EndsWith("+Query")) { var parentTypeName = type.FullName.Substring(type.FullName.LastIndexOf(".", StringComparison.Ordinal) + 1); return parentTypeName.Replace("+Command", "Command").Replace("+Query", "Query"); } return type.Name; }); }); services.AddMvc(); } public void ConfigureContainer(ContainerBuilder builder) { // Register custom autofac modules builder.RegisterModule(new StartupModule()); builder.RegisterModule(new DbContextModule()); builder.RegisterModule(new MediatrModule()); builder.RegisterModule(new SecureDataContextModule()); builder.RegisterModule(new AutoMapperModule()); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAntiforgery antiforgery) { // Add custom error hadling with http requests app.UseMiddleware<ErrorHandlingMiddleware>(); app.UseHttpsRedirection(); app.UseHsts(); // Using development tools if (env.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "API V1"); }); } app.UseRouting(); // Implement CORS policy app.UseCors("CorsPolicy"); app.UseAuthentication(); app.Use(next => context => { var tokens = antiforgery.GetAndStoreTokens(context); context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false }); return next(context); }); app.UseAuthorization(); // Add serilog middleware with http context enricher app.UseMiddleware<SerilogMiddleware>(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapHub<GroupChatHub>("/hubs/chat"); }); } }
and decoded JWT:
Tuesday, June 30, 2020 9:17 AM
Answers
-
User475983607 posted
The standard anti-forgery token is used in browser based applications and designed to protect MVC Controllers not Web API. Web API uses CORS to handle cross origin requests from browsers.
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Tuesday, June 30, 2020 10:32 AM