0

In ASP.NET Core-6 Web API project, I am implementing Basic Authentication. I have this code:

Model:

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
}

Service:

public interface IUserService
{
    public bool isUser(string username, string password);
}

public class UserService : IUserService
{
    private readonly ApplicationDbContext _dbContext;        
    public UserService(
        ApplicationDbContext dbContext,
        )
    {
        _dbContext = dbContext;
    }

    public bool isUser(string username, string password)
    {
        var userFind = _dbContext.User.Where(u => u.Username == username && u.Password == password);
        if (userFind.Any()) return true;
        else return false;
    }
}

Then I have this Basic Auth Handler code in the helpers.

BasicAuthHandler:

public class BasicAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    private readonly IUserService _repository;
    public BasicAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
                            ILoggerFactory loggerFactory,
                            UrlEncoder urlEncoder,
                            ISystemClock systemClockm,
                            IUserService repository)
        : base(options, loggerFactory, urlEncoder, systemClockm)
    {
        _repository = repository;
    }
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        if (!Request.Headers.ContainsKey("Authorization")) 
            return AuthenticateResult.Fail("No contains header");

        bool result = false;

        try
        {
            var AuthHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
            var credentialBytes = Convert.FromBase64String(AuthHeader.Parameter);
            var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
            //get parameters in array format
            var username = credentials[0];
            var password = credentials[1];
            result = _repository.isUser(username, password);
        }
        catch(Exception)
        {
            return AuthenticateResult.Fail("Some ERROR");
        }
        if (!result)
            return AuthenticateResult.Fail("Error User Name or Password ");

        var claims = new Claim[]
        {
            new Claim(ClaimTypes.NameIdentifier, "id"),
             new Claim(ClaimTypes.Name, "user")
        };
        var identity = new ClaimsIdentity(claims, Scheme.Name);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, Scheme.Name);
        return AuthenticateResult.Success(ticket);
    }
}

Program.cs:

builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddAuthentication("BasicAuthentication")
    .AddScheme<AuthenticationSchemeOptions, BasicAuthHandler>("BasicAuthentication", null);

When I run the application, the page was blank, and I got this error in the log file:

BasicAuthentication was not authenticated. Failure message: No contains header

Which indicates that it stops here:

if (!Request.Headers.ContainsKey("Authorization")) 
    return AuthenticateResult.Fail("No contains header");

bool result = false;

How do I resolve it?

Thanks

11
  • Have you confirmed that the header is included in the request (on the client side)? Commented Oct 11, 2022 at 2:03
  • @ProgrammingLlama - Can you explain? The error occurs as soon as I load the application Commented Oct 11, 2022 at 2:06
  • What is there to explain? I don't follow. If the client doesn't send an Authorization header then the header won't exist on the server... Commented Oct 11, 2022 at 2:08
  • 1
    Perhaps you want to add Response.Headers.Add("WWW-Authenticate", "Basic"); above the if (!Request.Headers.ContainsKey("Authorization")) line so that the browser's native login dialog will show for the site? Otherwise it sounds like you're not allowing anonymous access to the login page (if that's what you have). Commented Oct 11, 2022 at 2:11
  • 1
    Browsers work like this: Attempt to load a page. If the server responds with HTTP 401 UNAUTHORIZED, the browser will attempt to authenticate based on the server response's header WWW-Authenticate contents. If WWW-Authenticate is Basic then the browser will ask the user for username and password. So you must make sure that you always respond with 401 UNAUTHORIZED and the WWW-Authenticate header to obtain the desired result. Commented Oct 11, 2022 at 5:48

1 Answer 1

3

Try this.

if (!Request.Headers.ContainsKey("Authorization"))
{
    Response.Headers["WWW-Authenticate"] = "Basic";
    return AuthenticateResult.Fail("No contains header");
}

It will pop up the login window on browser like below

enter image description here

and once you give correct credentials , it will authenticate and return the result

enter image description here

Sign up to request clarification or add additional context in comments.

9 Comments

Still not working
what is not working. are still getting same error or your method is still not being hit.? i tried your complete code + some changes to hook up to rest of the middleware. would be helpful to put some screenshot. i hope you have added all the required middlewares.
Might also need to add Response.StatusCode = 401;
@CodingMytra - The login for username and password keep on coming several times
@CodingMytra - Which middleware am I to add again. I am getting Error: Response status is 401. Yes I successfully added the username and password
|

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.