6

I have this web api project in NET 6. In this project, I have a authentication controller and I have an endpoint which authenticate based on username and password.

[Route("api/[controller]")]
[ApiController]
public class AuthenticationController : ControllerBase
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly IdbContext _dbContext;

    public AuthenticationController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, IdbContext _dbContext)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _dbContext = dbContext;
    }

    [AllowAnonymous]
    [HttpPost]
    public async Task<IActionResult> PasswordSignInAsync([FromBody] LoginRequest loginRequest)
    {         
        if (ModelState.IsValid)
        {
            var result = await _signInManager.PasswordSignInAsync(loginRequest.Username, loginRequest.Password, isPersistent: false, lockoutOnFailure: false);
            if (result.Succeeded)
            {
                var armsUser = _dbContext.Users
                    .FirstOrDefault(u => u.Email == loginRequest.Username);

                if (armsUser == null) {
                    await _signInManager.SignOutAsync();
                    return BadRequest();
                }

                ApplicationUser user = await _userManager.FindByEmailAsync(loginRequest.Username);

                if (user == null)
                {
                    return NotFound($"Unable to load user with username '{loginRequest.Username}'.");
                }

                user.LastSignInDate = DateTime.Now;

                await _userManager.UpdateAsync(user);

                await _signInManager.RefreshSignInAsync(user);

                return Ok("Authenticated!");
            }
            await _signInManager.SignOutAsync();
            return BadRequest("Invalid username or password.");
        }
        return BadRequest(ModelState);
    }
}

and this is the protected resources.

[Route("api/[controller]")]
[ApiController]
public class MarketingEventController : ControllerBase
{        
    private readonly IMediator _mediator;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="mediator"></param>
    public MarketingEventController(IMediator mediator)
    {     
        _mediator = mediator;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
    public async Task<ActionResult> GetLatest()
    {
        try
        {
            var query = new GetLatestQuery();
            var result = await _mediator.Send(query);
            return Ok(result);

        }
        catch (Exception ex)
        {
            return Problem(ex.Message);
        }
    }
}

and some related code in program.cs

...

app.UseAuthentication();
app.UseAuthorization();


app.MapControllers().RequireAuthorization();

If I am authenticated, I get 200 status code as expected. But If I am not authentication and call /api/MarketingEvent, why I get 404 status code instead of 401 status code? Even if I decorate the marketing controller with [Authorize], it still behaves the same.

enter image description here

enter image description here

3
  • 2
    It may be a middleware magic, plz try stackoverflow.com/questions/58608536/… Commented Jun 15, 2022 at 7:29
  • 2
    Could you please show how you configure Authentitication and Authorization, i.e. AddAuthentication and AddAuthorization calls in your Startup? Also, RequireAuthorization registers a convention to apply [Authorize] to all controllers so doing it manually should lead to no difference. Additionally, I would like to inquire regarding framework - you mention .NET 6 and tag is ASP.NET. I presume this relates to ASP.NET Core, not ASP.NET 4. Commented Jun 15, 2022 at 10:35
  • 1
    Have you checked this question here? stackoverflow.com/questions/69169310/… Commented Jun 18, 2022 at 9:32

3 Answers 3

1

DefaultChallengeScheme may redirecting to a page that does not exist, which could cause the 404.

Try setting the default challenge to the Jwt schema which returns not authorized.

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
       options.DefaultAuthenticateScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
       options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddIdentityServerAuthentication(options =>
    {
      options.Authority = "http://localhost:60415";
      options.ApiName = "mCareApi";
      options.RequireHttpsMetadata = false;
    });
}
Sign up to request clarification or add additional context in comments.

1 Comment

I also think that something is wrong with authentication. There's probably an internal redirect to a login page that does not exist. In some older project I'm using learn.microsoft.com/en-us/dotnet/api/… to specify the path. So maybe the redirect to the login page or an error page leads to a 404
1

I don't know how have you registered your swagger service, but try to use something like that:

services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })

Also, Decorate your endpoint with the following attribute

[ProducesResponseType(StatusCodes.Status401Unauthorized)]

and also, if you want to avoid making this to all your action methods, you have 2 options:

  1. you can create a base controller and decorate it with that attribute. Then, make all your controllers inherit from that base controller.
  2. you can add a global filter in your program.cs. something like that
services.AddMvc(options =>
{
    options.Filters.Add(new Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute(typeof(ApiError), 400));
    options.Filters.Add(new Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute(typeof(ApiError), 401));
});

Comments

0

In each GET api/MarketingEvent/ request ASP.NET Core Routing will try to match incoming request with an Action method in your controller. In this case you haven't specified any Action method so it will look for the default Action method which is Index. This is why it is returning 404 Not Found.

I assume you want to call GetLatest action method so you need to decorate GetLatest action method with an empty [Route("")] attribute. Your method will look like this:

/// <summary>
/// 
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult> GetLatest()
{
    try
    {
        var query = new GetLatestQuery();
        var result = await _mediator.Send(query);
        return Ok(result);

    }
    catch (Exception ex)
    {
        return Problem(ex.Message);
    }
}

1 Comment

If I am authenticated, I get 200 status code as expected. read this line..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.