mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 01:43:50 +01:00
Update to OpenIddict3.0
This commit is contained in:
parent
d56a5ad86e
commit
3c9b58916b
@ -6,14 +6,14 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.1.2" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.2" />
|
||||
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="2.0.0" />
|
||||
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="3.0.0-alpha1.19515.63" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp2.1'">
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.0.1" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.0.0-rc1.final" />
|
||||
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="3.0.0-alpha1.19509.59" />
|
||||
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="3.0.0-alpha1.19515.63" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -4,16 +4,9 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
#if NETCOREAPP21
|
||||
using OpenIddictRequest = AspNet.Security.OpenIdConnect.Primitives.OpenIdConnectRequest;
|
||||
using OpenIddictResponse = AspNet.Security.OpenIdConnect.Primitives.OpenIdConnectResponse;
|
||||
using OpenIdConnectDefaults = OpenIddict.Server.OpenIddictServerDefaults;
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
#else
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
#endif
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
@ -31,7 +24,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
public class AuthenticationTests
|
||||
{
|
||||
public const int TestTimeout = 60_000;
|
||||
public const int TestTimeout = TestUtils.TestTimeout;
|
||||
public AuthenticationTests(ITestOutputHelper helper)
|
||||
{
|
||||
Logs.Tester = new XUnitLog(helper) {Name = "Tests"};
|
||||
@ -112,6 +105,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
await user.MakeAdmin();
|
||||
var id = Guid.NewGuid().ToString();
|
||||
var redirecturi = new Uri("http://127.0.0.1/oidc-callback");
|
||||
var openIdClient = await user.RegisterOpenIdClient(
|
||||
@ -124,7 +118,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
});
|
||||
var implicitAuthorizeUrl = new Uri(tester.PayTester.ServerUri,
|
||||
$"connect/authorize?response_type=token&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid&nonce={Guid.NewGuid().ToString()}");
|
||||
$"connect/authorize?response_type=token&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid server_management store_management&nonce={Guid.NewGuid().ToString()}");
|
||||
s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl);
|
||||
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||
s.Driver.FindElement(By.Id("consent-yes")).Click();
|
||||
@ -225,7 +219,7 @@ namespace BTCPayServer.Tests
|
||||
RedirectUris = {redirecturi}
|
||||
}, secret);
|
||||
var authorizeUrl = new Uri(tester.PayTester.ServerUri,
|
||||
$"connect/authorize?response_type=code&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid offline_access&state={Guid.NewGuid().ToString()}");
|
||||
$"connect/authorize?response_type=code&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid offline_access server_management store_management&state={Guid.NewGuid().ToString()}");
|
||||
s.Driver.Navigate().GoToUrl(authorizeUrl);
|
||||
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||
s.Driver.FindElement(By.Id("consent-yes")).Click();
|
||||
@ -325,7 +319,8 @@ namespace BTCPayServer.Tests
|
||||
new KeyValuePair<string, string>("grant_type",
|
||||
OpenIddictConstants.GrantTypes.ClientCredentials),
|
||||
new KeyValuePair<string, string>("client_id", openIdClient.ClientId),
|
||||
new KeyValuePair<string, string>("client_secret", secret)
|
||||
new KeyValuePair<string, string>("client_secret", secret),
|
||||
new KeyValuePair<string, string>("scope", "server_management store_management")
|
||||
})
|
||||
};
|
||||
|
||||
@ -365,7 +360,8 @@ namespace BTCPayServer.Tests
|
||||
new KeyValuePair<string, string>("username", user.RegisterDetails.Email),
|
||||
new KeyValuePair<string, string>("password", user.RegisterDetails.Password),
|
||||
new KeyValuePair<string, string>("client_id", openIdClient.ClientId),
|
||||
new KeyValuePair<string, string>("client_secret", secret)
|
||||
new KeyValuePair<string, string>("client_secret", secret),
|
||||
new KeyValuePair<string, string>("scope", "server_management store_management")
|
||||
})
|
||||
};
|
||||
|
||||
|
@ -32,11 +32,7 @@ using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
#if NETCOREAPP21
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
#else
|
||||
using OpenIdConnectConstants = OpenIddict.Abstractions.OpenIddictConstants;
|
||||
#endif
|
||||
using OpenIddict.Abstractions;
|
||||
using Xunit;
|
||||
using BTCPayServer.Services;
|
||||
using System.Net.Http;
|
||||
@ -305,7 +301,7 @@ namespace BTCPayServer.Tests
|
||||
if (userId != null)
|
||||
{
|
||||
List<Claim> claims = new List<Claim>();
|
||||
claims.Add(new Claim(OpenIdConnectConstants.Claims.Subject, userId));
|
||||
claims.Add(new Claim(OpenIddictConstants.Claims.Subject, userId));
|
||||
if (isAdmin)
|
||||
claims.Add(new Claim(ClaimTypes.Role, Roles.ServerAdmin));
|
||||
context.User = new ClaimsPrincipal(new ClaimsIdentity(claims.ToArray(), AuthenticationSchemes.Cookie));
|
||||
|
@ -5,10 +5,12 @@ ENV LC_ALL en_US.UTF-8
|
||||
ENV LANG en_US.UTF-8
|
||||
|
||||
WORKDIR /source
|
||||
COPY nuget.config nuget.config
|
||||
COPY Build/Common.csproj Build/Common.csproj
|
||||
COPY BTCPayServer/BTCPayServer.csproj BTCPayServer/BTCPayServer.csproj
|
||||
COPY BTCPayServer.Common/BTCPayServer.Common.csproj BTCPayServer.Common/BTCPayServer.Common.csproj
|
||||
COPY BTCPayServer.Rating/BTCPayServer.Rating.csproj BTCPayServer.Rating/BTCPayServer.Rating.csproj
|
||||
COPY BTCPayServer.Data/BTCPayServer.Data.csproj BTCPayServer.Data/BTCPayServer.Data.csproj
|
||||
COPY BTCPayServer.Tests/BTCPayServer.Tests.csproj BTCPayServer.Tests/BTCPayServer.Tests.csproj
|
||||
RUN dotnet restore BTCPayServer.Tests/BTCPayServer.Tests.csproj
|
||||
|
||||
|
@ -15,6 +15,11 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
public static class TestUtils
|
||||
{
|
||||
#if DEBUG
|
||||
public const int TestTimeout = 600_000;
|
||||
#else
|
||||
public const int TestTimeout = 60_000;
|
||||
#endif
|
||||
public static DirectoryInfo TryGetSolutionDirectoryInfo(string currentPath = null)
|
||||
{
|
||||
var directory = new DirectoryInfo(
|
||||
|
@ -30,6 +30,7 @@ namespace BTCPayServer.Authentication
|
||||
//create and manage apps
|
||||
public const string AppManagement = "app_management";
|
||||
public const string WalletManagement = "wallet_management";
|
||||
public const string ServerManagement = "server_management";
|
||||
}
|
||||
|
||||
public const string CanViewStores = nameof(CanViewStores);
|
||||
|
@ -1,27 +0,0 @@
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Options;
|
||||
using OpenIddict.Core;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId
|
||||
{
|
||||
public class AuthorizationCodeGrantTypeEventHandler : OpenIdGrantHandlerCheckCanSignIn
|
||||
{
|
||||
public AuthorizationCodeGrantTypeEventHandler(
|
||||
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||
SignInManager<ApplicationUser> signInManager,
|
||||
IOptions<IdentityOptions> identityOptions,
|
||||
UserManager<ApplicationUser> userManager) : base(applicationManager, authorizationManager, signInManager,
|
||||
identityOptions, userManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool IsValid(OpenIdConnectRequest request)
|
||||
{
|
||||
return request.IsAuthorizationCodeGrantType();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
using OpenIdConnectRequest = OpenIddict.Abstractions.OpenIddictRequest;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
@ -7,11 +7,13 @@ using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Options;
|
||||
using OpenIddict.Core;
|
||||
using OpenIddict.Server;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId
|
||||
{
|
||||
public abstract class BaseOpenIdGrantHandler<T> : IOpenIddictServerEventHandler<T>
|
||||
where T : class, IOpenIddictServerEvent
|
||||
public abstract class BaseOpenIdGrantHandler<T> :
|
||||
IOpenIddictServerHandler<T>
|
||||
where T : OpenIddictServerEvents.BaseContext
|
||||
{
|
||||
private readonly OpenIddictApplicationManager<BTCPayOpenIdClient> _applicationManager;
|
||||
private readonly OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> _authorizationManager;
|
||||
@ -31,14 +33,11 @@ namespace BTCPayServer.Authentication.OpenId
|
||||
}
|
||||
|
||||
|
||||
protected async Task<AuthenticationTicket> CreateTicketAsync(
|
||||
OpenIdConnectRequest request, ApplicationUser user,
|
||||
AuthenticationProperties properties = null)
|
||||
protected Task<ClaimsPrincipal> CreateClaimsPrincipalAsync(OpenIdConnectRequest request, ApplicationUser user)
|
||||
{
|
||||
return await OpenIdExtensions.CreateAuthenticationTicket(_applicationManager, _authorizationManager,
|
||||
_identityOptions.Value, _signInManager, request, user, properties);
|
||||
return OpenIdExtensions.CreateClaimsPrincipalAsync(_applicationManager, _authorizationManager,
|
||||
_identityOptions.Value, _signInManager, request, user);
|
||||
}
|
||||
|
||||
public abstract Task<OpenIddictServerEventState> HandleAsync(T notification);
|
||||
public abstract ValueTask HandleAsync(T notification);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using AspNet.Security.OpenIdConnect.Extensions;
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
@ -14,9 +12,13 @@ using OpenIddict.Server;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId
|
||||
{
|
||||
public class
|
||||
ClientCredentialsGrantTypeEventHandler : BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleTokenRequest>
|
||||
public class ClientCredentialsGrantTypeEventHandler :
|
||||
BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleTokenRequestContext>
|
||||
{
|
||||
public static OpenIddictServerHandlerDescriptor Descriptor { get; } =
|
||||
OpenIddictServerHandlerDescriptor.CreateBuilder<OpenIddictServerEvents.HandleTokenRequestContext>()
|
||||
.UseScopedHandler<ClientCredentialsGrantTypeEventHandler>()
|
||||
.Build();
|
||||
private readonly OpenIddictApplicationManager<BTCPayOpenIdClient> _applicationManager;
|
||||
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
@ -33,32 +35,28 @@ namespace BTCPayServer.Authentication.OpenId
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public override async Task<OpenIddictServerEventState> HandleAsync(
|
||||
OpenIddictServerEvents.HandleTokenRequest notification)
|
||||
public override async ValueTask HandleAsync(
|
||||
OpenIddictServerEvents.HandleTokenRequestContext notification)
|
||||
{
|
||||
var request = notification.Context.Request;
|
||||
var request = notification.Request;
|
||||
var context = notification;
|
||||
if (!request.IsClientCredentialsGrantType())
|
||||
{
|
||||
// Allow other handlers to process the event.
|
||||
return OpenIddictServerEventState.Unhandled;
|
||||
return;
|
||||
}
|
||||
|
||||
var application = await _applicationManager.FindByClientIdAsync(request.ClientId,
|
||||
notification.Context.HttpContext.RequestAborted);
|
||||
var application = await _applicationManager.FindByClientIdAsync(request.ClientId);
|
||||
if (application == null)
|
||||
{
|
||||
notification.Context.Reject(
|
||||
context.Reject(
|
||||
error: OpenIddictConstants.Errors.InvalidClient,
|
||||
description: "The client application was not found in the database.");
|
||||
// Don't allow other handlers to process the event.
|
||||
return OpenIddictServerEventState.Handled;
|
||||
return;
|
||||
}
|
||||
|
||||
var user = await _userManager.FindByIdAsync(application.ApplicationUserId);
|
||||
|
||||
notification.Context.Validate(await CreateTicketAsync(request, user));
|
||||
// Don't allow other handlers to process the event.
|
||||
return OpenIddictServerEventState.Handled;
|
||||
context.Principal = await CreateClaimsPrincipalAsync(request, user);
|
||||
notification.HandleAuthentication();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,37 +3,33 @@ using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Options;
|
||||
using OpenIddict.Core;
|
||||
using OpenIddict.Server;
|
||||
using Microsoft.AspNetCore;
|
||||
using OpenIddict.Server.AspNetCore;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId
|
||||
{
|
||||
public class LogoutEventHandler : BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleLogoutRequest>
|
||||
public class LogoutEventHandler : IOpenIddictServerHandler<OpenIddictServerEvents.HandleLogoutRequestContext>
|
||||
{
|
||||
public LogoutEventHandler(
|
||||
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||
SignInManager<ApplicationUser> signInManager, IOptions<IdentityOptions> identityOptions) : base(
|
||||
applicationManager, authorizationManager,
|
||||
signInManager, identityOptions)
|
||||
protected readonly SignInManager<ApplicationUser> _signInManager;
|
||||
public static OpenIddictServerHandlerDescriptor Descriptor { get; } =
|
||||
OpenIddictServerHandlerDescriptor.CreateBuilder<OpenIddictServerEvents.HandleLogoutRequestContext>()
|
||||
.UseScopedHandler<LogoutEventHandler>()
|
||||
.Build();
|
||||
public LogoutEventHandler(SignInManager<ApplicationUser> signInManager)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
public override async Task<OpenIddictServerEventState> HandleAsync(
|
||||
OpenIddictServerEvents.HandleLogoutRequest notification)
|
||||
public async ValueTask HandleAsync(
|
||||
OpenIddictServerEvents.HandleLogoutRequestContext notification)
|
||||
{
|
||||
// Ask ASP.NET Core Identity to delete the local and external cookies created
|
||||
// when the user agent is redirected from the external identity provider
|
||||
// after a successful authentication flow (e.g Google or Facebook).
|
||||
await _signInManager.SignOutAsync();
|
||||
|
||||
// Returning a SignOutResult will ask OpenIddict to redirect the user agent
|
||||
// to the post_logout_redirect_uri specified by the client application.
|
||||
await notification.Context.HttpContext.SignOutAsync(OpenIddictServerDefaults.AuthenticationScheme);
|
||||
notification.Context.HandleResponse();
|
||||
return OpenIddictServerEventState.Handled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using AspNet.Security.OpenIdConnect.Extensions;
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
@ -12,108 +11,80 @@ using Microsoft.AspNetCore.Identity;
|
||||
using OpenIddict.Abstractions;
|
||||
using OpenIddict.Core;
|
||||
using OpenIddict.Server;
|
||||
using static BTCPayServer.Authentication.RestAPIPolicies;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId
|
||||
{
|
||||
public static class OpenIdExtensions
|
||||
{
|
||||
public static async Task<AuthenticationTicket> CreateAuthenticationTicket(
|
||||
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||
public static ImmutableHashSet<string> Restrict(this ImmutableHashSet<string> scopes, ClaimsPrincipal claimsPrincipal)
|
||||
{
|
||||
HashSet<string> restricted = new HashSet<string>();
|
||||
foreach (var scope in scopes)
|
||||
{
|
||||
if (scope == BTCPayScopes.ServerManagement && !claimsPrincipal.IsInRole(Roles.ServerAdmin))
|
||||
continue;
|
||||
restricted.Add(scope);
|
||||
}
|
||||
return restricted.ToImmutableHashSet();
|
||||
}
|
||||
public static async Task<ClaimsPrincipal> CreateClaimsPrincipalAsync(OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||
IdentityOptions identityOptions,
|
||||
SignInManager<ApplicationUser> signInManager,
|
||||
OpenIdConnectRequest request,
|
||||
ApplicationUser user,
|
||||
AuthenticationProperties properties = null)
|
||||
OpenIddictRequest request,
|
||||
ApplicationUser user)
|
||||
{
|
||||
var principal = await signInManager.CreateUserPrincipalAsync(user);
|
||||
|
||||
// Create a new authentication ticket holding the user identity.
|
||||
var ticket = new AuthenticationTicket(principal, properties,
|
||||
OpenIddictServerDefaults.AuthenticationScheme);
|
||||
|
||||
if (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType())
|
||||
{
|
||||
ticket.SetScopes(request.GetScopes());
|
||||
principal.SetScopes(request.GetScopes().Restrict(principal));
|
||||
}
|
||||
else if (request.IsAuthorizationCodeGrantType() &&
|
||||
string.IsNullOrEmpty(ticket.GetInternalAuthorizationId()))
|
||||
string.IsNullOrEmpty(principal.GetInternalAuthorizationId()))
|
||||
{
|
||||
var app = await applicationManager.FindByClientIdAsync(request.ClientId);
|
||||
var authorizationId = await IsUserAuthorized(authorizationManager, request, user.Id, app.Id);
|
||||
if (!string.IsNullOrEmpty(authorizationId))
|
||||
{
|
||||
ticket.SetInternalAuthorizationId(authorizationId);
|
||||
principal.SetInternalAuthorizationId(authorizationId);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var claim in ticket.Principal.Claims)
|
||||
{
|
||||
claim.SetDestinations(GetDestinations(identityOptions, claim, ticket));
|
||||
}
|
||||
principal.SetDestinations(identityOptions);
|
||||
return principal;
|
||||
}
|
||||
|
||||
return ticket;
|
||||
public static void SetDestinations(this ClaimsPrincipal principal, IdentityOptions identityOptions)
|
||||
{
|
||||
foreach (var claim in principal.Claims)
|
||||
{
|
||||
claim.SetDestinations(GetDestinations(identityOptions, claim, principal));
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetDestinations(IdentityOptions identityOptions, Claim claim,
|
||||
AuthenticationTicket ticket)
|
||||
ClaimsPrincipal principal)
|
||||
{
|
||||
// Note: by default, claims are NOT automatically included in the access and identity tokens.
|
||||
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
|
||||
// whether they should be included in access tokens, in identity tokens or in both.
|
||||
|
||||
|
||||
switch (claim.Type)
|
||||
{
|
||||
case OpenIddictConstants.Claims.Name:
|
||||
yield return OpenIddictConstants.Destinations.AccessToken;
|
||||
|
||||
if (ticket.HasScope(OpenIddictConstants.Scopes.Profile))
|
||||
yield return OpenIddictConstants.Destinations.IdentityToken;
|
||||
|
||||
yield break;
|
||||
|
||||
case OpenIddictConstants.Claims.Email:
|
||||
yield return OpenIddictConstants.Destinations.AccessToken;
|
||||
|
||||
if (ticket.HasScope(OpenIddictConstants.Scopes.Email))
|
||||
yield return OpenIddictConstants.Destinations.IdentityToken;
|
||||
|
||||
yield break;
|
||||
|
||||
case OpenIddictConstants.Claims.Role:
|
||||
yield return OpenIddictConstants.Destinations.AccessToken;
|
||||
|
||||
if (ticket.HasScope(OpenIddictConstants.Scopes.Roles))
|
||||
yield return OpenIddictConstants.Destinations.IdentityToken;
|
||||
|
||||
yield break;
|
||||
default:
|
||||
if (claim.Type == identityOptions.ClaimsIdentity.SecurityStampClaimType)
|
||||
{
|
||||
// Never include the security stamp in the access and identity tokens, as it's a secret value.
|
||||
yield break;
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return OpenIddictConstants.Destinations.AccessToken;
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> IsUserAuthorized(
|
||||
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||
OpenIdConnectRequest request, string userId, string applicationId)
|
||||
OpenIddictRequest request, string userId, string applicationId)
|
||||
{
|
||||
var authorizations =
|
||||
await authorizationManager.ListAsync(queryable =>
|
||||
var authorizations = await authorizationManager.ListAsync(queryable =>
|
||||
queryable.Where(authorization =>
|
||||
authorization.Subject.Equals(userId, StringComparison.OrdinalIgnoreCase) &&
|
||||
applicationId.Equals(authorization.Application.Id, StringComparison.OrdinalIgnoreCase) &&
|
||||
authorization.Status.Equals(OpenIddictConstants.Statuses.Valid,
|
||||
StringComparison.OrdinalIgnoreCase)));
|
||||
|
||||
StringComparison.OrdinalIgnoreCase))).ToArrayAsync();
|
||||
|
||||
if (authorizations.Length > 0)
|
||||
{
|
||||
|
@ -1,22 +1,29 @@
|
||||
using System.Threading.Tasks;
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
using OpenIddict.Abstractions;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Options;
|
||||
using OpenIddict.Abstractions;
|
||||
using OpenIddict.Core;
|
||||
using OpenIddict.Server;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
using OpenIddict.Server.AspNetCore;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId
|
||||
{
|
||||
public abstract class
|
||||
OpenIdGrantHandlerCheckCanSignIn : BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleTokenRequest>
|
||||
public class OpenIdGrantHandlerCheckCanSignIn :
|
||||
BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleTokenRequestContext>
|
||||
{
|
||||
public static OpenIddictServerHandlerDescriptor Descriptor { get; } =
|
||||
OpenIddictServerHandlerDescriptor.CreateBuilder<OpenIddictServerEvents.HandleTokenRequestContext>()
|
||||
.UseScopedHandler<OpenIdGrantHandlerCheckCanSignIn>()
|
||||
.Build();
|
||||
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
protected OpenIdGrantHandlerCheckCanSignIn(
|
||||
public OpenIdGrantHandlerCheckCanSignIn(
|
||||
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||
SignInManager<ApplicationUser> signInManager,
|
||||
@ -27,44 +34,39 @@ namespace BTCPayServer.Authentication.OpenId
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
protected abstract bool IsValid(OpenIdConnectRequest request);
|
||||
|
||||
public override async Task<OpenIddictServerEventState> HandleAsync(
|
||||
OpenIddictServerEvents.HandleTokenRequest notification)
|
||||
public override async ValueTask HandleAsync(
|
||||
OpenIddictServerEvents.HandleTokenRequestContext notification)
|
||||
{
|
||||
var request = notification.Context.Request;
|
||||
if (!IsValid(request))
|
||||
var request = notification.Request;
|
||||
if (!request.IsRefreshTokenGrantType() && !request.IsAuthorizationCodeGrantType())
|
||||
{
|
||||
// Allow other handlers to process the event.
|
||||
return OpenIddictServerEventState.Unhandled;
|
||||
return;
|
||||
}
|
||||
|
||||
var scheme = notification.Context.Scheme.Name;
|
||||
var authenticateResult = (await notification.Context.HttpContext.AuthenticateAsync(scheme));
|
||||
var httpContext = notification.Transaction.GetHttpRequest().HttpContext;
|
||||
var authenticateResult = (await httpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
|
||||
|
||||
var user = await _userManager.GetUserAsync(authenticateResult.Principal);
|
||||
if (user == null)
|
||||
{
|
||||
notification.Context.Reject(
|
||||
notification.Reject(
|
||||
error: OpenIddictConstants.Errors.InvalidGrant,
|
||||
description: "The token is no longer valid.");
|
||||
// Don't allow other handlers to process the event.
|
||||
return OpenIddictServerEventState.Handled;
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the user is still allowed to sign in.
|
||||
if (!await _signInManager.CanSignInAsync(user))
|
||||
{
|
||||
notification.Context.Reject(
|
||||
notification.Reject(
|
||||
error: OpenIddictConstants.Errors.InvalidGrant,
|
||||
description: "The user is no longer allowed to sign in.");
|
||||
// Don't allow other handlers to process the event.
|
||||
return OpenIddictServerEventState.Handled;
|
||||
return;
|
||||
}
|
||||
|
||||
notification.Context.Validate(await CreateTicketAsync(request, user));
|
||||
// Don't allow other handlers to process the event.
|
||||
return OpenIddictServerEventState.Handled;
|
||||
notification.Principal = await this.CreateClaimsPrincipalAsync(request, user);
|
||||
notification.HandleAuthentication();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.U2F;
|
||||
@ -8,12 +8,14 @@ using Microsoft.Extensions.Options;
|
||||
using OpenIddict.Abstractions;
|
||||
using OpenIddict.Core;
|
||||
using OpenIddict.Server;
|
||||
using Microsoft.AspNetCore;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId
|
||||
{
|
||||
public class PasswordGrantTypeEventHandler : BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleTokenRequest>
|
||||
public class PasswordGrantTypeEventHandler : BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleTokenRequestContext>
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly NicolasDorier.RateLimits.RateLimitService _rateLimitService;
|
||||
private readonly U2FService _u2FService;
|
||||
|
||||
public PasswordGrantTypeEventHandler(
|
||||
@ -21,43 +23,44 @@ namespace BTCPayServer.Authentication.OpenId
|
||||
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||
SignInManager<ApplicationUser> signInManager,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
NicolasDorier.RateLimits.RateLimitService rateLimitService,
|
||||
IOptions<IdentityOptions> identityOptions, U2FService u2FService) : base(applicationManager,
|
||||
authorizationManager, signInManager, identityOptions)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_rateLimitService = rateLimitService;
|
||||
_u2FService = u2FService;
|
||||
}
|
||||
|
||||
public override async Task<OpenIddictServerEventState> HandleAsync(
|
||||
OpenIddictServerEvents.HandleTokenRequest notification)
|
||||
public static OpenIddictServerHandlerDescriptor Descriptor { get; } =
|
||||
OpenIddictServerHandlerDescriptor.CreateBuilder<OpenIddictServerEvents.HandleTokenRequestContext>()
|
||||
.UseScopedHandler<PasswordGrantTypeEventHandler>()
|
||||
.Build();
|
||||
|
||||
public override async ValueTask HandleAsync(
|
||||
OpenIddictServerEvents.HandleTokenRequestContext notification)
|
||||
{
|
||||
var request = notification.Context.Request;
|
||||
var request = notification.Request;
|
||||
if (!request.IsPasswordGrantType())
|
||||
{
|
||||
// Allow other handlers to process the event.
|
||||
return OpenIddictServerEventState.Unhandled;
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate the user credentials.
|
||||
// Note: to mitigate brute force attacks, you SHOULD strongly consider
|
||||
// applying a key derivation function like PBKDF2 to slow down
|
||||
// the password validation process. You SHOULD also consider
|
||||
// using a time-constant comparer to prevent timing attacks.
|
||||
var httpContext = notification.Transaction.GetHttpRequest().HttpContext;
|
||||
await _rateLimitService.Throttle(ZoneLimits.Login, httpContext.Connection.RemoteIpAddress.ToString(), httpContext.RequestAborted);
|
||||
var user = await _userManager.FindByNameAsync(request.Username);
|
||||
if (user == null || await _u2FService.HasDevices(user.Id) ||
|
||||
!(await _signInManager.CheckPasswordSignInAsync(user, request.Password, lockoutOnFailure: true))
|
||||
.Succeeded)
|
||||
{
|
||||
notification.Context.Reject(
|
||||
notification.Reject(
|
||||
error: OpenIddictConstants.Errors.InvalidGrant,
|
||||
description: "The specified credentials are invalid.");
|
||||
// Don't allow other handlers to process the event.
|
||||
return OpenIddictServerEventState.Handled;
|
||||
return;
|
||||
}
|
||||
|
||||
notification.Context.Validate(await CreateTicketAsync(request, user));
|
||||
// Don't allow other handlers to process the event.
|
||||
return OpenIddictServerEventState.Handled;
|
||||
notification.Principal = await CreateClaimsPrincipalAsync(request, user);
|
||||
notification.HandleAuthentication();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Options;
|
||||
using OpenIddict.Core;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId
|
||||
{
|
||||
public class RefreshTokenGrantTypeEventHandler : OpenIdGrantHandlerCheckCanSignIn
|
||||
{
|
||||
public RefreshTokenGrantTypeEventHandler(
|
||||
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||
SignInManager<ApplicationUser> signInManager,
|
||||
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(
|
||||
applicationManager, authorizationManager, signInManager,
|
||||
identityOptions, userManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool IsValid(OpenIdConnectRequest request)
|
||||
{
|
||||
return request.IsRefreshTokenGrantType();
|
||||
}
|
||||
}
|
||||
}
|
@ -6,9 +6,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Build\**" />
|
||||
<Compile Remove="Authentication\OpenId\**" Condition="'$(TargetFramework)' != 'netcoreapp2.1'" />
|
||||
<Compile Remove="Storage\Services\Providers\GoogleCloudStorage\**" Condition="'$(TargetFramework)' != 'netcoreapp2.1'" />
|
||||
<Compile Remove="Authentication\OpenId3\**" Condition="'$(TargetFramework)' == 'netcoreapp2.1'" />
|
||||
<Compile Remove="wwwroot\bundles\jqueryvalidate\**" />
|
||||
<Compile Remove="wwwroot\css\**" />
|
||||
<Compile Remove="wwwroot\vendor\jquery-nice-select\**" />
|
||||
@ -51,7 +49,6 @@
|
||||
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.3" />
|
||||
<PackageReference Include="NicolasDorier.RateLimits" Version="1.0.0.9" />
|
||||
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.18" />
|
||||
<PackageReference Include="OpenIddict" Version="2.0.0" Condition="'$(TargetFramework)' == 'netcoreapp2.1'" />
|
||||
<PackageReference Include="Serilog" Version="2.7.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
|
||||
@ -71,13 +68,14 @@
|
||||
<PackageReference Include="TwentyTwenty.Storage.Local" Version="2.11.2" />
|
||||
<PackageReference Include="U2F.Core" Version="1.0.4" />
|
||||
<PackageReference Include="YamlDotNet" Version="5.2.1" />
|
||||
<PackageReference Include="OpenIddict" Version="3.0.0-alpha1.19515.63" />
|
||||
<PackageReference Include="OpenIddict.Server.AspNetCore" Version="3.0.0-alpha1.19515.63"></PackageReference>
|
||||
<PackageReference Include="OpenIddict.Validation.AspNetCore" Version="3.0.0-alpha1.19515.63"></PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp2.1'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.0.0"></PackageReference>
|
||||
<PackageReference Include="OpenIddict" Version="3.0.0-alpha1.19503.62" />
|
||||
<PackageReference Include="OpenIddict.Server.AspNetCore" Version="3.0.0-alpha1.19503.62"></PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -223,4 +221,4 @@
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -17,18 +17,13 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.AspNetCore;
|
||||
using OpenIddict.Abstractions;
|
||||
using OpenIddict.Core;
|
||||
using OpenIddict.Server;
|
||||
#if NETCOREAPP21
|
||||
using OpenIddictRequest = AspNet.Security.OpenIdConnect.Primitives.OpenIdConnectRequest;
|
||||
using OpenIdConnectDefaults = OpenIddict.Server.OpenIddictServerDefaults;
|
||||
using AspNet.Security.OpenIdConnect.Extensions;
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
#else
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
#endif
|
||||
using OpenIddict.Server.AspNetCore;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
@ -56,10 +51,11 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[HttpGet("/connect/authorize")]
|
||||
public async Task<IActionResult> Authorize(OpenIddictRequest openIdConnectRequest)
|
||||
public async Task<IActionResult> Authorize()
|
||||
{
|
||||
var request = HttpContext.GetOpenIddictServerRequest();
|
||||
// Retrieve the application details from the database.
|
||||
var application = await _applicationManager.FindByClientIdAsync(openIdConnectRequest.ClientId);
|
||||
var application = await _applicationManager.FindByClientIdAsync(request.ClientId);
|
||||
|
||||
if (application == null)
|
||||
{
|
||||
@ -74,9 +70,9 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
var userId = _userManager.GetUserId(User);
|
||||
if (!string.IsNullOrEmpty(
|
||||
await OpenIdExtensions.IsUserAuthorized(_authorizationManager, openIdConnectRequest, userId, application.Id)))
|
||||
await OpenIdExtensions.IsUserAuthorized(_authorizationManager, request, userId, application.Id)))
|
||||
{
|
||||
return await Authorize(openIdConnectRequest, "YES", false);
|
||||
return await Authorize("YES", false);
|
||||
}
|
||||
|
||||
// Flow the request_id to allow OpenIddict to restore
|
||||
@ -84,16 +80,16 @@ namespace BTCPayServer.Controllers
|
||||
return View(new AuthorizeViewModel
|
||||
{
|
||||
ApplicationName = await _applicationManager.GetDisplayNameAsync(application),
|
||||
RequestId = openIdConnectRequest.RequestId,
|
||||
Scope = openIdConnectRequest.GetScopes()
|
||||
RequestId = request.RequestId,
|
||||
Scope = request.GetScopes()
|
||||
});
|
||||
}
|
||||
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[HttpPost("/connect/authorize")]
|
||||
public async Task<IActionResult> Authorize(OpenIddictRequest openIdConnectRequest,
|
||||
string consent, bool createAuthorization = true)
|
||||
public async Task<IActionResult> Authorize(string consent, bool createAuthorization = true)
|
||||
{
|
||||
var request = HttpContext.GetOpenIddictServerRequest();
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
@ -118,26 +114,24 @@ namespace BTCPayServer.Controllers
|
||||
default:
|
||||
// Notify OpenIddict that the authorization grant has been denied by the resource owner
|
||||
// to redirect the user agent to the client application using the appropriate response_mode.
|
||||
return Forbid(OpenIdConnectDefaults.AuthenticationScheme);
|
||||
return Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
|
||||
}
|
||||
|
||||
|
||||
// Create a new authentication ticket.
|
||||
var ticket =
|
||||
await OpenIdExtensions.CreateAuthenticationTicket(_applicationManager, _authorizationManager,
|
||||
_IdentityOptions.Value, _signInManager,
|
||||
openIdConnectRequest, user);
|
||||
var principal = await _signInManager.CreateUserPrincipalAsync(user);
|
||||
principal = await _signInManager.CreateUserPrincipalAsync(user);
|
||||
principal.SetScopes(request.GetScopes().Restrict(principal));
|
||||
principal.SetDestinations(_IdentityOptions.Value);
|
||||
if (createAuthorization)
|
||||
{
|
||||
var application = await _applicationManager.FindByClientIdAsync(openIdConnectRequest.ClientId);
|
||||
var application = await _applicationManager.FindByClientIdAsync(request.ClientId);
|
||||
var authorization = await _authorizationManager.CreateAsync(User, user.Id, application.Id,
|
||||
type, ticket.GetScopes().ToImmutableArray(),
|
||||
ticket.Properties.Items.ToImmutableDictionary());
|
||||
ticket.SetInternalAuthorizationId(authorization.Id);
|
||||
type, principal.GetScopes().ToImmutableArray());
|
||||
principal.SetInternalAuthorizationId(authorization.Id);
|
||||
}
|
||||
|
||||
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
|
||||
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
|
||||
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,7 @@ using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using OpenIddict.Validation;
|
||||
#if !NETCOREAPP21
|
||||
using OpenIddictValidationDefaults = Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectDefaults;
|
||||
#endif
|
||||
using OpenIddict.Validation.AspNetCore;
|
||||
|
||||
namespace BTCPayServer.Controllers.RestApi
|
||||
{
|
||||
@ -111,7 +108,6 @@ namespace BTCPayServer.Controllers.RestApi
|
||||
|
||||
[Authorize(Policy = RestAPIPolicies.CanViewProfile,
|
||||
AuthenticationSchemes = AuthenticationSchemes.OpenId)]
|
||||
|
||||
[HttpGet(nameof(ScopeCanViewProfile))]
|
||||
public bool ScopeCanViewProfile() { return true; }
|
||||
|
||||
|
@ -10,10 +10,10 @@ namespace BTCPayServer
|
||||
{
|
||||
public static class OpenIddictExtensions
|
||||
{
|
||||
public static SecurityKey GetSigningKey(IConfiguration configuration)
|
||||
public static SecurityKey GetSigningKey(IConfiguration configuration, string fileName)
|
||||
{
|
||||
|
||||
var file = Path.Combine(configuration.GetDataDir(), "rsaparams");
|
||||
var file = Path.Combine(configuration.GetDataDir(), fileName);
|
||||
var rsa = new RSACryptoServiceProvider(2048);
|
||||
if (File.Exists(file))
|
||||
{
|
||||
@ -29,7 +29,9 @@ namespace BTCPayServer
|
||||
public static OpenIddictServerBuilder ConfigureSigningKey(this OpenIddictServerBuilder builder,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
return builder.AddSigningKey(GetSigningKey(configuration));
|
||||
return builder
|
||||
.AddSigningKey(GetSigningKey(configuration, "signing.rsaparams"))
|
||||
.AddEncryptionKey(GetSigningKey(configuration, "encrypting.rsaparams"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -276,21 +276,7 @@ namespace BTCPayServer.Hosting
|
||||
private static void AddBtcPayServerAuthenticationSchemes(this IServiceCollection services,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
|
||||
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
//Disabled so that Tor works witt JWT auth
|
||||
options.RequireHttpsMetadata = false;
|
||||
options.TokenValidationParameters.ValidateAudience = false;
|
||||
//we do not validate the issuer directly because btcpay can be accessed through multiple urls that we cannot predetermine
|
||||
options.TokenValidationParameters.ValidateIssuer = false;
|
||||
options.TokenValidationParameters.IssuerSigningKey =
|
||||
OpenIddictExtensions.GetSigningKey(configuration);
|
||||
options.IncludeErrorDetails = true;
|
||||
})
|
||||
.AddCookie()
|
||||
.AddBitpayAuthentication();
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
#if NETCOREAPP21
|
||||
using IWebHostEnvironment = Microsoft.AspNetCore.Hosting.IHostingEnvironment;
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
#else
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OpenIdConnectConstants = OpenIddict.Abstractions.OpenIddictConstants;
|
||||
#endif
|
||||
using OpenIddict.Validation.AspNetCore;
|
||||
using OpenIddict.Abstractions;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@ -22,7 +22,6 @@ using System.IO;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using BTCPayServer.Security;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using OpenIddict.Abstractions;
|
||||
using OpenIddict.EntityFrameworkCore.Models;
|
||||
using System.Net;
|
||||
using BTCPayServer.Authentication;
|
||||
@ -57,8 +56,8 @@ namespace BTCPayServer.Hosting
|
||||
services.AddMemoryCache();
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
ConfigureOpenIddict(services);
|
||||
|
||||
services.AddBTCPayServer(Configuration);
|
||||
@ -95,13 +94,13 @@ namespace BTCPayServer.Hosting
|
||||
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
|
||||
options.Lockout.MaxFailedAccessAttempts = 5;
|
||||
options.Lockout.AllowedForNewUsers = true;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
// Configure Identity to use the same JWT claims as OpenIddict instead
|
||||
// of the legacy WS-Federation claims it uses by default (ClaimTypes),
|
||||
// which saves you from doing the mapping in your authorization controller.
|
||||
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
|
||||
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
|
||||
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
|
||||
options.ClaimsIdentity.UserNameClaimType = OpenIddictConstants.Claims.Name;
|
||||
options.ClaimsIdentity.UserIdClaimType = OpenIddictConstants.Claims.Subject;
|
||||
options.ClaimsIdentity.RoleClaimType = OpenIddictConstants.Claims.Role;
|
||||
});
|
||||
// If the HTTPS certificate path is not set this logic will NOT be used and the default Kestrel binding logic will be.
|
||||
string httpsCertificateFilePath = Configuration.GetOrDefault<string>("HttpsCertificateFilePath", null);
|
||||
@ -123,7 +122,7 @@ namespace BTCPayServer.Hosting
|
||||
// Note that by design this is a fatal error condition that will cause the process to exit.
|
||||
throw new ConfigException($"The https certificate file could not be found at {httpsCertificateFilePath}.");
|
||||
}
|
||||
if(hasCertPath && useDefaultCertificate)
|
||||
if (hasCertPath && useDefaultCertificate)
|
||||
{
|
||||
throw new ConfigException($"Conflicting settings: if HttpsUseDefaultCertificate is true, HttpsCertificateFilePath should not be used");
|
||||
}
|
||||
@ -147,7 +146,7 @@ namespace BTCPayServer.Hosting
|
||||
|
||||
private void ConfigureOpenIddict(IServiceCollection services)
|
||||
{
|
||||
// Register the OpenIddict services.
|
||||
// Register the OpenIddict services.
|
||||
services.AddOpenIddict()
|
||||
.AddCore(options =>
|
||||
{
|
||||
@ -159,15 +158,6 @@ namespace BTCPayServer.Hosting
|
||||
})
|
||||
.AddServer(options =>
|
||||
{
|
||||
#if NETCOREAPP21
|
||||
options.EnableRequestCaching();
|
||||
//Disabled so that Tor works with OpenIddict too
|
||||
options.DisableHttpsRequirement();
|
||||
// Register the ASP.NET Core MVC binder used by OpenIddict.
|
||||
// Note: if you don't call this method, you won't be able to
|
||||
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
|
||||
options.UseMvc();
|
||||
#else
|
||||
options.UseAspNetCore()
|
||||
.EnableStatusCodePagesIntegration()
|
||||
.EnableAuthorizationEndpointPassthrough()
|
||||
@ -175,18 +165,11 @@ namespace BTCPayServer.Hosting
|
||||
.EnableTokenEndpointPassthrough()
|
||||
.EnableRequestCaching()
|
||||
.DisableTransportSecurityRequirement();
|
||||
#endif
|
||||
|
||||
// Enable the token endpoint (required to use the password flow).
|
||||
#if NETCOREAPP21
|
||||
options.EnableTokenEndpoint("/connect/token");
|
||||
options.EnableAuthorizationEndpoint("/connect/authorize");
|
||||
options.EnableLogoutEndpoint("/connect/logout");
|
||||
#else
|
||||
options.SetTokenEndpointUris("/connect/token");
|
||||
options.SetAuthorizationEndpointUris("/connect/authorize");
|
||||
options.SetLogoutEndpointUris("/connect/logout");
|
||||
#endif
|
||||
|
||||
//we do not care about these granular controls for now
|
||||
options.IgnoreScopePermissions();
|
||||
@ -198,36 +181,26 @@ namespace BTCPayServer.Hosting
|
||||
options.AllowPasswordFlow();
|
||||
options.AllowAuthorizationCodeFlow();
|
||||
options.UseRollingTokens();
|
||||
#if NETCOREAPP21
|
||||
options.UseJsonWebTokens();
|
||||
#endif
|
||||
|
||||
options.RegisterScopes(
|
||||
OpenIdConnectConstants.Scopes.OpenId,
|
||||
OpenIdConnectConstants.Scopes.OfflineAccess,
|
||||
OpenIdConnectConstants.Scopes.Email,
|
||||
OpenIdConnectConstants.Scopes.Profile,
|
||||
OpenIddictConstants.Scopes.Roles,
|
||||
OpenIddictConstants.Scopes.OpenId,
|
||||
RestAPIPolicies.BTCPayScopes.ViewStores,
|
||||
RestAPIPolicies.BTCPayScopes.CreateInvoices,
|
||||
RestAPIPolicies.BTCPayScopes.StoreManagement,
|
||||
RestAPIPolicies.BTCPayScopes.ViewApps,
|
||||
RestAPIPolicies.BTCPayScopes.ServerManagement,
|
||||
RestAPIPolicies.BTCPayScopes.AppManagement
|
||||
);
|
||||
#if NETCOREAPP21
|
||||
options.AddEventHandler<PasswordGrantTypeEventHandler>();
|
||||
options.AddEventHandler<AuthorizationCodeGrantTypeEventHandler>();
|
||||
options.AddEventHandler<RefreshTokenGrantTypeEventHandler>();
|
||||
options.AddEventHandler<ClientCredentialsGrantTypeEventHandler>();
|
||||
options.AddEventHandler<LogoutEventHandler>();
|
||||
#else
|
||||
options.AddEventHandler(PasswordGrantTypeEventHandler.Descriptor);
|
||||
options.AddEventHandler(AuthorizationCodeGrantTypeEventHandler.Descriptor);
|
||||
options.AddEventHandler(RefreshTokenGrantTypeEventHandler.Descriptor);
|
||||
options.AddEventHandler(OpenIdGrantHandlerCheckCanSignIn.Descriptor);
|
||||
options.AddEventHandler(ClientCredentialsGrantTypeEventHandler.Descriptor);
|
||||
options.AddEventHandler(LogoutEventHandler.Descriptor);
|
||||
#endif
|
||||
options.ConfigureSigningKey(Configuration);
|
||||
})
|
||||
.AddValidation(options =>
|
||||
{
|
||||
options.UseLocalServer();
|
||||
options.UseAspNetCore();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ namespace BTCPayServer
|
||||
l.AddFilter("Microsoft", LogLevel.Error);
|
||||
l.AddFilter("System.Net.Http.HttpClient", LogLevel.Critical);
|
||||
l.AddFilter("Microsoft.AspNetCore.Antiforgery.Internal", LogLevel.Critical);
|
||||
l.AddFilter("AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerHandler", LogLevel.Error);
|
||||
l.AddFilter("OpenIddict.Server.OpenIddictServerProvider", LogLevel.Error);
|
||||
l.AddProvider(new CustomConsoleLogProvider(processor));
|
||||
|
||||
// Use Serilog for debug log file.
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using OpenIddict.Validation;
|
||||
using OpenIddict.Validation.AspNetCore;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
@ -10,6 +11,6 @@ namespace BTCPayServer.Security
|
||||
{
|
||||
public const string Cookie = "Identity.Application";
|
||||
public const string Bitpay = "Bitpay";
|
||||
public const string OpenId = OpenIddictValidationDefaults.AuthenticationScheme;
|
||||
public const string OpenId = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,11 @@ namespace BTCPayServer.Security
|
||||
success = context.HasScopes(OpenIddictConstants.Scopes.Profile);
|
||||
break;
|
||||
case Policies.CanModifyStoreSettings.Key:
|
||||
if (!context.HasScopes(BTCPayScopes.StoreManagement))
|
||||
break;
|
||||
// TODO: It should be possible to grant permission to a specific store
|
||||
// we can do this by adding saving a claim with the specific store id
|
||||
// to the access_token
|
||||
string storeId = _HttpContext.GetImplicitStoreId();
|
||||
if (storeId == null)
|
||||
break;
|
||||
@ -79,7 +84,15 @@ namespace BTCPayServer.Security
|
||||
_HttpContext.SetStoreData(store);
|
||||
break;
|
||||
case Policies.CanModifyServerSettings.Key:
|
||||
success = context.User.HasClaim("role", Roles.ServerAdmin);
|
||||
if (!context.HasScopes(BTCPayScopes.ServerManagement))
|
||||
break;
|
||||
// For this authorization, we stil check in database because it is super sensitive.
|
||||
var user = await _userManager.GetUserAsync(context.User);
|
||||
if (user == null)
|
||||
break;
|
||||
if (!await _userManager.IsInRoleAsync(user, Roles.ServerAdmin))
|
||||
break;
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ namespace BTCPayServer.Security
|
||||
{
|
||||
public static bool HasScopes(this AuthorizationHandlerContext context, params string[] scopes)
|
||||
{
|
||||
return scopes.All(s => context.User.HasClaim(OpenIddictConstants.Claims.Scope, s));
|
||||
return scopes.All(s => context.User.HasClaim(c => c.Type == OpenIddictConstants.Claims.Scope && c.Value.Split(' ').Contains(s)));
|
||||
}
|
||||
public static string GetImplicitStoreId(this HttpContext httpContext)
|
||||
{
|
||||
|
@ -12,10 +12,8 @@
|
||||
{RestAPIPolicies.BTCPayScopes.StoreManagement, ("Manage your stores", "The app will be able to create, modify and delete all your stores.")},
|
||||
{RestAPIPolicies.BTCPayScopes.ViewStores, ("View your stores", "The app will be able to list and view all your stores.")},
|
||||
{RestAPIPolicies.BTCPayScopes.WalletManagement, ("Manage your wallet", "The app will be able to manage your wallet associate to stores. This includes configuring it, transaction creation and signing.")},
|
||||
{RestAPIPolicies.BTCPayScopes.ServerManagement, ("Manage your server", "The app will have total control on your server")},
|
||||
{RestAPIPolicies.BTCPayScopes.ViewInvoices, ("View your invoices", "The app will be able to list and view all your apps.")},
|
||||
{OpenIddictConstants.Scopes.Email, ("View your email", "The app will have access to your email.")},
|
||||
{OpenIddictConstants.Scopes.Profile, ("View your account", "The app will have access to your account details.")},
|
||||
{OpenIddictConstants.Scopes.Roles, ("View your roles", "The app will know if you are a server admin.")},
|
||||
};
|
||||
}
|
||||
<form method="post">
|
||||
|
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear/>
|
||||
<add key="aspnetcidev" value="https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json"/>
|
||||
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json"/>
|
||||
</packageSources>
|
||||
</configuration>
|
@ -1,5 +1,6 @@
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:2.1.505-alpine3.7 AS builder
|
||||
WORKDIR /source
|
||||
COPY nuget.config nuget.config
|
||||
COPY Build/Common.csproj Build/Common.csproj
|
||||
COPY BTCPayServer/BTCPayServer.csproj BTCPayServer/BTCPayServer.csproj
|
||||
COPY BTCPayServer.Common/BTCPayServer.Common.csproj BTCPayServer.Common/BTCPayServer.Common.csproj
|
||||
|
@ -4,6 +4,7 @@ RUN apt-get update \
|
||||
&& apt-get install -qq --no-install-recommends qemu qemu-user-static qemu-user binfmt-support
|
||||
|
||||
WORKDIR /source
|
||||
COPY nuget.config nuget.config
|
||||
COPY Build/Common.csproj Build/Common.csproj
|
||||
COPY BTCPayServer/BTCPayServer.csproj BTCPayServer/BTCPayServer.csproj
|
||||
COPY BTCPayServer.Common/BTCPayServer.Common.csproj BTCPayServer.Common/BTCPayServer.Common.csproj
|
||||
|
13
nuget.config
Normal file
13
nuget.config
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
<add key="arcade" value="https://dotnetfeed.blob.core.windows.net/dotnet-tools-internal/index.json" />
|
||||
<add key="dotnet-core" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
|
||||
<add key="azureadwebstacknightly" value="https://www.myget.org/F/azureadwebstacknightly/api/v3/index.json" />
|
||||
<add key="aspnetcidev" value="https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json" />
|
||||
<add key="Openiddict" value="https://www.myget.org/F/openiddict/api/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
|
Loading…
Reference in New Issue
Block a user