mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-12 02:08:32 +01:00
Request consent from user before giving application access to the user's data & services.
This commit is contained in:
parent
1447b5e8be
commit
c5227d9996
15 changed files with 410 additions and 188 deletions
|
@ -114,20 +114,27 @@ namespace BTCPayServer.Tests
|
||||||
$"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&nonce={Guid.NewGuid().ToString()}");
|
||||||
s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl);
|
s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl);
|
||||||
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||||
|
s.Driver.FindElement(By.Id("consent-yes")).Click();
|
||||||
var url = s.Driver.Url;
|
var url = s.Driver.Url;
|
||||||
var results = url.Split("#").Last().Split("&")
|
var results = url.Split("#").Last().Split("&")
|
||||||
.ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
|
.ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
|
||||||
await TestApiAgainstAccessToken(results["access_token"], tester, user);
|
await TestApiAgainstAccessToken(results["access_token"], tester, user);
|
||||||
|
|
||||||
|
|
||||||
//in Implicit mode, you renew your token by hitting the same endpoint but adding prompt=none. If you are still logged in on the site, you will receive a fresh token.
|
//in Implicit mode, you renew your token by hitting the same endpoint but adding prompt=none. If you are still logged in on the site, you will receive a fresh token.
|
||||||
var implicitAuthorizeUrlSilentModel = new Uri($"{implicitAuthorizeUrl.OriginalString}&prompt=none");
|
var implicitAuthorizeUrlSilentModel = new Uri($"{implicitAuthorizeUrl.OriginalString}&prompt=none");
|
||||||
s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl);
|
s.Driver.Navigate().GoToUrl(implicitAuthorizeUrlSilentModel);
|
||||||
url = s.Driver.Url;
|
url = s.Driver.Url;
|
||||||
results = url.Split("#").Last().Split("&").ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
|
results = url.Split("#").Last().Split("&").ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
|
||||||
await TestApiAgainstAccessToken(results["access_token"], tester, user);
|
await TestApiAgainstAccessToken(results["access_token"], tester, user);
|
||||||
|
|
||||||
LogoutFlow(tester, id, s);
|
LogoutFlow(tester, id, s);
|
||||||
|
|
||||||
|
s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl);
|
||||||
|
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||||
|
|
||||||
|
Assert.Throws<NoSuchElementException>(() => s.Driver.FindElement(By.Id("consent-yes")));
|
||||||
|
results = url.Split("#").Last().Split("&")
|
||||||
|
.ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
|
||||||
|
await TestApiAgainstAccessToken(results["access_token"], tester, user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +178,7 @@ namespace BTCPayServer.Tests
|
||||||
$"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&state={Guid.NewGuid().ToString()}");
|
||||||
s.Driver.Navigate().GoToUrl(authorizeUrl);
|
s.Driver.Navigate().GoToUrl(authorizeUrl);
|
||||||
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||||
|
s.Driver.FindElement(By.Id("consent-yes")).Click();
|
||||||
var url = s.Driver.Url;
|
var url = s.Driver.Url;
|
||||||
var results = url.Split("?").Last().Split("&")
|
var results = url.Split("?").Last().Split("&")
|
||||||
.ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
|
.ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
|
||||||
|
@ -204,6 +212,15 @@ namespace BTCPayServer.Tests
|
||||||
var refreshedAccessToken = await RefreshAnAccessToken(result.RefreshToken, httpClient, id, secret);
|
var refreshedAccessToken = await RefreshAnAccessToken(result.RefreshToken, httpClient, id, secret);
|
||||||
|
|
||||||
await TestApiAgainstAccessToken(refreshedAccessToken, tester, user);
|
await TestApiAgainstAccessToken(refreshedAccessToken, tester, user);
|
||||||
|
|
||||||
|
LogoutFlow(tester, id, s);
|
||||||
|
s.Driver.Navigate().GoToUrl(authorizeUrl);
|
||||||
|
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
|
||||||
|
|
||||||
|
Assert.Throws<NoSuchElementException>(() => s.Driver.FindElement(By.Id("consent-yes")));
|
||||||
|
results = url.Split("?").Last().Split("&")
|
||||||
|
.ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
|
||||||
|
Assert.True(results.ContainsKey("code"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
using AspNet.Security.OpenIdConnect.Primitives;
|
using AspNet.Security.OpenIdConnect.Primitives;
|
||||||
|
using BTCPayServer.Authentication.OpenId.Models;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using OpenIddict.Core;
|
||||||
|
|
||||||
namespace BTCPayServer.Authentication.OpenId
|
namespace BTCPayServer.Authentication.OpenId
|
||||||
{
|
{
|
||||||
public class AuthorizationCodeGrantTypeEventHandler : OpenIdGrantHandlerCheckCanSignIn
|
public class AuthorizationCodeGrantTypeEventHandler : OpenIdGrantHandlerCheckCanSignIn
|
||||||
{
|
{
|
||||||
public AuthorizationCodeGrantTypeEventHandler(SignInManager<ApplicationUser> signInManager,
|
public AuthorizationCodeGrantTypeEventHandler(
|
||||||
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(signInManager,
|
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||||
|
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager,
|
||||||
|
IOptions<IdentityOptions> identityOptions,
|
||||||
|
UserManager<ApplicationUser> userManager) : base(applicationManager, authorizationManager, signInManager,
|
||||||
identityOptions, userManager)
|
identityOptions, userManager)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using AspNet.Security.OpenIdConnect.Primitives;
|
|
||||||
using BTCPayServer.Models;
|
|
||||||
using BTCPayServer.Security;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
||||||
using Microsoft.AspNetCore.Identity;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using OpenIddict.Abstractions;
|
|
||||||
using OpenIddict.Server;
|
|
||||||
|
|
||||||
namespace BTCPayServer.Authentication.OpenId
|
|
||||||
{
|
|
||||||
public class AuthorizationEventHandler : BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleAuthorizationRequest>
|
|
||||||
{
|
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
|
||||||
|
|
||||||
public override async Task<OpenIddictServerEventState> HandleAsync(
|
|
||||||
OpenIddictServerEvents.HandleAuthorizationRequest notification)
|
|
||||||
{
|
|
||||||
if (!notification.Context.Request.IsAuthorizationRequest())
|
|
||||||
{
|
|
||||||
return OpenIddictServerEventState.Unhandled;
|
|
||||||
}
|
|
||||||
|
|
||||||
var auth = await notification.Context.HttpContext.AuthenticateAsync();
|
|
||||||
if (!auth.Succeeded)
|
|
||||||
{
|
|
||||||
// If the client application request promptless authentication,
|
|
||||||
// return an error indicating that the user is not logged in.
|
|
||||||
if (notification.Context.Request.HasPrompt(OpenIdConnectConstants.Prompts.None))
|
|
||||||
{
|
|
||||||
var properties = new AuthenticationProperties(new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
[OpenIdConnectConstants.Properties.Error] = OpenIdConnectConstants.Errors.LoginRequired,
|
|
||||||
[OpenIdConnectConstants.Properties.ErrorDescription] = "The user is not logged in."
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Ask OpenIddict to return a login_required error to the client application.
|
|
||||||
await notification.Context.HttpContext.ForbidAsync(properties);
|
|
||||||
notification.Context.HandleResponse();
|
|
||||||
return OpenIddictServerEventState.Handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
await notification.Context.HttpContext.ChallengeAsync();
|
|
||||||
notification.Context.HandleResponse();
|
|
||||||
return OpenIddictServerEventState.Handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the profile of the logged in user.
|
|
||||||
var user = await _userManager.GetUserAsync(auth.Principal);
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
notification.Context.Reject(
|
|
||||||
error: OpenIddictConstants.Errors.InvalidGrant,
|
|
||||||
description: "An internal error has occurred");
|
|
||||||
|
|
||||||
return OpenIddictServerEventState.Handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new authentication ticket.
|
|
||||||
var ticket = await CreateTicketAsync(notification.Context.Request, user);
|
|
||||||
|
|
||||||
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
|
|
||||||
notification.Context.Validate(ticket);
|
|
||||||
return OpenIddictServerEventState.Handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AuthorizationEventHandler(
|
|
||||||
UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager,
|
|
||||||
IOptions<IdentityOptions> identityOptions) : base(signInManager, identityOptions)
|
|
||||||
{
|
|
||||||
_userManager = userManager;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +1,11 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AspNet.Security.OpenIdConnect.Extensions;
|
|
||||||
using AspNet.Security.OpenIdConnect.Primitives;
|
using AspNet.Security.OpenIdConnect.Primitives;
|
||||||
|
using BTCPayServer.Authentication.OpenId.Models;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using OpenIddict.Abstractions;
|
using OpenIddict.Core;
|
||||||
using OpenIddict.Server;
|
using OpenIddict.Server;
|
||||||
|
|
||||||
namespace BTCPayServer.Authentication.OpenId
|
namespace BTCPayServer.Authentication.OpenId
|
||||||
|
@ -15,88 +13,30 @@ namespace BTCPayServer.Authentication.OpenId
|
||||||
public abstract class BaseOpenIdGrantHandler<T> : IOpenIddictServerEventHandler<T>
|
public abstract class BaseOpenIdGrantHandler<T> : IOpenIddictServerEventHandler<T>
|
||||||
where T : class, IOpenIddictServerEvent
|
where T : class, IOpenIddictServerEvent
|
||||||
{
|
{
|
||||||
|
private readonly OpenIddictApplicationManager<BTCPayOpenIdClient> _applicationManager;
|
||||||
|
private readonly OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> _authorizationManager;
|
||||||
protected readonly SignInManager<ApplicationUser> _signInManager;
|
protected readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
protected readonly IOptions<IdentityOptions> _identityOptions;
|
protected readonly IOptions<IdentityOptions> _identityOptions;
|
||||||
|
|
||||||
protected BaseOpenIdGrantHandler(SignInManager<ApplicationUser> signInManager,
|
protected BaseOpenIdGrantHandler(
|
||||||
|
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||||
|
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager,
|
||||||
IOptions<IdentityOptions> identityOptions)
|
IOptions<IdentityOptions> identityOptions)
|
||||||
{
|
{
|
||||||
|
_applicationManager = applicationManager;
|
||||||
|
_authorizationManager = authorizationManager;
|
||||||
_signInManager = signInManager;
|
_signInManager = signInManager;
|
||||||
_identityOptions = identityOptions;
|
_identityOptions = identityOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected async Task<AuthenticationTicket> CreateTicketAsync(
|
protected async Task<AuthenticationTicket> CreateTicketAsync(
|
||||||
OpenIdConnectRequest request, ApplicationUser user,
|
OpenIdConnectRequest request, ApplicationUser user,
|
||||||
AuthenticationProperties properties = null)
|
AuthenticationProperties properties = null)
|
||||||
{
|
{
|
||||||
// Create a new ClaimsPrincipal containing the claims that
|
return await OpenIdExtensions.CreateAuthenticationTicket(_applicationManager, _authorizationManager,
|
||||||
// will be used to create an id_token, a token or a code.
|
_identityOptions.Value, _signInManager, request, user, properties);
|
||||||
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())
|
|
||||||
{
|
|
||||||
// Note: in this sample, the granted scopes match the requested scope
|
|
||||||
// but you may want to allow the user to uncheck specific scopes.
|
|
||||||
// For that, simply restrict the list of scopes before calling SetScopes.
|
|
||||||
ticket.SetScopes(request.GetScopes());
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var claim in ticket.Principal.Claims)
|
|
||||||
{
|
|
||||||
claim.SetDestinations(GetDestinations(claim, ticket));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ticket;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<string> GetDestinations(Claim claim, AuthenticationTicket ticket)
|
|
||||||
{
|
|
||||||
// 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.Value.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 abstract Task<OpenIddictServerEventState> HandleAsync(T notification);
|
public abstract Task<OpenIddictServerEventState> HandleAsync(T notification);
|
||||||
|
|
|
@ -21,9 +21,12 @@ namespace BTCPayServer.Authentication.OpenId
|
||||||
|
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
|
||||||
public ClientCredentialsGrantTypeEventHandler(SignInManager<ApplicationUser> signInManager,
|
public ClientCredentialsGrantTypeEventHandler(
|
||||||
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||||
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(signInManager,
|
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager,
|
||||||
|
IOptions<IdentityOptions> identityOptions,
|
||||||
|
UserManager<ApplicationUser> userManager) : base(applicationManager, authorizationManager, signInManager,
|
||||||
identityOptions)
|
identityOptions)
|
||||||
{
|
{
|
||||||
_applicationManager = applicationManager;
|
_applicationManager = applicationManager;
|
||||||
|
|
|
@ -1,21 +1,28 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Authentication.OpenId.Models;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using OpenIddict.Core;
|
||||||
using OpenIddict.Server;
|
using OpenIddict.Server;
|
||||||
|
|
||||||
namespace BTCPayServer.Authentication.OpenId
|
namespace BTCPayServer.Authentication.OpenId
|
||||||
{
|
{
|
||||||
|
|
||||||
public class LogoutEventHandler : BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleLogoutRequest>
|
public class LogoutEventHandler : BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleLogoutRequest>
|
||||||
{
|
{
|
||||||
public LogoutEventHandler(SignInManager<ApplicationUser> signInManager, IOptions<IdentityOptions> identityOptions) : base(signInManager, identityOptions)
|
public LogoutEventHandler(
|
||||||
|
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||||
|
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager, IOptions<IdentityOptions> identityOptions) : base(
|
||||||
|
applicationManager, authorizationManager,
|
||||||
|
signInManager, identityOptions)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<OpenIddictServerEventState> HandleAsync(OpenIddictServerEvents.HandleLogoutRequest notification)
|
public override async Task<OpenIddictServerEventState> HandleAsync(
|
||||||
|
OpenIddictServerEvents.HandleLogoutRequest notification)
|
||||||
{
|
{
|
||||||
// Ask ASP.NET Core Identity to delete the local and external cookies created
|
// Ask ASP.NET Core Identity to delete the local and external cookies created
|
||||||
// when the user agent is redirected from the external identity provider
|
// when the user agent is redirected from the external identity provider
|
||||||
|
|
139
BTCPayServer/Authentication/OpenId/OpenIdExtensions.cs
Normal file
139
BTCPayServer/Authentication/OpenId/OpenIdExtensions.cs
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AspNet.Security.OpenIdConnect.Extensions;
|
||||||
|
using AspNet.Security.OpenIdConnect.Primitives;
|
||||||
|
using BTCPayServer.Authentication.OpenId.Models;
|
||||||
|
using BTCPayServer.Models;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using OpenIddict.Abstractions;
|
||||||
|
using OpenIddict.Core;
|
||||||
|
using OpenIddict.Server;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Authentication.OpenId
|
||||||
|
{
|
||||||
|
public static class OpenIdExtensions
|
||||||
|
{
|
||||||
|
public static async Task<AuthenticationTicket> CreateAuthenticationTicket(
|
||||||
|
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||||
|
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||||
|
IdentityOptions identityOptions,
|
||||||
|
SignInManager<ApplicationUser> signInManager,
|
||||||
|
OpenIdConnectRequest request,
|
||||||
|
ApplicationUser user,
|
||||||
|
AuthenticationProperties properties = null)
|
||||||
|
{
|
||||||
|
// Create a new ClaimsPrincipal containing the claims that
|
||||||
|
// will be used to create an id_token, a token or a code.
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
else if (request.IsAuthorizationCodeGrantType() &&
|
||||||
|
string.IsNullOrEmpty(ticket.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var claim in ticket.Principal.Claims)
|
||||||
|
{
|
||||||
|
claim.SetDestinations(GetDestinations(identityOptions, claim, ticket));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> GetDestinations(IdentityOptions identityOptions, Claim claim,
|
||||||
|
AuthenticationTicket ticket)
|
||||||
|
{
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
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)));
|
||||||
|
|
||||||
|
|
||||||
|
if (authorizations.Length > 0)
|
||||||
|
{
|
||||||
|
var scopeTasks = authorizations.Select(authorization =>
|
||||||
|
(authorizationManager.GetScopesAsync(authorization).AsTask(), authorization.Id));
|
||||||
|
await Task.WhenAll(scopeTasks.Select((tuple) => tuple.Item1));
|
||||||
|
|
||||||
|
var authorizationsWithSufficientScopes = scopeTasks
|
||||||
|
.Select((tuple) => (tuple.Id, Scopes: tuple.Item1.Result))
|
||||||
|
.Where((tuple) => !request.GetScopes().Except(tuple.Scopes).Any());
|
||||||
|
|
||||||
|
if (authorizationsWithSufficientScopes.Any())
|
||||||
|
{
|
||||||
|
return authorizationsWithSufficientScopes.First().Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,12 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AspNet.Security.OpenIdConnect.Primitives;
|
using AspNet.Security.OpenIdConnect.Primitives;
|
||||||
|
using BTCPayServer.Authentication.OpenId.Models;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using OpenIddict.Abstractions;
|
using OpenIddict.Abstractions;
|
||||||
|
using OpenIddict.Core;
|
||||||
using OpenIddict.Server;
|
using OpenIddict.Server;
|
||||||
|
|
||||||
namespace BTCPayServer.Authentication.OpenId
|
namespace BTCPayServer.Authentication.OpenId
|
||||||
|
@ -14,8 +16,12 @@ namespace BTCPayServer.Authentication.OpenId
|
||||||
{
|
{
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
|
||||||
protected OpenIdGrantHandlerCheckCanSignIn(SignInManager<ApplicationUser> signInManager,
|
protected OpenIdGrantHandlerCheckCanSignIn(
|
||||||
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(signInManager,
|
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||||
|
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager,
|
||||||
|
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(
|
||||||
|
applicationManager, authorizationManager, signInManager,
|
||||||
identityOptions)
|
identityOptions)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
|
@ -36,7 +42,6 @@ namespace BTCPayServer.Authentication.OpenId
|
||||||
var scheme = notification.Context.Scheme.Name;
|
var scheme = notification.Context.Scheme.Name;
|
||||||
var authenticateResult = (await notification.Context.HttpContext.AuthenticateAsync(scheme));
|
var authenticateResult = (await notification.Context.HttpContext.AuthenticateAsync(scheme));
|
||||||
|
|
||||||
|
|
||||||
var user = await _userManager.GetUserAsync(authenticateResult.Principal);
|
var user = await _userManager.GetUserAsync(authenticateResult.Principal);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AspNet.Security.OpenIdConnect.Primitives;
|
using AspNet.Security.OpenIdConnect.Primitives;
|
||||||
|
using BTCPayServer.Authentication.OpenId.Models;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using BTCPayServer.Services.U2F;
|
using BTCPayServer.Services.U2F;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using OpenIddict.Abstractions;
|
using OpenIddict.Abstractions;
|
||||||
|
using OpenIddict.Core;
|
||||||
using OpenIddict.Server;
|
using OpenIddict.Server;
|
||||||
|
|
||||||
namespace BTCPayServer.Authentication.OpenId
|
namespace BTCPayServer.Authentication.OpenId
|
||||||
|
@ -14,9 +16,13 @@ namespace BTCPayServer.Authentication.OpenId
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
private readonly U2FService _u2FService;
|
private readonly U2FService _u2FService;
|
||||||
|
|
||||||
public PasswordGrantTypeEventHandler(SignInManager<ApplicationUser> signInManager,
|
public PasswordGrantTypeEventHandler(
|
||||||
|
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||||
|
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager,
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
IOptions<IdentityOptions> identityOptions, U2FService u2FService) : base(signInManager, identityOptions)
|
IOptions<IdentityOptions> identityOptions, U2FService u2FService) : base(applicationManager,
|
||||||
|
authorizationManager, signInManager, identityOptions)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_u2FService = u2FService;
|
_u2FService = u2FService;
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
using AspNet.Security.OpenIdConnect.Primitives;
|
using AspNet.Security.OpenIdConnect.Primitives;
|
||||||
|
using BTCPayServer.Authentication.OpenId.Models;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using OpenIddict.Core;
|
||||||
|
|
||||||
namespace BTCPayServer.Authentication.OpenId
|
namespace BTCPayServer.Authentication.OpenId
|
||||||
{
|
{
|
||||||
public class RefreshTokenGrantTypeEventHandler : OpenIdGrantHandlerCheckCanSignIn
|
public class RefreshTokenGrantTypeEventHandler : OpenIdGrantHandlerCheckCanSignIn
|
||||||
{
|
{
|
||||||
public RefreshTokenGrantTypeEventHandler(SignInManager<ApplicationUser> signInManager,
|
public RefreshTokenGrantTypeEventHandler(
|
||||||
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(signInManager,
|
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||||
|
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager,
|
||||||
|
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(
|
||||||
|
applicationManager, authorizationManager, signInManager,
|
||||||
identityOptions, userManager)
|
identityOptions, userManager)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
136
BTCPayServer/Controllers/AuthorizationController.cs
Normal file
136
BTCPayServer/Controllers/AuthorizationController.cs
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
* See https://github.com/openiddict/openiddict-core for more information concerning
|
||||||
|
* the license and the contributors participating to this project.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AspNet.Security.OpenIdConnect.Extensions;
|
||||||
|
using AspNet.Security.OpenIdConnect.Primitives;
|
||||||
|
using BTCPayServer.Authentication.OpenId;
|
||||||
|
using BTCPayServer.Authentication.OpenId.Models;
|
||||||
|
using BTCPayServer.Models;
|
||||||
|
using BTCPayServer.Models.Authorization;
|
||||||
|
using BTCPayServer.Security;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using OpenIddict.Abstractions;
|
||||||
|
using OpenIddict.Core;
|
||||||
|
using OpenIddict.Server;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Controllers
|
||||||
|
{
|
||||||
|
public class AuthorizationController : Controller
|
||||||
|
{
|
||||||
|
private readonly OpenIddictApplicationManager<BTCPayOpenIdClient> _applicationManager;
|
||||||
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
private readonly OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> _authorizationManager;
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly IOptions<IdentityOptions> _IdentityOptions;
|
||||||
|
|
||||||
|
public AuthorizationController(
|
||||||
|
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager,
|
||||||
|
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
IOptions<IdentityOptions> identityOptions)
|
||||||
|
{
|
||||||
|
_applicationManager = applicationManager;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
_authorizationManager = authorizationManager;
|
||||||
|
_userManager = userManager;
|
||||||
|
_IdentityOptions = identityOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||||
|
[HttpGet("/connect/authorize")]
|
||||||
|
public async Task<IActionResult> Authorize(OpenIdConnectRequest request)
|
||||||
|
{
|
||||||
|
// Retrieve the application details from the database.
|
||||||
|
var application = await _applicationManager.FindByClientIdAsync(request.ClientId);
|
||||||
|
|
||||||
|
if (application == null)
|
||||||
|
{
|
||||||
|
return View("Error",
|
||||||
|
new ErrorViewModel
|
||||||
|
{
|
||||||
|
Error = OpenIddictConstants.Errors.InvalidClient,
|
||||||
|
ErrorDescription =
|
||||||
|
"Details concerning the calling client application cannot be found in the database"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var userId = _userManager.GetUserId(User);
|
||||||
|
if (!string.IsNullOrEmpty(
|
||||||
|
await OpenIdExtensions.IsUserAuthorized(_authorizationManager, request, userId, application.Id)))
|
||||||
|
{
|
||||||
|
return await Authorize(request, "YES", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flow the request_id to allow OpenIddict to restore
|
||||||
|
// the original authorization request from the cache.
|
||||||
|
return View(new AuthorizeViewModel
|
||||||
|
{
|
||||||
|
ApplicationName = await _applicationManager.GetDisplayNameAsync(application),
|
||||||
|
RequestId = request.RequestId,
|
||||||
|
Scope = request.Scope
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||||
|
[HttpPost("/connect/authorize")]
|
||||||
|
public async Task<IActionResult> Authorize(OpenIdConnectRequest request,
|
||||||
|
string consent, bool createAuthorization = true)
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return View("Error",
|
||||||
|
new ErrorViewModel
|
||||||
|
{
|
||||||
|
Error = OpenIddictConstants.Errors.ServerError,
|
||||||
|
ErrorDescription = "The specified user could not be found"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
string type = null;
|
||||||
|
switch (consent.ToUpperInvariant())
|
||||||
|
{
|
||||||
|
case "YESTEMPORARY":
|
||||||
|
type = OpenIddictConstants.AuthorizationTypes.AdHoc;
|
||||||
|
break;
|
||||||
|
case "YES":
|
||||||
|
type = OpenIddictConstants.AuthorizationTypes.Permanent;
|
||||||
|
break;
|
||||||
|
case "NO":
|
||||||
|
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(OpenIddictServerDefaults.AuthenticationScheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Create a new authentication ticket.
|
||||||
|
var ticket =
|
||||||
|
await OpenIdExtensions.CreateAuthenticationTicket(_applicationManager, _authorizationManager,
|
||||||
|
_IdentityOptions.Value, _signInManager,
|
||||||
|
request, user);
|
||||||
|
if (createAuthorization)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
|
||||||
|
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,30 +10,21 @@ namespace BTCPayServer
|
||||||
{
|
{
|
||||||
public static class OpenIddictExtensions
|
public static class OpenIddictExtensions
|
||||||
{
|
{
|
||||||
private static SecurityKey _key = null;
|
|
||||||
public static SecurityKey GetSigningKey(IConfiguration configuration)
|
public static SecurityKey GetSigningKey(IConfiguration configuration)
|
||||||
{
|
{
|
||||||
if (_key != null)
|
|
||||||
{
|
|
||||||
return _key;
|
|
||||||
}
|
|
||||||
var file = Path.Combine(configuration.GetDataDir(), "rsaparams");
|
var file = Path.Combine(configuration.GetDataDir(), "rsaparams");
|
||||||
|
var rsa = new RSACryptoServiceProvider(2048);
|
||||||
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(2048);
|
|
||||||
|
|
||||||
if (File.Exists(file))
|
if (File.Exists(file))
|
||||||
{
|
{
|
||||||
RSA.FromXmlString2(File.ReadAllText(file));
|
rsa.FromXmlString2(File.ReadAllText(file));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var contents = RSA.ToXmlString2(true);
|
var contents = rsa.ToXmlString2(true);
|
||||||
File.WriteAllText(file, contents);
|
File.WriteAllText(file, contents);
|
||||||
}
|
}
|
||||||
|
return new RsaSecurityKey(rsa.ExportParameters(true));;
|
||||||
RSAParameters KeyParam = RSA.ExportParameters(true);
|
|
||||||
_key = new RsaSecurityKey(KeyParam);
|
|
||||||
return _key;
|
|
||||||
}
|
}
|
||||||
public static OpenIddictServerBuilder ConfigureSigningKey(this OpenIddictServerBuilder builder,
|
public static OpenIddictServerBuilder ConfigureSigningKey(this OpenIddictServerBuilder builder,
|
||||||
IConfiguration configuration)
|
IConfiguration configuration)
|
||||||
|
|
|
@ -147,7 +147,7 @@ namespace BTCPayServer.Hosting
|
||||||
})
|
})
|
||||||
.AddServer(options =>
|
.AddServer(options =>
|
||||||
{
|
{
|
||||||
|
options.EnableRequestCaching();
|
||||||
//Disabled so that Tor works with OpenIddict too
|
//Disabled so that Tor works with OpenIddict too
|
||||||
options.DisableHttpsRequirement();
|
options.DisableHttpsRequirement();
|
||||||
// Register the ASP.NET Core MVC binder used by OpenIddict.
|
// Register the ASP.NET Core MVC binder used by OpenIddict.
|
||||||
|
@ -182,7 +182,6 @@ namespace BTCPayServer.Hosting
|
||||||
options.AddEventHandler<AuthorizationCodeGrantTypeEventHandler>();
|
options.AddEventHandler<AuthorizationCodeGrantTypeEventHandler>();
|
||||||
options.AddEventHandler<RefreshTokenGrantTypeEventHandler>();
|
options.AddEventHandler<RefreshTokenGrantTypeEventHandler>();
|
||||||
options.AddEventHandler<ClientCredentialsGrantTypeEventHandler>();
|
options.AddEventHandler<ClientCredentialsGrantTypeEventHandler>();
|
||||||
options.AddEventHandler<AuthorizationEventHandler>();
|
|
||||||
options.AddEventHandler<LogoutEventHandler>();
|
options.AddEventHandler<LogoutEventHandler>();
|
||||||
|
|
||||||
options.ConfigureSigningKey(Configuration);
|
options.ConfigureSigningKey(Configuration);
|
||||||
|
|
14
BTCPayServer/Models/Authorization/AuthorizeViewModel.cs
Normal file
14
BTCPayServer/Models/Authorization/AuthorizeViewModel.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Models.Authorization
|
||||||
|
{
|
||||||
|
public class AuthorizeViewModel
|
||||||
|
{
|
||||||
|
[Display(Name = "Application")] public string ApplicationName { get; set; }
|
||||||
|
|
||||||
|
[BindNever] public string RequestId { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Scope")] public string Scope { get; set; }
|
||||||
|
}
|
||||||
|
}
|
31
BTCPayServer/Views/Authorization/Authorize.cshtml
Normal file
31
BTCPayServer/Views/Authorization/Authorize.cshtml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
@model BTCPayServer.Models.Authorization.AuthorizeViewModel
|
||||||
|
<form method="post">
|
||||||
|
<input type="hidden" name="request_id" value="@Model.RequestId"/>
|
||||||
|
<section>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 text-center">
|
||||||
|
<h2 class="section-heading">Authorization Request</h2>
|
||||||
|
<hr class="primary">
|
||||||
|
<p>@Model.ApplicationName is requesting access to your account.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 text-center">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-lg btn-success" name="consent" id="consent-yes" type="submit" value="Yes">Authorize app</button>
|
||||||
|
<button type="button" class="btn btn-success dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="sr-only">Toggle Dropdown</span>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<button class="dropdown-item" name="consent" id="consent-yes-temporary" type="submit" value="YesTemporary">Authorize app until session ends</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-lg btn-secondary" id="consent-no" name="consent" type="submit" value="No">Cancel</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
Loading…
Add table
Reference in a new issue