using System; using System.Collections.Generic; using System.Security.Claims; using System.Text; using System.Text.Encodings.Web; using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Authentication; namespace Lidarr.Http.Authentication { public class BasicAuthenticationHandler : AuthenticationHandler { private readonly IAuthenticationService _authService; public BasicAuthenticationHandler(IAuthenticationService authService, IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { _authService = authService; } protected override Task HandleAuthenticateAsync() { if (!Request.Headers.ContainsKey("Authorization")) { return Task.FromResult(AuthenticateResult.Fail("Authorization header missing.")); } // Get authorization key var authorizationHeader = Request.Headers["Authorization"].ToString(); var authHeaderRegex = new Regex(@"Basic (.*)"); if (!authHeaderRegex.IsMatch(authorizationHeader)) { return Task.FromResult(AuthenticateResult.Fail("Authorization code not formatted properly.")); } var authBase64 = Encoding.UTF8.GetString(Convert.FromBase64String(authHeaderRegex.Replace(authorizationHeader, "$1"))); var authSplit = authBase64.Split(':', 2); var authUsername = authSplit[0]; var authPassword = authSplit.Length > 1 ? authSplit[1] : throw new Exception("Unable to get password"); var user = _authService.Login(Request, authUsername, authPassword); if (user == null) { return Task.FromResult(AuthenticateResult.Fail("The username or password is not correct.")); } var claims = new List { new Claim("user", user.Username), new Claim("identifier", user.Identifier.ToString()), new Claim("AuthType", AuthenticationType.Basic.ToString()) }; var identity = new ClaimsIdentity(claims, "Basic", "user", "identifier"); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, "Basic"); return Task.FromResult(AuthenticateResult.Success(ticket)); } protected override Task HandleChallengeAsync(AuthenticationProperties properties) { Response.Headers.Add("WWW-Authenticate", $"Basic realm=\"{BuildInfo.AppName}\""); Response.StatusCode = 401; return Task.CompletedTask; } protected override Task HandleForbiddenAsync(AuthenticationProperties properties) { Response.StatusCode = 403; return Task.CompletedTask; } } }