I'm using Visual studio code and I'm using dot net core framework for a RestAPI. When I access do a controller with "Authorize" attribute, it should return a 401 request but it doesn't return anything in postman. Just a blank.
I think it should comes from my startup code.
I'll share you my configure method in startup file.
Best thanks for your help. If you can find a solution on internet, just share it (I already look for but... Maybe I didn't type the right keyword.)
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)
{
ConfigureContext(services);
services.AddCors();
services.AddAutoMapper(typeof(Startup));
// configure strongly typed settings objects
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
// configure jwt authentication
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
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;
}
};
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "dotnetcore-api-core",
Version = "v1"
});
});
services.AddScoped<IUserService, UserService>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
app.UseMvc();
app.UseStaticFiles();
app.UseHttpsRedirection();
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();
}
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Security JWT
app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "dotnetcore-api-core V1");
});
}
public void ConfigureContext(IServiceCollection services)
{
// Database injection
services.AddDbContext<UserContext>(options =>
options.UseMySql(Configuration.GetConnectionString("AppDatabase")));
}
}
My controller that doesn't return 401 unauthorized:
[Authorize]
[Route("api/users")]
[ApiController]
public class UserController : ControllerBase
{
private readonly IUserService _userService;
private IMapper _mapper;
public UserController(
IUserService userService,
IMapper mapper)
{
_userService = userService;
_mapper = mapper;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
IEnumerable<User> users = await _userService.GetAll();
if(users == null)
{
return NotFound();
}
return Ok(users);
}
I followed this tutorial -> https://jasonwatmore.com/post/2018/08/14/aspnet-core-21-jwt-authentication-tutorial-with-example-api
An example image in postman : Image example of empty body postman
If we put app. UseMvc() first - then the MVC actions would get in the routing and if they see the Authorize attribute they will take control of its handling and that's why we receives 401 Unauthorized error. If you're using ASP.NET Core 3.0 previews, swap UseMvc with UseRouting .
Authorization in ASP.NET Core is controlled with AuthorizeAttribute and its various parameters. In its most basic form, applying the [Authorize] attribute to a controller, action, or Razor Page, limits access to that component to authenticated users. Now only authenticated users can access the Logout function.
I think your problem is the same. You can add a few lines of code as below (in the Startup.cs file):
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors(pol => pol.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
app.UseAuthentication();
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseStatusCodePages(async context =>
{
if (context.HttpContext.Request.Path.StartsWithSegments("/api"))
{
if (!context.HttpContext.Response.ContentLength.HasValue || context.HttpContext.Response.ContentLength == 0)
{
// You can change ContentType as json serialize
context.HttpContext.Response.ContentType = "text/plain";
await context.HttpContext.Response.WriteAsync($"Status Code: {context.HttpContext.Response.StatusCode}");
}
}
else
{
// You can ignore redirect
context.HttpContext.Response.Redirect($"/error?code={context.HttpContext.Response.StatusCode}");
}
});
app.UseMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseExceptionHandler("/api/errors/500");
app.UseStatusCodePagesWithReExecute("/api/errors/{0}");
// or app.UseStatusCodePagesWithRedirects("~/api/errors/{0}");
app.UseRouting();
...
}
Then, create ErrorController like:
[ApiController]
[Route("api/errors")]
public class ErrorController : Controller
{
[HttpGet("{code}")]
public async Task<IActionResult> Get(int code)
{
return await Task.Run(() =>
{
return StatusCode(code, new ProblemDetails()
{
Detail = "See the errors property for details.",
Instance = HttpContext.Request.Path,
Status = code,
Title = ((HttpStatusCode)code).ToString(),
Type = "https://my.api.com/response"
});
});
}
}
I hope this helps.
Try to move this line to the top of the Configure method:
app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
Eg:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
app.UseAuthentication();
app.UseMvc();
// the rest of you code here
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With