How to force Blazor Web API so it to map as it was in some previous .NET Framework?
If I have this UserController:
[Route("api/[controller]")]
[ApiController]
public class UserController : Controller
{
private readonly IHttpContextAccessor httpContextAccessor;
private readonly IConfiguration configuration;
public UserController(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
[HttpGet]
[Authorize(AuthenticationSchemes = NegotiateDefaults.AuthenticationScheme)]
[Route("GetUserName")]
public String GetUserName()
{
return httpContextAccessor!.HttpContext!.User?.Identity?.Name?;
}
}
Then what should I do to be able to delete
[Route("api/[controller]")] and [Route("GetUserName")]
and still be able to use same route? .../user/getusername
i want to be able to add new controllers / methods without having to specyfy any [Route("xxx")]. want it wo towk using default controller / action name for each own
I thought that
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
will do it - but it is not... It does not change anything in that case...
Thanks and regards!
Right, I see, you need to add MVC middleware service then, see below
builder.Services.AddMvc(opt =>
{
opt.EnableEndpointRouting = false;
});
...
app.UseMvcWithDefaultRoute();
Full example
Controller
public class UserController : Controller
{
private readonly IHttpContextAccessor httpContextAccessor;
public UserController(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public String? GetUserName()
{
return httpContextAccessor!.HttpContext!.User?.Identity?.Name;
}
}
Program.cs
using Microsoft.AspNetCore.Authentication.Negotiate;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy.
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddHttpContextAccessor();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddMvc(opt =>
{
opt.EnableEndpointRouting = false;
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseMvcWithDefaultRoute();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
Related
I am trying to get IConfiguration in controller api with .NET6 . Ihave this Program.cs:
var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<IConfiguration>(config);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
And i have this controller:
[Route("api/[controller]")]
[ApiController]
public class PeriodsController : ControllerBase
{
IConfiguration conf;
PeriodsController(IConfiguration _conf)
{
conf = _conf;
}
// GET: api/Periods
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
That does not work. How can i get IConfiguration using injection dependency??
I receive this error:
A suitable constructor for type 'xxxx.Controllers.PeriodsController' could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments
You need to make the constructor public:
[Route("api/[controller]")]
[ApiController]
public class PeriodsController : ControllerBase
{
IConfiguration conf;
public PeriodsController(IConfiguration _conf)
{
conf = _conf;
}
// ...
}
So, I am trying to set the "Access-Control-Allow-Credentials", using the HttpContext.Request.Headers.Add("Access-Control-Allow-Credentials", "true"); , but it will only work for my GET requests, but not for my POST ones, and i don't know why.
I've also tried to set those in Startup.cs, as so:
services.AddCors(o => o.AddPolicy("ApiCorsPolicy", builder =>
{
builder
.WithOrigins("My_Web_App")
.AllowCredentials()
.AllowAnyMethod()
.AllowAnyHeader();
}));
And then use it in Configure with app.UseCors("ApiCorsPolicy");
That's the code from my controller, so nothing fancy in here, I think:
[ApiController]
[Route("[controller]")]
[EnableCors("ApiCorsPolicy")]
public class UserController : Controller
{
private readonly IUserService _userService;
public UserController(IUserService userService)
{
_userService = userService;
}
[HttpPost("RegisterUser")]
public void GetTournaments([FromBody] UserDTO user)
{
HttpContext.Request.Headers.Add("Access-Control-Allow-Credentials", "true");
_userService.RegisterUser(user);
}
}
My question is, why I can't put the "Access-Control-Allow-Credentials" header using the Headers.add method( Which works for my GET requests ) ? If that is not the correct approach to it, then how to do it ?
Thanks!
Edit:
I've added all my Startup file, as requested:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("ApiCorsPolicy", builder =>
{
builder
.WithOrigins("http://"MyIpAddressHere"/")
.AllowAnyMethod()
.AllowAnyHeader();
}));
var mapperConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingGeneral());
});
IMapper mapper = mapperConfig.CreateMapper();
services.AddSingleton(mapper);
services.AddSingleton<ITournamentService, TournamentService>();
services.AddSingleton<IUserService, UserService>();
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "my_app", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "my_app v1"));
}
app.UseRouting();
app.UseCors("ApiCorsPolicy");
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Change
.WithOrigins("My_Web_App")
to
.WithOrigins("http://localhost....") //your web app url
//your Url shouldn't end with "/"
and remove from code:
//sometimes it interfers with token if you have
.AllowCredentials()
// you don't need this at all
HttpContext.Request.Headers.Add("Access-Control-Allow-Credentials", "true");
Your UseCors should be after UseRouting but before UseAuthorization.
and remove from your controller:
[EnableCors("ApiCorsPolicy")]
I highly suspect that the culprit is in this:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSNotSupportingCredentials
Credentials are not supported without a correct origin.
I have an Blazor webassembly application which uses IdentityServerJwt AddAuthentication and is working with Hangfire. I am trying to configure Hangfire to allow only users who are admins authorization based on the article here but I am getting an No authentication handler is registered for the scheme 'Bearer' error. What should I add as an AuthenticationSchemes. JwtBearerDefaults.AuthenticationScheme` does not work.
What am I missing?
public partial class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
string handfirepolicyname="HangfirePolicyName";
public void ConfigureServices(IServiceCollection services)
{
...Code removed for brevity
services.AddAuthentication().AddIdentityServerJwt();
services.AddAuthorization(options =>
{
options.AddPolicy(handfirepolicyname, builder =>
{ builder.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme).RequireAuthenticatedUser();
builder.RequireRole("admin");
});
});
var hangfireConnectionstring = "SomeHangfireDatabaseConnectionString";
var mySqlStorageOptions = new MySqlStorageOptions();
var mySqlStorage = new MySqlStorage(hangfireConnectionstring, mySqlStorageOptions);
services.AddHangfire(config => config.UseStorage(mySqlStorage));
services.AddHangfireServer();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ApplicationIdentityDbContext identityDbContext)
{
...Code removed for brevity
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
//UseAuthentication, UseAuthorization should be before UseHangfireDashboard
app.UseHangfireDashboard();
app.UseEndpoints(endpoints =>
{
endpoints.MapHangfireDashboard("/hangfire", new DashboardOptions()
{
Authorization = new List<IDashboardAuthorizationFilter> { }
}).RequireAuthorization(handfirepolicyname);
});
}
Error:
No authentication handler is registered for the scheme 'Bearer'. The registered schemes are: Identity.Application, Identity.External, Identity.TwoFactorRememberMe, Identity.TwoFactorUserId, idsrv, idsrv.external, IdentityServerJwt, IdentityServerJwtBearer. Did you forget to call AddAuthentication().Add[SomeAuthHandler]("Bearer",...)?
by adding
[Authorize(AuthenticationSchemes = "Bearer")]
public class TestController : ControllerBase{}
on top of the controller
I try to define an authorization policy to be applied in all methods of all my controllers. I am trying to follow the guidelines given here, in "Authorization for specific endpoints" subsection to substitute my previous AuthorizeFilter but it does not work.
In my Startup I have:
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute().RequireAuthorization();
});
In ConfigureServices:
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => this.Configuration.Bind("AzureAd", options));
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.AddRequirements(new MyRequirement(MyParams))
.Build();
});
(...)
services.AddTransient<IAuthorizationHandler, MyAuthorizationHandler>();
And I have a Requirement:
public class MyRequirement : IAuthorizationRequirement
{
public EntityType MyParams { get; private set; }
public MyRequirement(MyParams myParams) { MyParams = myParams; }
}
and a Handler:
public class MyAuthorizationHandler : AuthorizationHandler<MyRequirement>
{
private readonly ILogger<MyAuthorizationHandler> logger;
private readonly IHttpContextAccessor httpContextAccessor;
public MyAuthorizationHandler(IHttpContextAccessor httpContextAccessor, ILogger<MyAuthorizationHandler> logger)
{
this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
this.logger = logger;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirement requirement)
{
---> Some things. I don't get here when I debug.
}
}
In my controllers I do NOT put any decorator, because I want to apply this authorization policy to ALL my methods, and that's why I override the DefaultPolicy.
If I debug, I do not stop at the Handler as I expect. Actually, if I put a decorator [Authorize] in the controller, I do stop there but, as I mentioned, I'm trying to avoid having to write this decorator in all the controllers.
Why is is not working? Thank you!
My understanding is that the [Authorize] attribute is required in order to use even the default policy. What I normally do when I need to protect all my endpoints in this way is to create an abstract base controller with this attribute and then have my other controllers inherit from this.
For example:
Base controller
[Authorize]
public abstract class MyBaseController : Controller //use Controller for mvc controller or ControllerBase for api controller
{
//base controller methods and properties
}
Other controllers:
public class MyOtherController : MyBaseController
{
//controller methods and properties
}
I finally solved it. In ConfigureServices in startup:
services.AddAuthorization(options =>
{
options.AddPolicy(
"UserIsRegistered",
new AuthorizationPolicyBuilder()
.AddRequirements(new RegistrationRequirement())
.Build());
});
Then I defined the RegistrationRequirement:
public class RegistrationRequirement : IAuthorizationRequirement
{
}
Then I defined the RegistrationAuthorizationHandler
public class RegistrationAuthorizationHandler : AuthorizationHandler<RegistrationRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RegistrationRequirement requirement)
{
---> LOGIC I WANT TO CHECK
if (WHATEVER)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
and finally in Configure in the startup again:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireAuthorization("UserIsRegistered");
});
To sum up, my main problem it was using MapDefaultControllerRoute instead of MapControllers...
Try this:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddTransient<IAuthorizationHandler, MyAuthorizationHandler>();
services.AddControllersWithViews(config =>
{
var policy = new AuthorizationPolicyBuilder()
.AddRequirements(new MyRequirement(MyParams))
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});
services.AddDbContext<MvcProj3Context>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MvcProj3Context")));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
I´m working on a ASP.NET Core Web API. I´m using the newest version 3.0.0-preview4.19216.2.
I have the problem, that my API-Controller ignores the Authorize-Attribute but on another controller the Attribute works fine.
[Route("api/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes =JwtBearerDefaults.AuthenticationScheme)]
public class SuppliersController : ControllerBase
{
[HttpGet("GetAll")]
public IActionResult GetAll()
{
var companyId = int.Parse(User.Claims.FirstOrDefault(c => c.Type == "Company_Id").Value); // throws nullreference exception
return Ok();
}
}
But on another controller I have something similar but there the attribute works as expected
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class UsersController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
var test = User.Claims.FirstOrDefault(c => c.Type == "Company_Id").Value;
}
}
In the user controller works everything fine.
I also tried it in the SupplierController without the
AuthenticationSchemes
but no different.
This is my AddAuthentication in the Startup.cs
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>();
var userId = int.Parse(context.Principal.Identity.Name);
var user = userService.GetById(userId);
if (user == null)
{
// return unauthorized if user no longer exists
context.Fail("Unauthorized");
}
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
Console.WriteLine(context);
return Task.CompletedTask;
},
OnMessageReceived = context =>
{
return Task.CompletedTask;
}
};
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
here is my complete startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
AuthenticationService.ConfigureSchoolProjectAuthentication(services, appSettingsSection);
DependencyInjectionService.Inject(services);
services.AddMvcCore()
.AddNewtonsoftJson();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseAuthentication();
app.UseRouting();
app.UseEndpoints(routes =>
{
routes.MapControllers();
});
}
}
The strange thing is, when my SupplierController is called my authorization logic is not called (checked it with debugger) and when I call my UserController the logic is executed.
I think this is the reason why the claim is null. But why is the logic not called when the controller have a authorization attribute?
It´s seem like my authentication doesn't work entirely because I can access all my controller simply by using no authentication in Postman. What I´m doing wrong here?
Ok I found the anwser in this blog post ASP.NET Core updates in .NET Core 3.0 Preview 4
I have to change the order of my authentication registration from
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseAuthentication();
app.UseRouting();
app.UseEndpoints(routes =>
{
routes.MapControllers();
});
}
to
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(routes =>
{
routes.MapControllers();
});
}
So this solve my problem.