MisterLexisDev
MisterLexisDev

Reputation: 140

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

Upvotes: 6

Views: 18679

Answers (3)

lrfahmi
lrfahmi

Reputation: 120

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.

Upvotes: 9

golobitch
golobitch

Reputation: 1334

Your call is returning 401. It is clearly visible in the postman. Body is ofcourse empty, but if you look a little bit higher and on the right site (in the same line that it is body, cookies, headers tab), you will see Status line that will say 401 Unauthorised. It also shows you how much time did it take for this response and what is the size of the response.enter image description here

Upvotes: 1

Davi Ruiz
Davi Ruiz

Reputation: 120

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
}

Upvotes: 2

Related Questions