Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why don't my unauthorized controllers return 401 ASP.Net Core?

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

like image 734
MrBB Avatar asked Aug 09 '19 09:08

MrBB


People also ask

How do I fix the 401 unauthorized error in ASP.NET Core?

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 .

How does Authorize attribute work in ASP.NET Core?

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.


2 Answers

I think your problem is the same. You can add a few lines of code as below (in the Startup.cs file):

Option 1:

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();
}

Option 2

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.

like image 82
lrfahmi Avatar answered Nov 14 '22 22:11

lrfahmi


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
}
like image 34
Davi Ruiz Avatar answered Nov 14 '22 23:11

Davi Ruiz