btcpayserver/BTCPayServer/Security/Bitpay/BitpayAuthenticationHandler.cs
Nicolas Dorier 1081eab9db
Fix warnings (#5517)
Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
2023-11-28 15:20:03 +01:00

113 lines
4.4 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NBitcoin;
using NBitcoin.DataEncoders;
using NBitpayClient.Extensions;
namespace BTCPayServer.Security.Bitpay
{
public class BitpayAuthenticationHandler : AuthenticationHandler<BitpayAuthenticationOptions>
{
readonly StoreRepository _StoreRepository;
readonly TokenRepository _TokenRepository;
public BitpayAuthenticationHandler(
TokenRepository tokenRepository,
StoreRepository storeRepository,
IOptionsMonitor<BitpayAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder)
{
_TokenRepository = tokenRepository;
_StoreRepository = storeRepository;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Context.Request.HttpContext.TryGetBitpayAuth(out var bitpayAuth))
return AuthenticateResult.NoResult();
if (!string.IsNullOrEmpty(bitpayAuth.Signature) && !string.IsNullOrEmpty(bitpayAuth.Id))
{
var sin = await CheckBitId(Context.Request.HttpContext, bitpayAuth.Signature, bitpayAuth.Id);
if (sin == null)
return AuthenticateResult.Fail("BitId authentication failed");
return Success(BitpayClaims.SIN, sin, BitpayAuthenticationTypes.SinAuthentication);
}
else if (!string.IsNullOrEmpty(bitpayAuth.Authorization))
{
var storeId = await GetStoreIdFromAuth(Context.Request.HttpContext, bitpayAuth.Authorization);
if (storeId == null)
return AuthenticateResult.Fail("ApiKey authentication failed");
return Success(BitpayClaims.ApiKeyStoreId, storeId, BitpayAuthenticationTypes.ApiKeyAuthentication);
}
else
{
return Success(null, null, BitpayAuthenticationTypes.Anonymous);
}
}
private AuthenticateResult Success(string claimType, string claimValue, string authenticationType)
{
List<Claim> claims = new List<Claim>();
if (claimType != null)
claims.Add(new Claim(claimType, claimValue));
return AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(claims, authenticationType)), authenticationType));
}
private async Task<string> CheckBitId(HttpContext httpContext, string sig, string id)
{
httpContext.Request.EnableBuffering();
string body = string.Empty;
if (httpContext.Request.ContentLength != 0 && httpContext.Request.Body != null)
{
using (StreamReader reader = new StreamReader(httpContext.Request.Body, Encoding.UTF8, true, 1024, true))
{
body = await reader.ReadToEndAsync();
}
httpContext.Request.Body.Position = 0;
}
var url = httpContext.Request.GetEncodedUrl();
try
{
var key = new PubKey(id);
if (BitIdExtensions.CheckBitIDSignature(key, sig, url, body))
{
return key.GetBitIDSIN();
}
}
catch { }
return null;
}
private async Task<string> GetStoreIdFromAuth(HttpContext httpContext, string auth)
{
var splitted = auth.Split(' ', StringSplitOptions.RemoveEmptyEntries);
if (splitted.Length != 2 || !splitted[0].Equals("Basic", StringComparison.OrdinalIgnoreCase))
{
return null;
}
string apiKey = null;
try
{
apiKey = Encoders.ASCII.EncodeData(Encoders.Base64.DecodeData(splitted[1]));
}
catch
{
return null;
}
return await _TokenRepository.GetStoreIdFromAPIKey(apiKey);
}
}
}