diff --git a/BTCPayServer.Data/BTCPayServer.Data.csproj b/BTCPayServer.Data/BTCPayServer.Data.csproj index 6639da853..a8612e9cf 100644 --- a/BTCPayServer.Data/BTCPayServer.Data.csproj +++ b/BTCPayServer.Data/BTCPayServer.Data.csproj @@ -7,7 +7,6 @@ - diff --git a/BTCPayServer.Data/Data/ApplicationDbContext.cs b/BTCPayServer.Data/Data/ApplicationDbContext.cs index e245b85bc..b00c8e1ba 100644 --- a/BTCPayServer.Data/Data/ApplicationDbContext.cs +++ b/BTCPayServer.Data/Data/ApplicationDbContext.cs @@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.EntityFrameworkCore.Infrastructure; -using OpenIddict.EntityFrameworkCore.Models; namespace BTCPayServer.Data { @@ -261,28 +260,6 @@ namespace BTCPayServer.Data .HasOne(o => o.WalletData) .WithMany(w => w.WalletTransactions).OnDelete(DeleteBehavior.Cascade); - builder.UseOpenIddict, BTCPayOpenIdToken, string>(); - - if (Database.IsSqlite() && !_designTime) - { - // SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations - // here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations - // To work around this, when the Sqlite database provider is used, all model properties of type DateTimeOffset - // use the DateTimeOffsetToBinaryConverter - // Based on: https://github.com/aspnet/EntityFrameworkCore/issues/10784#issuecomment-415769754 - // This only supports millisecond precision, but should be sufficient for most use cases. - foreach (var entityType in builder.Model.GetEntityTypes()) - { - var properties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == typeof(DateTimeOffset)); - foreach (var property in properties) - { - builder - .Entity(entityType.Name) - .Property(property.Name) - .HasConversion(new Microsoft.EntityFrameworkCore.Storage.ValueConversion.DateTimeOffsetToBinaryConverter()); - } - } - } } } diff --git a/BTCPayServer.Data/Data/ApplicationUser.cs b/BTCPayServer.Data/Data/ApplicationUser.cs index 1286e147c..6c9c28fbc 100644 --- a/BTCPayServer.Data/Data/ApplicationUser.cs +++ b/BTCPayServer.Data/Data/ApplicationUser.cs @@ -20,9 +20,7 @@ namespace BTCPayServer.Data { get; set; } - - public List OpenIdClients { get; set; } - + public List StoredFiles { get; diff --git a/BTCPayServer.Data/Data/BTCPayOpenIdAuthorization.cs b/BTCPayServer.Data/Data/BTCPayOpenIdAuthorization.cs deleted file mode 100644 index cfc3c3bfc..000000000 --- a/BTCPayServer.Data/Data/BTCPayOpenIdAuthorization.cs +++ /dev/null @@ -1,6 +0,0 @@ -using OpenIddict.EntityFrameworkCore.Models; - -namespace BTCPayServer.Data -{ - public class BTCPayOpenIdAuthorization : OpenIddictAuthorization { } -} diff --git a/BTCPayServer.Data/Data/BTCPayOpenIdClient.cs b/BTCPayServer.Data/Data/BTCPayOpenIdClient.cs deleted file mode 100644 index eb4e94b25..000000000 --- a/BTCPayServer.Data/Data/BTCPayOpenIdClient.cs +++ /dev/null @@ -1,10 +0,0 @@ -using OpenIddict.EntityFrameworkCore.Models; - -namespace BTCPayServer.Data -{ - public class BTCPayOpenIdClient: OpenIddictApplication - { - public string ApplicationUserId { get; set; } - public ApplicationUser ApplicationUser { get; set; } - } -} diff --git a/BTCPayServer.Data/Data/BTCPayOpenIdToken.cs b/BTCPayServer.Data/Data/BTCPayOpenIdToken.cs deleted file mode 100644 index 94951a118..000000000 --- a/BTCPayServer.Data/Data/BTCPayOpenIdToken.cs +++ /dev/null @@ -1,6 +0,0 @@ -using OpenIddict.EntityFrameworkCore.Models; - -namespace BTCPayServer.Data -{ - public class BTCPayOpenIdToken : OpenIddictToken { } -} diff --git a/BTCPayServer.Data/Migrations/20200224134444_Remove_OpenIddict.cs b/BTCPayServer.Data/Migrations/20200224134444_Remove_OpenIddict.cs new file mode 100644 index 000000000..78f323529 --- /dev/null +++ b/BTCPayServer.Data/Migrations/20200224134444_Remove_OpenIddict.cs @@ -0,0 +1,169 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace BTCPayServer.Migrations +{ + public partial class Remove_OpenIddict : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "OpenIddictScopes"); + + migrationBuilder.DropTable( + name: "OpenIddictTokens"); + + migrationBuilder.DropTable( + name: "OpenIddictAuthorizations"); + + migrationBuilder.DropTable( + name: "OpenIddictApplications"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + int? maxLength = this.IsMySql(migrationBuilder.ActiveProvider) ? (int?)255 : null; + migrationBuilder.CreateTable( + name: "OpenIddictApplications", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false, maxLength: maxLength), + ApplicationUserId = table.Column(type: "TEXT", nullable: true, maxLength: maxLength), + ClientId = table.Column(type: "TEXT", maxLength: 100, nullable: false), + ClientSecret = table.Column(type: "TEXT", nullable: true), + ConcurrencyToken = table.Column(type: "TEXT", maxLength: 50, nullable: true), + ConsentType = table.Column(type: "TEXT", nullable: true), + DisplayName = table.Column(type: "TEXT", nullable: true), + Permissions = table.Column(type: "TEXT", nullable: true), + PostLogoutRedirectUris = table.Column(type: "TEXT", nullable: true), + Properties = table.Column(type: "TEXT", nullable: true), + RedirectUris = table.Column(type: "TEXT", nullable: true), + Requirements = table.Column(type: "TEXT", nullable: true), + Type = table.Column(type: "TEXT", maxLength: 25, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictApplications", x => x.Id); + table.ForeignKey( + name: "FK_OpenIddictApplications_AspNetUsers_ApplicationUserId", + column: x => x.ApplicationUserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictScopes", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false, maxLength: maxLength), + ConcurrencyToken = table.Column(type: "TEXT", maxLength: 50, nullable: true), + Description = table.Column(type: "TEXT", nullable: true), + DisplayName = table.Column(type: "TEXT", nullable: true), + Name = table.Column(type: "TEXT", maxLength: 200, nullable: false), + Properties = table.Column(type: "TEXT", nullable: true), + Resources = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictScopes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictAuthorizations", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false, maxLength: maxLength), + ApplicationId = table.Column(type: "TEXT", nullable: true, maxLength: maxLength), + ConcurrencyToken = table.Column(type: "TEXT", maxLength: 50, nullable: true), + Properties = table.Column(type: "TEXT", nullable: true), + Scopes = table.Column(type: "TEXT", nullable: true), + Status = table.Column(type: "TEXT", maxLength: 25, nullable: false), + Subject = table.Column(type: "TEXT", maxLength: 450, nullable: true), + Type = table.Column(type: "TEXT", maxLength: 25, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id); + table.ForeignKey( + name: "FK_OpenIddictAuthorizations_OpenIddictApplications_ApplicationId", + column: x => x.ApplicationId, + principalTable: "OpenIddictApplications", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictTokens", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false, maxLength: maxLength), + ApplicationId = table.Column(type: "TEXT", nullable: true, maxLength: maxLength), + AuthorizationId = table.Column(type: "TEXT", nullable: true, maxLength: maxLength), + ConcurrencyToken = table.Column(type: "TEXT", maxLength: 50, nullable: true), + CreationDate = table.Column(type: "TEXT", nullable: true), + ExpirationDate = table.Column(type: "TEXT", nullable: true), + Payload = table.Column(type: "TEXT", nullable: true), + Properties = table.Column(type: "TEXT", nullable: true), + ReferenceId = table.Column(type: "TEXT", maxLength: 100, nullable: true), + Status = table.Column(type: "TEXT", maxLength: 25, nullable: false), + Subject = table.Column(type: "TEXT", maxLength: 450, nullable: true), + Type = table.Column(type: "TEXT", maxLength: 25, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictTokens", x => x.Id); + table.ForeignKey( + name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId", + column: x => x.ApplicationId, + principalTable: "OpenIddictApplications", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId", + column: x => x.AuthorizationId, + principalTable: "OpenIddictAuthorizations", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictApplications_ApplicationUserId", + table: "OpenIddictApplications", + column: "ApplicationUserId"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictApplications_ClientId", + table: "OpenIddictApplications", + column: "ClientId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type", + table: "OpenIddictAuthorizations", + columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictScopes_Name", + table: "OpenIddictScopes", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_AuthorizationId", + table: "OpenIddictTokens", + column: "AuthorizationId"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ReferenceId", + table: "OpenIddictTokens", + column: "ReferenceId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type", + table: "OpenIddictTokens", + columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + } + } +} diff --git a/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs b/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs index c71ba52bd..b2a26241a 100644 --- a/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/BTCPayServer.Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -160,164 +160,6 @@ namespace BTCPayServer.Migrations b.ToTable("AspNetUsers"); }); - modelBuilder.Entity("BTCPayServer.Data.BTCPayOpenIdAuthorization", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ApplicationId") - .HasColumnType("TEXT"); - - b.Property("ConcurrencyToken") - .IsConcurrencyToken() - .HasColumnType("TEXT") - .HasMaxLength(50); - - b.Property("Properties") - .HasColumnType("TEXT"); - - b.Property("Scopes") - .HasColumnType("TEXT"); - - b.Property("Status") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(25); - - b.Property("Subject") - .HasColumnType("TEXT") - .HasMaxLength(450); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(25); - - b.HasKey("Id"); - - b.HasIndex("ApplicationId", "Status", "Subject", "Type"); - - b.ToTable("OpenIddictAuthorizations"); - }); - - modelBuilder.Entity("BTCPayServer.Data.BTCPayOpenIdClient", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ApplicationUserId") - .HasColumnType("TEXT"); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(100); - - b.Property("ClientSecret") - .HasColumnType("TEXT"); - - b.Property("ConcurrencyToken") - .IsConcurrencyToken() - .HasColumnType("TEXT") - .HasMaxLength(50); - - b.Property("ConsentType") - .HasColumnType("TEXT"); - - b.Property("DisplayName") - .HasColumnType("TEXT"); - - b.Property("Permissions") - .HasColumnType("TEXT"); - - b.Property("PostLogoutRedirectUris") - .HasColumnType("TEXT"); - - b.Property("Properties") - .HasColumnType("TEXT"); - - b.Property("RedirectUris") - .HasColumnType("TEXT"); - - b.Property("Requirements") - .HasColumnType("TEXT"); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(25); - - b.HasKey("Id"); - - b.HasIndex("ApplicationUserId"); - - b.HasIndex("ClientId") - .IsUnique(); - - b.ToTable("OpenIddictApplications"); - }); - - modelBuilder.Entity("BTCPayServer.Data.BTCPayOpenIdToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ApplicationId") - .HasColumnType("TEXT"); - - b.Property("AuthorizationId") - .HasColumnType("TEXT"); - - b.Property("ConcurrencyToken") - .IsConcurrencyToken() - .HasColumnType("TEXT") - .HasMaxLength(50); - - b.Property("CreationDate") - .HasColumnType("TEXT"); - - b.Property("ExpirationDate") - .HasColumnType("TEXT"); - - b.Property("Payload") - .HasColumnType("TEXT"); - - b.Property("Properties") - .HasColumnType("TEXT"); - - b.Property("ReferenceId") - .HasColumnType("TEXT") - .HasMaxLength(100); - - b.Property("Status") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(25); - - b.Property("Subject") - .HasColumnType("TEXT") - .HasMaxLength(450); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(25); - - b.HasKey("Id"); - - b.HasIndex("AuthorizationId"); - - b.HasIndex("ReferenceId") - .IsUnique(); - - b.HasIndex("ApplicationId", "Status", "Subject", "Type"); - - b.ToTable("OpenIddictTokens"); - }); - modelBuilder.Entity("BTCPayServer.Data.HistoricalAddressInvoiceData", b => { b.Property("InvoiceDataId") @@ -812,42 +654,6 @@ namespace BTCPayServer.Migrations b.ToTable("AspNetUserTokens"); }); - modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ConcurrencyToken") - .IsConcurrencyToken() - .HasColumnType("TEXT") - .HasMaxLength(50); - - b.Property("Description") - .HasColumnType("TEXT"); - - b.Property("DisplayName") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(200); - - b.Property("Properties") - .HasColumnType("TEXT"); - - b.Property("Resources") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.ToTable("OpenIddictScopes"); - }); - modelBuilder.Entity("BTCPayServer.Data.APIKeyData", b => { b.HasOne("BTCPayServer.Data.StoreData", "StoreData") @@ -877,31 +683,6 @@ namespace BTCPayServer.Migrations .OnDelete(DeleteBehavior.Cascade); }); - modelBuilder.Entity("BTCPayServer.Data.BTCPayOpenIdAuthorization", b => - { - b.HasOne("BTCPayServer.Data.BTCPayOpenIdClient", "Application") - .WithMany("Authorizations") - .HasForeignKey("ApplicationId"); - }); - - modelBuilder.Entity("BTCPayServer.Data.BTCPayOpenIdClient", b => - { - b.HasOne("BTCPayServer.Data.ApplicationUser", "ApplicationUser") - .WithMany("OpenIdClients") - .HasForeignKey("ApplicationUserId"); - }); - - modelBuilder.Entity("BTCPayServer.Data.BTCPayOpenIdToken", b => - { - b.HasOne("BTCPayServer.Data.BTCPayOpenIdClient", "Application") - .WithMany("Tokens") - .HasForeignKey("ApplicationId"); - - b.HasOne("BTCPayServer.Data.BTCPayOpenIdAuthorization", "Authorization") - .WithMany("Tokens") - .HasForeignKey("AuthorizationId"); - }); - modelBuilder.Entity("BTCPayServer.Data.HistoricalAddressInvoiceData", b => { b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData") diff --git a/BTCPayServer.Tests/AuthenticationTests.cs b/BTCPayServer.Tests/AuthenticationTests.cs deleted file mode 100644 index 805db57f8..000000000 --- a/BTCPayServer.Tests/AuthenticationTests.cs +++ /dev/null @@ -1,428 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Threading.Tasks; -using System.Security.Claims; -using BTCPayServer.Tests.Logging; -using Xunit; -using Xunit.Abstractions; -using System.Net.Http; -using System.Net.Http.Headers; -using BTCPayServer.Data; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using OpenIddict.Abstractions; -using OpenQA.Selenium; -using Microsoft.AspNetCore.Identity; - -namespace BTCPayServer.Tests -{ - public class AuthenticationTests - { - public const string TestApiPath = "api/test/openid"; - public const int TestTimeout = TestUtils.TestTimeout; - public AuthenticationTests(ITestOutputHelper helper) - { - Logs.Tester = new XUnitLog(helper) {Name = "Tests"}; - Logs.LogProvider = new XUnitLogProvider(helper); - } - - [Fact(Timeout = TestTimeout)] - [Trait("Integration", "Integration")] - public async Task GetRedirectedToLoginPathOnChallenge() - { - using (var tester = ServerTester.Create()) - { - await tester.StartAsync(); - var client = tester.PayTester.HttpClient; - //Wallets endpoint is protected - var response = await client.GetAsync("wallets"); - var urlPath = response.RequestMessage.RequestUri.ToString() - .Replace(tester.PayTester.ServerUri.ToString(), ""); - //Cookie Challenge redirects you to login page - Assert.StartsWith("Account/Login", urlPath, StringComparison.InvariantCultureIgnoreCase); - - var queryString = response.RequestMessage.RequestUri.ParseQueryString(); - - Assert.NotNull(queryString["ReturnUrl"]); - Assert.Equal("/wallets", queryString["ReturnUrl"]); - } - } - - [Fact(Timeout = TestTimeout)] - [Trait("Integration", "Integration")] - public async Task CanGetOpenIdConfiguration() - { - using (var tester = ServerTester.Create()) - { - await tester.StartAsync(); - using (var response = - await tester.PayTester.HttpClient.GetAsync("/.well-known/openid-configuration")) - { - using (var streamToReadFrom = new StreamReader(await response.Content.ReadAsStreamAsync())) - { - var json = await streamToReadFrom.ReadToEndAsync(); - Assert.NotNull(json); - JObject.Parse(json); // Should do more tests but good enough - } - } - } - } - - [Fact(Timeout = TestTimeout)] - [Trait("Integration", "Integration")] - public async Task CanUseNonInteractiveFlows() - { - using (var tester = ServerTester.Create()) - { - await tester.StartAsync(); - - var user = tester.NewAccount(); - user.GrantAccess(); - await user.MakeAdmin(); - var token = await RegisterPasswordClientAndGetAccessToken(user, null, tester); - await TestApiAgainstAccessToken(token, tester, user); - token = await RegisterPasswordClientAndGetAccessToken(user, "secret", tester); - await TestApiAgainstAccessToken(token, tester, user); - token = await RegisterClientCredentialsFlowAndGetAccessToken(user, "secret", tester); - await TestApiAgainstAccessToken(token, tester, user); - } - } - - [Fact(Timeout = TestTimeout)] - [Trait("Selenium", "Selenium")] - public async Task CanUseImplicitFlow() - { - using (var s = SeleniumTester.Create()) - { - await s.StartAsync(); - var tester = s.Server; - - 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( - new OpenIddictApplicationDescriptor() - { - ClientId = id, - DisplayName = id, - Permissions = {OpenIddictConstants.Permissions.GrantTypes.Implicit}, - RedirectUris = {redirecturi}, - - }); - var implicitAuthorizeUrl = new Uri(tester.PayTester.ServerUri, - $"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(); - var url = s.Driver.Url; - var results = url.Split("#").Last().Split("&") - .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); - 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. - var implicitAuthorizeUrlSilentModel = new Uri($"{implicitAuthorizeUrl.OriginalString}&prompt=none"); - s.Driver.Navigate().GoToUrl(implicitAuthorizeUrlSilentModel); - url = s.Driver.Url; - results = url.Split("#").Last().Split("&").ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); - await TestApiAgainstAccessToken(results["access_token"], tester, user); - - var stores = await TestApiAgainstAccessToken(results["access_token"], - $"{TestApiPath}/me/stores", - tester.PayTester.HttpClient); - Assert.NotEmpty(stores); - - Assert.True(await TestApiAgainstAccessToken(results["access_token"], - $"{TestApiPath}/me/stores/{stores[0].Id}/can-edit", - tester.PayTester.HttpClient)); - - //we dont ask for consent after acquiring it the first time for the same scopes. - LogoutFlow(tester, id, s); - s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl); - s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); - s.Driver.AssertElementNotFound(By.Id("consent-yes")); - - // Let's asks without scopes - LogoutFlow(tester, id, s); - id = Guid.NewGuid().ToString(); - openIdClient = await user.RegisterOpenIdClient( - new OpenIddictApplicationDescriptor() - { - ClientId = id, - DisplayName = id, - Permissions = { OpenIddictConstants.Permissions.GrantTypes.Implicit }, - RedirectUris = { redirecturi }, - }); - implicitAuthorizeUrl = new Uri(tester.PayTester.ServerUri, - $"connect/authorize?response_type=token&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid&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(); - results = s.Driver.Url.Split("#").Last().Split("&") - .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); - - await Assert.ThrowsAnyAsync(async () => - { - await TestApiAgainstAccessToken(results["access_token"], - $"{TestApiPath}/me/stores", - tester.PayTester.HttpClient); - }); - await Assert.ThrowsAnyAsync(async () => - { - await TestApiAgainstAccessToken(results["access_token"], - $"{TestApiPath}/me/stores/{stores[0].Id}/can-edit", - tester.PayTester.HttpClient); - }); - } - } - - void LogoutFlow(ServerTester tester, string clientId, SeleniumTester seleniumTester) - { - var logoutUrl = new Uri(tester.PayTester.ServerUri, - $"connect/logout?response_type=token&client_id={clientId}"); - seleniumTester.Driver.Navigate().GoToUrl(logoutUrl); - seleniumTester.GoToHome(); - Assert.Throws(() => seleniumTester.Driver.FindElement(By.Id("Logout"))); - - } - - [Fact(Timeout = TestTimeout)] - [Trait("Selenium", "Selenium")] - public async Task CanUseCodeFlow() - { - using (var s = SeleniumTester.Create()) - { - await s.StartAsync(); - var tester = s.Server; - - 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 secret = "secret"; - var openIdClient = await user.RegisterOpenIdClient( - new OpenIddictApplicationDescriptor() - { - ClientId = id, - DisplayName = id, - Permissions = - { - OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, - OpenIddictConstants.Permissions.GrantTypes.RefreshToken - }, - 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 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(); - var url = s.Driver.Url; - var results = url.Split("?").Last().Split("&") - .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); - - var httpClient = tester.PayTester.HttpClient; - - var httpRequest = new HttpRequestMessage(HttpMethod.Post, - new Uri(tester.PayTester.ServerUri, "/connect/token")) - { - Content = new FormUrlEncodedContent(new List>() - { - new KeyValuePair("grant_type", - OpenIddictConstants.GrantTypes.AuthorizationCode), - new KeyValuePair("client_id", openIdClient.ClientId), - new KeyValuePair("client_secret", secret), - new KeyValuePair("code", results["code"]), - new KeyValuePair("redirect_uri", redirecturi.AbsoluteUri) - }) - }; - - - var response = await httpClient.SendAsync(httpRequest); - - Assert.True(response.IsSuccessStatusCode); - - string content = await response.Content.ReadAsStringAsync(); - var result = System.Text.Json.JsonSerializer.Deserialize(content); - - await TestApiAgainstAccessToken(result.AccessToken, tester, user); - - var refreshedAccessToken = await RefreshAnAccessToken(result.RefreshToken, httpClient, id, secret); - - await TestApiAgainstAccessToken(refreshedAccessToken, tester, user); - - LogoutFlow(tester, id, s); - s.Driver.Navigate().GoToUrl(authorizeUrl); - s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); - - Assert.Throws(() => 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")); - } - } - - private static async Task RefreshAnAccessToken(string refreshToken, HttpClient client, string clientId, - string clientSecret = null) - { - var httpRequest = new HttpRequestMessage(HttpMethod.Post, - new Uri(client.BaseAddress, "/connect/token")) - { - Content = new FormUrlEncodedContent(new List>() - { - new KeyValuePair("grant_type", - OpenIddictConstants.GrantTypes.RefreshToken), - new KeyValuePair("client_id", clientId), - new KeyValuePair("client_secret", clientSecret), - new KeyValuePair("refresh_token", refreshToken) - }) - }; - - var response = await client.SendAsync(httpRequest); - - Assert.True(response.IsSuccessStatusCode); - - string content = await response.Content.ReadAsStringAsync(); - var result = System.Text.Json.JsonSerializer.Deserialize(content); - Assert.NotEmpty(result.AccessToken); - Assert.Null(result.Error); - return result.AccessToken; - } - - private static async Task RegisterClientCredentialsFlowAndGetAccessToken(TestAccount user, - string secret, - ServerTester tester) - { - var id = Guid.NewGuid().ToString(); - var openIdClient = await user.RegisterOpenIdClient( - new OpenIddictApplicationDescriptor() - { - ClientId = id, - DisplayName = id, - Permissions = {OpenIddictConstants.Permissions.GrantTypes.ClientCredentials} - }, secret); - - - var httpClient = tester.PayTester.HttpClient; - - var httpRequest = new HttpRequestMessage(HttpMethod.Post, - new Uri(tester.PayTester.ServerUri, "/connect/token")) - { - Content = new FormUrlEncodedContent(new List>() - { - new KeyValuePair("grant_type", - OpenIddictConstants.GrantTypes.ClientCredentials), - new KeyValuePair("client_id", openIdClient.ClientId), - new KeyValuePair("client_secret", secret), - new KeyValuePair("scope", "server_management store_management") - }) - }; - - - var response = await httpClient.SendAsync(httpRequest); - - Assert.True(response.IsSuccessStatusCode); - - string content = await response.Content.ReadAsStringAsync(); - var result = System.Text.Json.JsonSerializer.Deserialize(content); - Assert.NotEmpty(result.AccessToken); - Assert.Null(result.Error); - return result.AccessToken; - } - - private static async Task RegisterPasswordClientAndGetAccessToken(TestAccount user, string secret, - ServerTester tester) - { - var id = Guid.NewGuid().ToString(); - var openIdClient = await user.RegisterOpenIdClient( - new OpenIddictApplicationDescriptor() - { - ClientId = id, - DisplayName = id, - Permissions = {OpenIddictConstants.Permissions.GrantTypes.Password} - }, secret); - - - var httpClient = tester.PayTester.HttpClient; - - var httpRequest = new HttpRequestMessage(HttpMethod.Post, - new Uri(tester.PayTester.ServerUri, "/connect/token")) - { - Content = new FormUrlEncodedContent(new List>() - { - new KeyValuePair("grant_type", OpenIddictConstants.GrantTypes.Password), - new KeyValuePair("username", user.RegisterDetails.Email), - new KeyValuePair("password", user.RegisterDetails.Password), - new KeyValuePair("client_id", openIdClient.ClientId), - new KeyValuePair("client_secret", secret), - new KeyValuePair("scope", "server_management store_management") - }) - }; - - - var response = await httpClient.SendAsync(httpRequest); - - Assert.True(response.IsSuccessStatusCode); - - string content = await response.Content.ReadAsStringAsync(); - var result = System.Text.Json.JsonSerializer.Deserialize(content); - Assert.NotEmpty(result.AccessToken); - Assert.Null(result.Error); - return result.AccessToken; - } - - async Task TestApiAgainstAccessToken(string accessToken, ServerTester tester, TestAccount testAccount) - { - var resultUser = - await TestApiAgainstAccessToken(accessToken, $"{TestApiPath}/me/id", - tester.PayTester.HttpClient); - Assert.Equal(testAccount.UserId, resultUser); - - var secondUser = tester.NewAccount(); - secondUser.GrantAccess(); - - var resultStores = - await TestApiAgainstAccessToken(accessToken, $"{TestApiPath}/me/stores", - tester.PayTester.HttpClient); - Assert.Contains(resultStores, - data => data.Id.Equals(testAccount.StoreId, StringComparison.InvariantCultureIgnoreCase)); - Assert.DoesNotContain(resultStores, - data => data.Id.Equals(secondUser.StoreId, StringComparison.InvariantCultureIgnoreCase)); - - Assert.True(await TestApiAgainstAccessToken(accessToken, - $"{TestApiPath}/me/stores/{testAccount.StoreId}/can-edit", - tester.PayTester.HttpClient)); - - Assert.True(await TestApiAgainstAccessToken(accessToken, - $"{TestApiPath}/me/is-admin", - tester.PayTester.HttpClient)); - - await Assert.ThrowsAnyAsync(async () => - { - await TestApiAgainstAccessToken(accessToken, $"{TestApiPath}/me/stores/{secondUser.StoreId}/can-edit", - tester.PayTester.HttpClient); - }); - } - - public async Task TestApiAgainstAccessToken(string accessToken, string url, HttpClient client) - { - var httpRequest = new HttpRequestMessage(HttpMethod.Get, - new Uri(client.BaseAddress, url)); - httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); - var result = await client.SendAsync(httpRequest); - result.EnsureSuccessStatusCode(); - - var rawJson = await result.Content.ReadAsStringAsync(); - if (typeof(T).IsPrimitive || typeof(T) == typeof(string)) - { - return (T)Convert.ChangeType(rawJson, typeof(T)); - } - - return JsonConvert.DeserializeObject(rawJson); - } - } -} diff --git a/BTCPayServer.Tests/BTCPayServerTester.cs b/BTCPayServer.Tests/BTCPayServerTester.cs index 39c51b2f2..c0d6fed70 100644 --- a/BTCPayServer.Tests/BTCPayServerTester.cs +++ b/BTCPayServer.Tests/BTCPayServerTester.cs @@ -32,7 +32,6 @@ using System.Security.Claims; using System.Security.Principal; using System.Text; using System.Threading; -using OpenIddict.Abstractions; using Xunit; using BTCPayServer.Services; using System.Net.Http; @@ -298,7 +297,7 @@ namespace BTCPayServer.Tests if (userId != null) { List claims = new List(); - claims.Add(new Claim(OpenIddictConstants.Claims.Subject, userId)); + claims.Add(new Claim(ClaimTypes.NameIdentifier, userId)); if (isAdmin) claims.Add(new Claim(ClaimTypes.Role, Roles.ServerAdmin)); context.User = new ClaimsPrincipal(new ClaimsIdentity(claims.ToArray(), AuthenticationSchemes.Cookie)); diff --git a/BTCPayServer.Tests/TestAccount.cs b/BTCPayServer.Tests/TestAccount.cs index f73e155cc..d57eb54df 100644 --- a/BTCPayServer.Tests/TestAccount.cs +++ b/BTCPayServer.Tests/TestAccount.cs @@ -18,8 +18,6 @@ using BTCPayServer.Tests.Logging; using BTCPayServer.Lightning; using BTCPayServer.Lightning.CLightning; using BTCPayServer.Data; -using OpenIddict.Abstractions; -using OpenIddict.Core; using Microsoft.AspNetCore.Identity; using NBXplorer.Models; @@ -194,14 +192,5 @@ namespace BTCPayServer.Tests if (storeController.ModelState.ErrorCount != 0) Assert.False(true, storeController.ModelState.FirstOrDefault().Value.Errors[0].ErrorMessage); } - - public async Task RegisterOpenIdClient(OpenIddictApplicationDescriptor descriptor, string secret = null) - { - var openIddictApplicationManager = parent.PayTester.GetService>(); - var client = new BTCPayOpenIdClient { Id = Guid.NewGuid().ToString(), ApplicationUserId = UserId}; - await openIddictApplicationManager.PopulateAsync(client, descriptor); - await openIddictApplicationManager.CreateAsync(client, secret); - return client; - } } } diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj index e2e795e75..749d9db8f 100644 --- a/BTCPayServer/BTCPayServer.csproj +++ b/BTCPayServer/BTCPayServer.csproj @@ -66,9 +66,6 @@ - - - @@ -220,4 +217,8 @@ $(IncludeRazorContentInPack) + + + <_ContentIncludedByDefault Remove="Views\Authorization\Authorize.cshtml" /> + diff --git a/BTCPayServer/Controllers/AuthorizationController.cs b/BTCPayServer/Controllers/AuthorizationController.cs deleted file mode 100644 index 21396e2c8..000000000 --- a/BTCPayServer/Controllers/AuthorizationController.cs +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 BTCPayServer.Security.OpenId; -using BTCPayServer.Data; -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 Microsoft.AspNetCore; -using OpenIddict.Abstractions; -using OpenIddict.Core; -using OpenIddict.Server; -using System.Security.Claims; -using OpenIddict.Server.AspNetCore; - -namespace BTCPayServer.Controllers -{ - public class AuthorizationController : Controller - { - private readonly OpenIddictApplicationManager _applicationManager; - private readonly SignInManager _signInManager; - private readonly OpenIddictAuthorizationManager _authorizationManager; - private readonly UserManager _userManager; - private readonly IOptions _IdentityOptions; - - public AuthorizationController( - OpenIddictApplicationManager applicationManager, - SignInManager signInManager, - OpenIddictAuthorizationManager authorizationManager, - UserManager userManager, - IOptions identityOptions) - { - _applicationManager = applicationManager; - _signInManager = signInManager; - _authorizationManager = authorizationManager; - _userManager = userManager; - _IdentityOptions = identityOptions; - } - - [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)] - [HttpGet("/connect/authorize")] - public async Task Authorize() - { - var request = HttpContext.GetOpenIddictServerRequest(); - // 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("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.GetScopes() - }); - } - - [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)] - [HttpPost("/connect/authorize")] - public async Task Authorize(string consent, bool createAuthorization = true) - { - var request = HttpContext.GetOpenIddictServerRequest(); - 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(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } - - - 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(request.ClientId); - var authorization = await _authorizationManager.CreateAsync(User, user.Id, application.Id, - type, principal.GetScopes()); - principal.SetInternalAuthorizationId(authorization.Id); - } - - // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. - return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } - } -} diff --git a/BTCPayServer/Controllers/RestApi/TestOpenIdController.cs b/BTCPayServer/Controllers/RestApi/TestOpenIdController.cs deleted file mode 100644 index 0e5623d29..000000000 --- a/BTCPayServer/Controllers/RestApi/TestOpenIdController.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Threading.Tasks; -using BTCPayServer.Data; -using BTCPayServer.Models; -using BTCPayServer.Security; -using BTCPayServer.Services.Stores; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using OpenIddict.Validation.AspNetCore; - -namespace BTCPayServer.Controllers.RestApi -{ - /// - /// this controller serves as a testing endpoint for our OpenId unit tests - /// - [Route("api/test/openid")] - [ApiController] - [Authorize(AuthenticationSchemes = AuthenticationSchemes.OpenId)] - public class TestOpenIdController : ControllerBase - { - private readonly UserManager _userManager; - private readonly StoreRepository _storeRepository; - - public TestOpenIdController(UserManager userManager, StoreRepository storeRepository) - { - _userManager = userManager; - _storeRepository = storeRepository; - } - - [HttpGet("me/id")] - public string GetCurrentUserId() - { - return _userManager.GetUserId(User); - } - - [HttpGet("me")] - public async Task GetCurrentUser() - { - return await _userManager.GetUserAsync(User); - } - - [HttpGet("me/is-admin")] - [Authorize(Policy = Policies.CanModifyServerSettings.Key, AuthenticationSchemes = AuthenticationSchemes.OpenId)] - public bool AmIAnAdmin() - { - return true; - } - - [HttpGet("me/stores")] - [Authorize(Policy = Policies.CanModifyStoreSettings.Key, - AuthenticationSchemes = AuthenticationSchemes.OpenId)] - public async Task GetCurrentUserStores() - { - return await _storeRepository.GetStoresByUserId(_userManager.GetUserId(User)); - } - - [HttpGet("me/stores/{storeId}/can-edit")] - [Authorize(Policy = Policies.CanModifyStoreSettings.Key, - AuthenticationSchemes = AuthenticationSchemes.OpenId)] - public bool CanEdit(string storeId) - { - return true; - } - } -} diff --git a/BTCPayServer/Extensions/OpenIddictExtensions.cs b/BTCPayServer/Extensions/OpenIddictExtensions.cs deleted file mode 100644 index 93814bb34..000000000 --- a/BTCPayServer/Extensions/OpenIddictExtensions.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.IO; -using System.Security.Cryptography; -using BTCPayServer.Configuration; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.IdentityModel.Tokens; -using NETCore.Encrypt.Extensions.Internal; - -namespace BTCPayServer -{ - public static class OpenIddictExtensions - { - public static SecurityKey GetSigningKey(IConfiguration configuration, string fileName) - { - - var file = Path.Combine(configuration.GetDataDir(), fileName); - using var rsa = new RSACryptoServiceProvider(2048); - if (File.Exists(file)) - { - rsa.FromXmlString2(File.ReadAllText(file)); - } - else - { - var contents = rsa.ToXmlString2(true); - File.WriteAllText(file, contents); - } - return new RsaSecurityKey(rsa.ExportParameters(true));; - } - public static OpenIddictServerBuilder ConfigureSigningKey(this OpenIddictServerBuilder builder, - IConfiguration configuration) - { - return builder - .AddSigningKey(GetSigningKey(configuration, "signing.rsaparams")) - .AddEncryptionKey(GetSigningKey(configuration, "encrypting.rsaparams")); - } - } -} diff --git a/BTCPayServer/Extensions/RsaKeyExtensions.cs b/BTCPayServer/Extensions/RsaKeyExtensions.cs deleted file mode 100644 index c201917bb..000000000 --- a/BTCPayServer/Extensions/RsaKeyExtensions.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Globalization; -using System.Security.Cryptography; -using System.Xml; - -namespace NETCore.Encrypt.Extensions.Internal -{ - /// - /// .net core's implementatiosn are still marked as unsupported because of stupid decisions( https://github.com/dotnet/corefx/issues/23686) - /// - internal static class RsaKeyExtensions - { - #region XML - - public static void FromXmlString2(this RSA rsa, string xmlString) - { - RSAParameters parameters = new RSAParameters(); - - XmlDocument xmlDoc = new XmlDocument(); - xmlDoc.LoadXml(xmlString); - - if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue", StringComparison.InvariantCulture)) - { - foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes) - { - switch (node.Name) - { - case "Modulus": - parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) - ? null - : Convert.FromBase64String(node.InnerText)); - break; - case "Exponent": - parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) - ? null - : Convert.FromBase64String(node.InnerText)); - break; - case "P": - parameters.P = (string.IsNullOrEmpty(node.InnerText) - ? null - : Convert.FromBase64String(node.InnerText)); - break; - case "Q": - parameters.Q = (string.IsNullOrEmpty(node.InnerText) - ? null - : Convert.FromBase64String(node.InnerText)); - break; - case "DP": - parameters.DP = (string.IsNullOrEmpty(node.InnerText) - ? null - : Convert.FromBase64String(node.InnerText)); - break; - case "DQ": - parameters.DQ = (string.IsNullOrEmpty(node.InnerText) - ? null - : Convert.FromBase64String(node.InnerText)); - break; - case "InverseQ": - parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) - ? null - : Convert.FromBase64String(node.InnerText)); - break; - case "D": - parameters.D = (string.IsNullOrEmpty(node.InnerText) - ? null - : Convert.FromBase64String(node.InnerText)); - break; - } - } - } - else - { - throw new Exception("Invalid XML RSA key."); - } - - rsa.ImportParameters(parameters); - } - - public static string ToXmlString2(this RSA rsa, bool includePrivateParameters) - { - RSAParameters parameters = rsa.ExportParameters(includePrivateParameters); - - return string.Format(CultureInfo.InvariantCulture, - "{0}{1}

{2}

{3}{4}{5}{6}{7}
", - parameters.Modulus != null ? Convert.ToBase64String(parameters.Modulus) : null, - parameters.Exponent != null ? Convert.ToBase64String(parameters.Exponent) : null, - parameters.P != null ? Convert.ToBase64String(parameters.P) : null, - parameters.Q != null ? Convert.ToBase64String(parameters.Q) : null, - parameters.DP != null ? Convert.ToBase64String(parameters.DP) : null, - parameters.DQ != null ? Convert.ToBase64String(parameters.DQ) : null, - parameters.InverseQ != null ? Convert.ToBase64String(parameters.InverseQ) : null, - parameters.D != null ? Convert.ToBase64String(parameters.D) : null); - } - - #endregion - } -} diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index a9064f044..7072b63e9 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -41,17 +41,6 @@ using Npgsql; using BTCPayServer.Services.Apps; using BTCPayServer.U2F; using BundlerMinifier.TagHelpers; -using OpenIddict.EntityFrameworkCore.Models; - -using System.Collections.Generic; -using System.Diagnostics; -using System.Security.Claims; -using System.Threading.Tasks; -using BTCPayServer.Models; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using BTCPayServer.Security.Bitpay; using Serilog; @@ -67,7 +56,6 @@ namespace BTCPayServer.Hosting { var factory = provider.GetRequiredService(); factory.ConfigureBuilder(o); - o.UseOpenIddict, BTCPayOpenIdToken, string>(); }); services.AddHttpClient(); services.AddHttpClient(nameof(ExplorerClientProvider), httpClient => @@ -221,7 +209,6 @@ namespace BTCPayServer.Hosting services.AddSingleton(); services.AddSingleton(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.TryAddSingleton(); diff --git a/BTCPayServer/Hosting/Startup.cs b/BTCPayServer/Hosting/Startup.cs index a88e7f037..86eadaa71 100644 --- a/BTCPayServer/Hosting/Startup.cs +++ b/BTCPayServer/Hosting/Startup.cs @@ -1,8 +1,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.Hosting; -using OpenIddict.Validation.AspNetCore; -using OpenIddict.Abstractions; + using Microsoft.AspNetCore.Builder; using System; using Microsoft.Extensions.DependencyInjection; @@ -19,14 +18,10 @@ using System.IO; using Microsoft.Extensions.DependencyInjection.Extensions; using BTCPayServer.Security; using Microsoft.AspNetCore.Server.Kestrel.Core; -using OpenIddict.EntityFrameworkCore.Models; using System.Net; -using BTCPayServer.Security.OpenId; using BTCPayServer.PaymentRequest; using BTCPayServer.Services.Apps; using BTCPayServer.Storage; -using Microsoft.Extensions.Options; -using OpenIddict.Core; namespace BTCPayServer.Hosting { @@ -57,8 +52,6 @@ namespace BTCPayServer.Hosting .AddEntityFrameworkStores() .AddDefaultTokenProviders(); - ConfigureOpenIddict(services); - services.AddBTCPayServer(Configuration); services.AddProviderStorage(); services.AddSession(); @@ -95,12 +88,6 @@ namespace BTCPayServer.Hosting options.Lockout.MaxFailedAccessAttempts = 5; options.Lockout.AllowedForNewUsers = true; 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 = 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("HttpsCertificateFilePath", null); @@ -143,67 +130,6 @@ namespace BTCPayServer.Hosting }); } } - - private DirectoryInfo GetDataDir() - { - return new DirectoryInfo(Configuration.GetDataDir(DefaultConfiguration.GetNetworkType(Configuration))); - } - - private void ConfigureOpenIddict(IServiceCollection services) - { - // Register the OpenIddict services. - services.AddOpenIddict() - .AddCore(options => - { - // Configure OpenIddict to use the Entity Framework Core stores and entities. - options.UseEntityFrameworkCore() - .UseDbContext() - .ReplaceDefaultEntities, - BTCPayOpenIdToken, string>(); - }) - .AddServer(options => - { - options.UseAspNetCore() - .EnableStatusCodePagesIntegration() - .EnableAuthorizationEndpointPassthrough() - .EnableLogoutEndpointPassthrough() - .EnableAuthorizationEndpointCaching() - .DisableTransportSecurityRequirement(); - - // Enable the token endpoint (required to use the password flow). - options.SetTokenEndpointUris("/connect/token"); - options.SetAuthorizationEndpointUris("/connect/authorize"); - options.SetLogoutEndpointUris("/connect/logout"); - - //we do not care about these granular controls for now - options.IgnoreScopePermissions(); - options.IgnoreEndpointPermissions(); - // Allow client applications various flows - options.AllowImplicitFlow(); - options.AllowClientCredentialsFlow(); - options.AllowRefreshTokenFlow(); - options.AllowPasswordFlow(); - options.AllowAuthorizationCodeFlow(); - options.UseRollingTokens(); - - options.RegisterScopes( - OpenIddictConstants.Scopes.OpenId, - BTCPayScopes.StoreManagement, - BTCPayScopes.ServerManagement - ); - options.AddEventHandler(PasswordGrantTypeEventHandler.Descriptor); - options.AddEventHandler(OpenIdGrantHandlerCheckCanSignIn.Descriptor); - options.AddEventHandler(ClientCredentialsGrantTypeEventHandler.Descriptor); - options.AddEventHandler(LogoutEventHandler.Descriptor); - options.ConfigureSigningKey(Configuration); - }) - .AddValidation(options => - { - options.UseLocalServer(); - options.UseAspNetCore(); - }); - } - public void Configure( IApplicationBuilder app, IWebHostEnvironment env, @@ -224,6 +150,10 @@ namespace BTCPayServer.Hosting }); } } + private DirectoryInfo GetDataDir() + { + return new DirectoryInfo(Configuration.GetDataDir(DefaultConfiguration.GetNetworkType(Configuration))); + } private static void ConfigureCore(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider prov, ILoggerFactory loggerFactory, BTCPayServerOptions options) { diff --git a/BTCPayServer/Program.cs b/BTCPayServer/Program.cs index 69bb07d2e..0f3d9cb4d 100644 --- a/BTCPayServer/Program.cs +++ b/BTCPayServer/Program.cs @@ -53,7 +53,6 @@ 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("OpenIddict.Server.OpenIddictServerProvider", LogLevel.Error); l.AddProvider(new CustomConsoleLogProvider(processor)); }) .UseStartup() diff --git a/BTCPayServer/Security/AuthenticationSchemes.cs b/BTCPayServer/Security/AuthenticationSchemes.cs index a3117ca3a..93bcfedc5 100644 --- a/BTCPayServer/Security/AuthenticationSchemes.cs +++ b/BTCPayServer/Security/AuthenticationSchemes.cs @@ -1,17 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using OpenIddict.Validation; -using OpenIddict.Validation.AspNetCore; - -namespace BTCPayServer.Security +namespace BTCPayServer.Security { public class AuthenticationSchemes { public const string Cookie = "Identity.Application"; public const string Bitpay = "Bitpay"; - public const string OpenId = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme; public const string ApiKey = "GreenfieldApiKey"; } } diff --git a/BTCPayServer/Security/OpenId/BTCPayScopes.cs b/BTCPayServer/Security/OpenId/BTCPayScopes.cs deleted file mode 100644 index 71d92e24e..000000000 --- a/BTCPayServer/Security/OpenId/BTCPayScopes.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using OpenIddict.Abstractions; - -namespace BTCPayServer.Security.OpenId -{ - public static class BTCPayScopes - { - public const string StoreManagement = "store_management"; - public const string ServerManagement = "server_management"; - } - public static class RestAPIPolicies - { - - } -} diff --git a/BTCPayServer/Security/OpenId/BaseOpenIdGrantHandler.cs b/BTCPayServer/Security/OpenId/BaseOpenIdGrantHandler.cs deleted file mode 100644 index 1dfc4a071..000000000 --- a/BTCPayServer/Security/OpenId/BaseOpenIdGrantHandler.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Threading.Tasks; -using OpenIdConnectRequest = OpenIddict.Abstractions.OpenIddictRequest; -using BTCPayServer.Data; -using BTCPayServer.Models; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Options; -using OpenIddict.Core; -using OpenIddict.Server; -using System.Security.Claims; - -namespace BTCPayServer.Security.OpenId -{ - public abstract class BaseOpenIdGrantHandler : - IOpenIddictServerHandler - where T : OpenIddictServerEvents.BaseContext - { - private readonly OpenIddictApplicationManager _applicationManager; - private readonly OpenIddictAuthorizationManager _authorizationManager; - protected readonly SignInManager _signInManager; - protected readonly IOptions _identityOptions; - - protected BaseOpenIdGrantHandler( - OpenIddictApplicationManager applicationManager, - OpenIddictAuthorizationManager authorizationManager, - SignInManager signInManager, - IOptions identityOptions) - { - _applicationManager = applicationManager; - _authorizationManager = authorizationManager; - _signInManager = signInManager; - _identityOptions = identityOptions; - } - - - protected Task CreateClaimsPrincipalAsync(OpenIdConnectRequest request, ApplicationUser user) - { - return OpenIdExtensions.CreateClaimsPrincipalAsync(_applicationManager, _authorizationManager, - _identityOptions.Value, _signInManager, request, user); - } - public abstract ValueTask HandleAsync(T notification); - } -} diff --git a/BTCPayServer/Security/OpenId/ClientCredentialsGrantTypeEventHandler.cs b/BTCPayServer/Security/OpenId/ClientCredentialsGrantTypeEventHandler.cs deleted file mode 100644 index af598a44a..000000000 --- a/BTCPayServer/Security/OpenId/ClientCredentialsGrantTypeEventHandler.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Security.Claims; -using System.Threading.Tasks; -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.EntityFrameworkCore.Models; -using OpenIddict.Server; - -namespace BTCPayServer.Security.OpenId -{ - public class ClientCredentialsGrantTypeEventHandler : - BaseOpenIdGrantHandler - { - public static OpenIddictServerHandlerDescriptor Descriptor { get; } = - OpenIddictServerHandlerDescriptor.CreateBuilder() - .UseScopedHandler() - .Build(); - private readonly OpenIddictApplicationManager _applicationManager; - - private readonly UserManager _userManager; - - public ClientCredentialsGrantTypeEventHandler( - OpenIddictApplicationManager applicationManager, - OpenIddictAuthorizationManager authorizationManager, - SignInManager signInManager, - IOptions identityOptions, - UserManager userManager) : base(applicationManager, authorizationManager, signInManager, - identityOptions) - { - _applicationManager = applicationManager; - _userManager = userManager; - } - - public override async ValueTask HandleAsync( - OpenIddictServerEvents.HandleTokenRequestContext notification) - { - var request = notification.Request; - var context = notification; - if (!request.IsClientCredentialsGrantType()) - { - return; - } - - var application = await _applicationManager.FindByClientIdAsync(request.ClientId); - if (application == null) - { - context.Reject( - error: OpenIddictConstants.Errors.InvalidClient, - description: "The client application was not found in the database."); - return; - } - - var user = await _userManager.FindByIdAsync(application.ApplicationUserId); - context.Principal = await CreateClaimsPrincipalAsync(request, user); - } - } -} diff --git a/BTCPayServer/Security/OpenId/LogoutEventHandler.cs b/BTCPayServer/Security/OpenId/LogoutEventHandler.cs deleted file mode 100644 index fc42d0e6c..000000000 --- a/BTCPayServer/Security/OpenId/LogoutEventHandler.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Threading.Tasks; -using BTCPayServer.Data; -using BTCPayServer.Models; -using Microsoft.AspNetCore.Authentication; -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.Security.OpenId -{ - public class LogoutEventHandler : IOpenIddictServerHandler - { - protected readonly SignInManager _signInManager; - public static OpenIddictServerHandlerDescriptor Descriptor { get; } = - OpenIddictServerHandlerDescriptor.CreateBuilder() - .UseScopedHandler() - .Build(); - public LogoutEventHandler(SignInManager signInManager) - { - _signInManager = signInManager; - } - - public async ValueTask HandleAsync( - OpenIddictServerEvents.HandleLogoutRequestContext notification) - { - await _signInManager.SignOutAsync(); - } - } -} diff --git a/BTCPayServer/Security/OpenId/OpenIdExtensions.cs b/BTCPayServer/Security/OpenId/OpenIdExtensions.cs deleted file mode 100644 index 12ef9e83b..000000000 --- a/BTCPayServer/Security/OpenId/OpenIdExtensions.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using BTCPayServer.Data; -using BTCPayServer.Models; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Identity; -using OpenIddict.Abstractions; -using OpenIddict.Core; -using OpenIddict.Server; - -namespace BTCPayServer.Security.OpenId -{ - public static class OpenIdExtensions - { - public static ImmutableHashSet Restrict(this ImmutableArray scopes, ClaimsPrincipal claimsPrincipal) - { - HashSet restricted = new HashSet(); - foreach (var scope in scopes) - { - if (scope == BTCPayScopes.ServerManagement && !claimsPrincipal.IsInRole(Roles.ServerAdmin)) - continue; - restricted.Add(scope); - } - return restricted.ToImmutableHashSet(); - } - public static async Task CreateClaimsPrincipalAsync(OpenIddictApplicationManager applicationManager, - OpenIddictAuthorizationManager authorizationManager, - IdentityOptions identityOptions, - SignInManager signInManager, - OpenIddictRequest request, - ApplicationUser user) - { - var principal = await signInManager.CreateUserPrincipalAsync(user); - if (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType()) - { - principal.SetScopes(request.GetScopes().Restrict(principal)); - } - else if (request.IsAuthorizationCodeGrantType() && - 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)) - { - principal.SetInternalAuthorizationId(authorizationId); - } - } - - principal.SetDestinations(identityOptions); - return principal; - } - - public static void SetDestinations(this ClaimsPrincipal principal, IdentityOptions identityOptions) - { - foreach (var claim in principal.Claims) - { - claim.SetDestinations(GetDestinations(identityOptions, claim, principal)); - } - } - - private static IEnumerable GetDestinations(IdentityOptions identityOptions, Claim claim, - ClaimsPrincipal principal) - { - switch (claim.Type) - { - case OpenIddictConstants.Claims.Name: - case OpenIddictConstants.Claims.Email: - yield return OpenIddictConstants.Destinations.AccessToken; - yield break; - } - } - - public static async Task IsUserAuthorized( - OpenIddictAuthorizationManager authorizationManager, - OpenIddictRequest request, string userId, string applicationId) - { - var authorizations = await authorizationManager.ListAsync(queryable => - queryable.Where(authorization => - authorization.Subject == userId && - authorization.Application.Id == applicationId && - authorization.Status == OpenIddictConstants.Statuses.Valid)).ToArrayAsync(); - - 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) => (Id: tuple.Id, Scopes: tuple.Item1.Result)) - .Where((tuple) => !request.GetScopes().Except(tuple.Scopes).Any()); - - if (authorizationsWithSufficientScopes.Any()) - { - return authorizationsWithSufficientScopes.First().Id; - } - } - - return null; - } - } -} diff --git a/BTCPayServer/Security/OpenId/OpenIdGrantHandlerCheckCanSignIn.cs b/BTCPayServer/Security/OpenId/OpenIdGrantHandlerCheckCanSignIn.cs deleted file mode 100644 index cd353d65d..000000000 --- a/BTCPayServer/Security/OpenId/OpenIdGrantHandlerCheckCanSignIn.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Threading.Tasks; -using OpenIddict.Abstractions; -using BTCPayServer.Data; -using BTCPayServer.Models; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Options; -using OpenIddict.Core; -using OpenIddict.Server; -using Microsoft.AspNetCore; -using OpenIddict.Server.AspNetCore; - -namespace BTCPayServer.Security.OpenId -{ - public class OpenIdGrantHandlerCheckCanSignIn : - BaseOpenIdGrantHandler - { - public static OpenIddictServerHandlerDescriptor Descriptor { get; } = - OpenIddictServerHandlerDescriptor.CreateBuilder() - .UseScopedHandler() - .Build(); - - private readonly UserManager _userManager; - - public OpenIdGrantHandlerCheckCanSignIn( - OpenIddictApplicationManager applicationManager, - OpenIddictAuthorizationManager authorizationManager, - SignInManager signInManager, - IOptions identityOptions, UserManager userManager) : base( - applicationManager, authorizationManager, signInManager, - identityOptions) - { - _userManager = userManager; - } - - public override async ValueTask HandleAsync( - OpenIddictServerEvents.HandleTokenRequestContext notification) - { - var request = notification.Request; - if (!request.IsRefreshTokenGrantType() && !request.IsAuthorizationCodeGrantType()) - { - // Allow other handlers to process the event. - return; - } - - var httpContext = notification.Transaction.GetHttpRequest().HttpContext; - var authenticateResult = (await httpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)); - - var user = await _userManager.GetUserAsync(authenticateResult.Principal); - if (user == null) - { - notification.Reject( - error: OpenIddictConstants.Errors.InvalidGrant, - description: "The token is no longer valid."); - return; - } - - // Ensure the user is still allowed to sign in. - if (!await _signInManager.CanSignInAsync(user)) - { - notification.Reject( - error: OpenIddictConstants.Errors.InvalidGrant, - description: "The user is no longer allowed to sign in."); - return; - } - - notification.Principal = await this.CreateClaimsPrincipalAsync(request, user); - } - } -} diff --git a/BTCPayServer/Security/OpenId/PasswordGrantTypeEventHandler.cs b/BTCPayServer/Security/OpenId/PasswordGrantTypeEventHandler.cs deleted file mode 100644 index b28018b4e..000000000 --- a/BTCPayServer/Security/OpenId/PasswordGrantTypeEventHandler.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Threading.Tasks; -using BTCPayServer.Data; -using BTCPayServer.Models; -using BTCPayServer.U2F; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Options; -using OpenIddict.Abstractions; -using OpenIddict.Core; -using OpenIddict.Server; -using Microsoft.AspNetCore; - -namespace BTCPayServer.Security.OpenId -{ - public class PasswordGrantTypeEventHandler : BaseOpenIdGrantHandler - { - private readonly UserManager _userManager; - private readonly NicolasDorier.RateLimits.RateLimitService _rateLimitService; - private readonly U2FService _u2FService; - - public PasswordGrantTypeEventHandler( - OpenIddictApplicationManager applicationManager, - OpenIddictAuthorizationManager authorizationManager, - SignInManager signInManager, - UserManager userManager, - NicolasDorier.RateLimits.RateLimitService rateLimitService, - IOptions identityOptions, U2FService u2FService) : base(applicationManager, - authorizationManager, signInManager, identityOptions) - { - _userManager = userManager; - _rateLimitService = rateLimitService; - _u2FService = u2FService; - } - - public static OpenIddictServerHandlerDescriptor Descriptor { get; } = - OpenIddictServerHandlerDescriptor.CreateBuilder() - .UseScopedHandler() - .Build(); - - public override async ValueTask HandleAsync( - OpenIddictServerEvents.HandleTokenRequestContext notification) - { - var request = notification.Request; - if (!request.IsPasswordGrantType()) - { - return; - } - - 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.Reject( - error: OpenIddictConstants.Errors.InvalidGrant, - description: "The specified credentials are invalid."); - return; - } - - notification.Principal = await CreateClaimsPrincipalAsync(request, user); - } - } -} diff --git a/BTCPayServer/Security/OpenIdAuthorizationHandler.cs b/BTCPayServer/Security/OpenIdAuthorizationHandler.cs deleted file mode 100644 index a7413e41f..000000000 --- a/BTCPayServer/Security/OpenIdAuthorizationHandler.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using BTCPayServer.Data; -using BTCPayServer.Services.Stores; -using System.Security.Claims; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Authentication; -using Microsoft.Extensions.Primitives; -using static BTCPayServer.Security.OpenId.RestAPIPolicies; -using OpenIddict.Abstractions; -using BTCPayServer.Security.OpenId; - -namespace BTCPayServer.Security -{ - public class OpenIdAuthorizationHandler : AuthorizationHandler - { - private readonly HttpContext _HttpContext; - private readonly UserManager _userManager; - private readonly StoreRepository _storeRepository; - - public OpenIdAuthorizationHandler(IHttpContextAccessor httpContextAccessor, - UserManager userManager, - StoreRepository storeRepository) - { - _HttpContext = httpContextAccessor.HttpContext; - _userManager = userManager; - _storeRepository = storeRepository; - } - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement) - { - if (context.User.Identity.AuthenticationType != "AuthenticationTypes.Federation") - return; - - bool success = false; - switch (requirement.Policy) - { - 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) - { - success = true; - } - else - { - var userid = _userManager.GetUserId(context.User); - if (string.IsNullOrEmpty(userid)) - break; - var store = await _storeRepository.FindStore((string)storeId, userid); - if (store == null) - break; - success = true; - _HttpContext.SetStoreData(store); - } - break; - case Policies.CanModifyServerSettings.Key: - 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; - } - - if (success) - { - context.Succeed(requirement); - } - } - } -} diff --git a/BTCPayServer/Security/SecurityExtensions.cs b/BTCPayServer/Security/SecurityExtensions.cs index 192aa3351..a90e39704 100644 --- a/BTCPayServer/Security/SecurityExtensions.cs +++ b/BTCPayServer/Security/SecurityExtensions.cs @@ -1,14 +1,8 @@ using System; -using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Routing; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; -using OpenIddict.Abstractions; -using BTCPayServer.Data; -using BTCPayServer.Services.Stores; -using Microsoft.AspNetCore.Identity; namespace BTCPayServer.Security { @@ -16,7 +10,7 @@ namespace BTCPayServer.Security { public static bool HasScopes(this AuthorizationHandlerContext context, params string[] scopes) { - return scopes.All(s => context.User.HasClaim(c => c.Type == OpenIddictConstants.Claims.Scope && c.Value.Split(' ').Contains(s))); + return scopes.All(s => context.User.HasClaim(c => c.Type.Equals("scope", StringComparison.InvariantCultureIgnoreCase) && c.Value.Split(' ').Contains(s))); } public static string GetImplicitStoreId(this HttpContext httpContext) { diff --git a/BTCPayServer/Views/Authorization/Authorize.cshtml b/BTCPayServer/Views/Authorization/Authorize.cshtml deleted file mode 100644 index 57085b8d9..000000000 --- a/BTCPayServer/Views/Authorization/Authorize.cshtml +++ /dev/null @@ -1,57 +0,0 @@ -@using BTCPayServer.Security.OpenId -@using OpenIddict.Abstractions -@model BTCPayServer.Models.Authorization.AuthorizeViewModel -@{ - - var scopeMappings = new Dictionary() - { - {BTCPayScopes.StoreManagement, ("Manage your stores", "The app will be able to create, modify and delete all your stores.")}, - {BTCPayScopes.ServerManagement, ("Manage your server", "The app will have total control on your server")}, - }; -} -
- -
-
-
-
-

Authorization Request

-
-

@Model.ApplicationName is requesting access to your account.

-
-
-
-
-
- @foreach (var scope in Model.Scope) - { - @if (scopeMappings.TryGetValue(scope, out var text)) - { -
  • -
    @text.Title
    -

    @text.Description.

    -
  • - } - } -
    - -
    -
    -
    -
    -
    - - - -
    - - -
    -
    -
    -
    -