using System; using System.Linq; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace AMWD.Common.AspNetCore.BasicAuthentication { /// /// Implements a basic authentication. /// public class BasicAuthenticationMiddleware { private readonly RequestDelegate next; private readonly IBasicAuthenticationValidator validator; /// /// Initializes a new instance of the class. /// /// The following delegate in the process chain. /// A basic authentication validator. public BasicAuthenticationMiddleware(RequestDelegate next, IBasicAuthenticationValidator validator) { this.next = next; this.validator = validator; } /// /// The delegate invokation. /// Performs the authentication check. /// /// The corresponding HTTP context. /// An awaitable task. public async Task InvokeAsync(HttpContext httpContext) { if (!httpContext.Request.Headers.ContainsKey("Authorization")) { SetAuthenticateRequest(httpContext, validator.Realm); return; } try { var authHeader = AuthenticationHeaderValue.Parse(httpContext.Request.Headers["Authorization"]); byte[] decoded = Convert.FromBase64String(authHeader.Parameter); string plain = Encoding.UTF8.GetString(decoded); string[] credentials = plain.Split(':', 2, StringSplitOptions.RemoveEmptyEntries); var principal = await validator.ValidateAsync(credentials.First(), credentials.Last(), httpContext.GetRemoteIpAddress()); if (principal == null) { SetAuthenticateRequest(httpContext, validator.Realm); return; } await next.Invoke(httpContext); } catch { SetAuthenticateRequest(httpContext, validator.Realm); } } private static void SetAuthenticateRequest(HttpContext httpContext, string realm) { httpContext.Response.Headers["WWW-Authenticate"] = "Basic"; if (!string.IsNullOrWhiteSpace(realm)) httpContext.Response.Headers["WWW-Authenticate"] += $" realm=\"{realm.Replace("\"", "")}\""; httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; } } }