The modern way is AuthenticationHandlers
in startup.cs add
services.AddAuthentication("BasicAuthentication").AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null); public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> { private readonly IUserService _userService; public BasicAuthenticationHandler( IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IUserService userService) : base(options, logger, encoder, clock) { _userService = userService; } protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { if (!Request.Headers.ContainsKey("Authorization")) return AuthenticateResult.Fail("Missing Authorization Header"); User user = null; try { var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]); var credentialBytes = Convert.FromBase64String(authHeader.Parameter); var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2); var username = credentials[0]; var password = credentials[1]; user = await _userService.Authenticate(username, password); } catch { return AuthenticateResult.Fail("Invalid Authorization Header"); } if (user == null) return AuthenticateResult.Fail("Invalid User-name or Password"); var claims = new[] { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.Username), }; var identity = new ClaimsIdentity(claims, Scheme.Name); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, Scheme.Name); return AuthenticateResult.Success(ticket); } }
UPDATE .NET 8
using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Options; using System.Net.Http.Headers; using System.Security.Claims; using System.Text; using System.Text.Encodings.Web; namespace LicenseWeppApp.Infrastructure; internal class CustomBasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> { private readonly IOptionsMonitor<AuthenticationSchemeOptions> _options; private readonly ILogger _logger; private readonly UrlEncoder _encoder; IUserService _userService; public CustomBasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, IUserService userService) : base(options, logger, encoder) { _options = options; _logger = logger.CreateLogger("LicenseWeppApp.Infrastructure.CustomBasicAuthenticationHandler");//create a logger like this if you use obfuscation _encoder = encoder; _userService = userService; } protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { if (!Request.Headers.TryGetValue("Authorization", out Microsoft.Extensions.Primitives.StringValues value) || value.Count == 0) return AuthenticateResult.Fail("Missing Authorization Header"); User? user = null; try { var authHeader = AuthenticationHeaderValue.Parse(value); var credentialBytes = Convert.FromBase64String(authHeader.Parameter); var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':', 2); var username = credentials[0]; var password = credentials[1]; user = await _userService.Authenticate(username, password); } catch { _logger.LogInformation("Invalid Authorization Header {value}", value.ToString()); return AuthenticateResult.Fail("Invalid Authorization Header"); } if (user == null) return AuthenticateResult.Fail("Invalid User-name or Password"); var claims = new[] { new Claim(type: ClaimTypes.NameIdentifier, value:user.ID.ToString()), new Claim(type: ClaimTypes.Name, value:user.UserName), }; var identity = new ClaimsIdentity(claims, Scheme.Name); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, Scheme.Name); return AuthenticateResult.Success(ticket); } }
IUserService is a service that you make where you have user name and password. basically it returns a user class that you use to map your claims on.
var claims = new[] { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.Username), };
Then you can query these claims and her any data you mapped, ther are quite a few, have a look at ClaimTypes class
you can use this in an extension method an get any of the mappings
public int? GetUserId() { if (context.User.Identity.IsAuthenticated) { var id=context.User.FindFirst(ClaimTypes.NameIdentifier); if (!(id is null) && int.TryParse(id.Value, out var userId)) return userId; } return new Nullable<int>(); }
This new way, i think is better than the old way as shown here, both work
public class BasicAuthenticationAttribute : AuthorizationFilterAttribute { public override void OnAuthorization(HttpActionContext actionContext) { if (actionContext.Request.Headers.Authorization != null) { var authToken = actionContext.Request.Headers.Authorization.Parameter; // decoding authToken we get decode value in 'Username:Password' format var decodeauthToken = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(authToken)); // spliting decodeauthToken using ':' var arrUserNameandPassword = decodeauthToken.Split(':'); // at 0th postion of array we get username and at 1st we get password if (IsAuthorizedUser(arrUserNameandPassword[0], arrUserNameandPassword[1])) { // setting current principle Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(arrUserNameandPassword[0]), null); } else { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); } } else { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); } } public static bool IsAuthorizedUser(string Username, string Password) { // In this method we can handle our database logic here... return Username.Equals("test") && Password == "test"; } }