diff --git a/BTCPayServer/Security/AuthenticationSchemes.cs b/BTCPayServer/Security/AuthenticationSchemes.cs index de87c4116..4594f2e09 100644 --- a/BTCPayServer/Security/AuthenticationSchemes.cs +++ b/BTCPayServer/Security/AuthenticationSchemes.cs @@ -4,6 +4,8 @@ { public const string Cookie = "Identity.Application"; public const string Bitpay = "Bitpay"; - public const string Greenfield = "Greenfield"; + public const string Greenfield = "Greenfield.APIKeys,Greenfield.Basic"; + public const string GreenfieldAPIKeys = "Greenfield.APIKeys"; + public const string GreenfieldBasic = "Greenfield.Basic"; } } diff --git a/BTCPayServer/Security/GreenField/APIKeyExtensions.cs b/BTCPayServer/Security/GreenField/APIKeyExtensions.cs index 7b12c0bc5..ef68a0505 100644 --- a/BTCPayServer/Security/GreenField/APIKeyExtensions.cs +++ b/BTCPayServer/Security/GreenField/APIKeyExtensions.cs @@ -26,7 +26,9 @@ namespace BTCPayServer.Security.GreenField public static AuthenticationBuilder AddAPIKeyAuthentication(this AuthenticationBuilder builder) { - builder.AddScheme(AuthenticationSchemes.Greenfield, + builder.AddScheme(AuthenticationSchemes.GreenfieldAPIKeys, + o => { }); + builder.AddScheme(AuthenticationSchemes.GreenfieldBasic, o => { }); return builder; } diff --git a/BTCPayServer/Security/GreenField/APIKeysAuthenticationHandler.cs b/BTCPayServer/Security/GreenField/APIKeysAuthenticationHandler.cs new file mode 100644 index 000000000..ec30b1c13 --- /dev/null +++ b/BTCPayServer/Security/GreenField/APIKeysAuthenticationHandler.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using BTCPayServer.Client; +using BTCPayServer.Data; +using BTCPayServer.Security.Bitpay; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace BTCPayServer.Security.GreenField +{ + public class APIKeysAuthenticationHandler : AuthenticationHandler + { + private readonly APIKeyRepository _apiKeyRepository; + private readonly IOptionsMonitor _identityOptions; + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + + public APIKeysAuthenticationHandler( + APIKeyRepository apiKeyRepository, + IOptionsMonitor identityOptions, + IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder, + ISystemClock clock, + SignInManager signInManager, + UserManager userManager) : base(options, logger, encoder, clock) + { + _apiKeyRepository = apiKeyRepository; + _identityOptions = identityOptions; + _signInManager = signInManager; + _userManager = userManager; + } + + protected override async Task HandleAuthenticateAsync() + { + if (!Context.Request.HttpContext.GetAPIKey(out var apiKey) || string.IsNullOrEmpty(apiKey)) + return AuthenticateResult.NoResult(); + + var key = await _apiKeyRepository.GetKey(apiKey); + + if (key == null) + { + return AuthenticateResult.Fail("ApiKey authentication failed"); + } + + List claims = new List(); + claims.Add(new Claim(_identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, key.UserId)); + claims.AddRange(Permission.ToPermissions(key.Permissions).Select(permission => + new Claim(GreenFieldConstants.ClaimTypes.Permission, permission.ToString()))); + return AuthenticateResult.Success(new AuthenticationTicket( + new ClaimsPrincipal(new ClaimsIdentity(claims, GreenFieldConstants.AuthenticationType)), + GreenFieldConstants.AuthenticationType)); + } + } +} diff --git a/BTCPayServer/Security/GreenField/GreenFieldAuthenticationHandler.cs b/BTCPayServer/Security/GreenField/BasicAuthenticationHandler.cs similarity index 59% rename from BTCPayServer/Security/GreenField/GreenFieldAuthenticationHandler.cs rename to BTCPayServer/Security/GreenField/BasicAuthenticationHandler.cs index 810d084ca..ee531245a 100644 --- a/BTCPayServer/Security/GreenField/GreenFieldAuthenticationHandler.cs +++ b/BTCPayServer/Security/GreenField/BasicAuthenticationHandler.cs @@ -15,15 +15,13 @@ using Microsoft.Extensions.Options; namespace BTCPayServer.Security.GreenField { - public class GreenFieldAuthenticationHandler : AuthenticationHandler + public class BasicAuthenticationHandler : AuthenticationHandler { - private readonly APIKeyRepository _apiKeyRepository; private readonly IOptionsMonitor _identityOptions; private readonly SignInManager _signInManager; private readonly UserManager _userManager; - public GreenFieldAuthenticationHandler( - APIKeyRepository apiKeyRepository, + public BasicAuthenticationHandler( IOptionsMonitor identityOptions, IOptionsMonitor options, ILoggerFactory logger, @@ -32,49 +30,17 @@ namespace BTCPayServer.Security.GreenField SignInManager signInManager, UserManager userManager) : base(options, logger, encoder, clock) { - _apiKeyRepository = apiKeyRepository; _identityOptions = identityOptions; _signInManager = signInManager; _userManager = userManager; } protected override async Task HandleAuthenticateAsync() - { - var res = await HandleApiKeyAuthenticateResult(); - if (res.None) - { - return await HandleBasicAuthenticateAsync(); - } - - return res; - } - - private async Task HandleApiKeyAuthenticateResult() - { - if (!Context.Request.HttpContext.GetAPIKey(out var apiKey) || string.IsNullOrEmpty(apiKey)) - return AuthenticateResult.NoResult(); - - var key = await _apiKeyRepository.GetKey(apiKey); - - if (key == null) - { - return AuthenticateResult.Fail("ApiKey authentication failed"); - } - - List claims = new List(); - claims.Add(new Claim(_identityOptions.CurrentValue.ClaimsIdentity.UserIdClaimType, key.UserId)); - claims.AddRange(Permission.ToPermissions(key.Permissions).Select(permission => - new Claim(GreenFieldConstants.ClaimTypes.Permission, permission.ToString()))); - return AuthenticateResult.Success(new AuthenticationTicket( - new ClaimsPrincipal(new ClaimsIdentity(claims, GreenFieldConstants.AuthenticationType)), - GreenFieldConstants.AuthenticationType)); - } - - private async Task HandleBasicAuthenticateAsync() { string authHeader = Context.Request.Headers["Authorization"]; - if (authHeader == null || !authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) return AuthenticateResult.NoResult(); + if (authHeader == null || !authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) + return AuthenticateResult.NoResult(); var encodedUsernamePassword = authHeader.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries)[1]?.Trim(); var decodedUsernamePassword = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUsernamePassword)).Split(':'); @@ -82,7 +48,8 @@ namespace BTCPayServer.Security.GreenField var password = decodedUsernamePassword[1]; var result = await _signInManager.PasswordSignInAsync(username, password, true, true); - if (!result.Succeeded) return AuthenticateResult.Fail(result.ToString()); + if (!result.Succeeded) + return AuthenticateResult.Fail(result.ToString()); var user = await _userManager.FindByNameAsync(username); var claims = new List()