btcpayserver/BTCPayServer/Security/Bitpay/BitpayAuthenticationHandler.cs

113 lines
4.4 KiB
C#
Raw Normal View History

2020-06-28 21:44:35 -05:00
using System;
2019-06-09 01:36:54 +09:00
using System.Collections.Generic;
using System.IO;
using System.Security.Claims;
using System.Text;
2020-06-28 17:55:27 +09:00
using System.Text.Encodings.Web;
2019-06-09 01:36:54 +09:00
using System.Threading.Tasks;
using BTCPayServer.Services.Stores;
2020-06-28 17:55:27 +09:00
using Microsoft.AspNetCore.Authentication;
2019-06-09 01:36:54 +09:00
using Microsoft.AspNetCore.Http;
2020-06-28 17:55:27 +09:00
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Logging;
2019-06-09 01:36:54 +09:00
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;
2019-06-09 01:36:54 +09:00
public BitpayAuthenticationHandler(
TokenRepository tokenRepository,
StoreRepository storeRepository,
IOptionsMonitor<BitpayAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
_TokenRepository = tokenRepository;
_StoreRepository = storeRepository;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
2019-06-11 18:40:47 +09:00
if (!Context.Request.HttpContext.TryGetBitpayAuth(out var bitpayAuth))
return AuthenticateResult.NoResult();
2019-06-09 01:36:54 +09:00
if (!string.IsNullOrEmpty(bitpayAuth.Signature) && !string.IsNullOrEmpty(bitpayAuth.Id))
{
2019-10-12 20:35:30 +09:00
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);
2019-06-09 01:36:54 +09:00
}
else if (!string.IsNullOrEmpty(bitpayAuth.Authorization))
{
2019-10-12 20:35:30 +09:00
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);
2019-06-09 01:36:54 +09:00
}
else
{
2019-10-12 20:35:30 +09:00
return Success(null, null, BitpayAuthenticationTypes.Anonymous);
2019-06-09 01:36:54 +09:00
}
2019-10-12 20:35:30 +09:00
}
2019-06-09 01:36:54 +09:00
2019-10-12 20:35:30 +09:00
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));
2019-06-09 01:36:54 +09:00
}
2019-10-12 20:35:30 +09:00
private async Task<string> CheckBitId(HttpContext httpContext, string sig, string id)
2019-06-09 01:36:54 +09:00
{
2019-10-03 17:37:10 +09:00
httpContext.Request.EnableBuffering();
2019-06-09 01:36:54 +09:00
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();
2019-06-09 01:36:54 +09:00
}
httpContext.Request.Body.Position = 0;
}
var url = httpContext.Request.GetEncodedUrl();
try
{
var key = new PubKey(id);
if (BitIdExtensions.CheckBitIDSignature(key, sig, url, body))
{
2019-10-12 20:35:30 +09:00
return key.GetBitIDSIN();
2019-06-09 01:36:54 +09:00
}
}
2019-10-12 20:35:30 +09:00
catch { }
return null;
2019-06-09 01:36:54 +09:00
}
2019-10-12 20:35:30 +09:00
private async Task<string> GetStoreIdFromAuth(HttpContext httpContext, string auth)
2019-06-09 01:36:54 +09:00
{
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);
}
}
}