using System; using System.Linq; using System.Net.Http.Headers; using System.Security.Claims; using System.Text; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace AMWD.Common.AspNetCore.BasicAuthentication { /// /// Implements the for Basic Authentication. /// [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public class BasicAuthenticationHandler : AuthenticationHandler { private readonly ILogger logger; private readonly IBasicAuthenticationValidator validator; /// /// Initializes a new instance of the class. /// /// The authentication scheme options. /// The logger factory. /// The URL encoder. /// The system clock. /// An basic autentication validator implementation. public BasicAuthenticationHandler(IOptionsMonitor options, ILoggerFactory loggerFactory, UrlEncoder encoder, ISystemClock clock, IBasicAuthenticationValidator validator) : base(options, loggerFactory, encoder, clock) { logger = loggerFactory.CreateLogger(); this.validator = validator; } /// protected override async Task HandleAuthenticateAsync() { var endpoint = Context.GetEndpoint(); if (endpoint?.Metadata?.GetMetadata() != null) return AuthenticateResult.NoResult(); if (!Request.Headers.ContainsKey("Authorization")) return AuthenticateResult.Fail("Authorization header missing"); ClaimsPrincipal principal; try { var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]); string plain = Encoding.UTF8.GetString(Convert.FromBase64String(authHeader.Parameter)); // See: https://www.rfc-editor.org/rfc/rfc2617, page 6 string username = plain.Split(':').First(); string password = plain[(username.Length + 1)..]; var ipAddress = Context.GetRemoteIpAddress(); principal = await validator.ValidateAsync(username, password, ipAddress, Context.RequestAborted); } catch (Exception ex) { logger.LogError(ex, $"Handling the Basic Authentication failed: {ex.Message}"); return AuthenticateResult.Fail("Authorization header invalid"); } if (principal == null) return AuthenticateResult.Fail("Invalid credentials"); var ticket = new AuthenticationTicket(principal, Scheme.Name); return AuthenticateResult.Success(ticket); } } }