diff --git a/backend/src/Squidex.Infrastructure/Json/ClaimsPrinicpalSurrogate.cs b/backend/src/Squidex.Infrastructure/Json/ClaimsPrincipalSurrogate.cs similarity index 96% rename from backend/src/Squidex.Infrastructure/Json/ClaimsPrinicpalSurrogate.cs rename to backend/src/Squidex.Infrastructure/Json/ClaimsPrincipalSurrogate.cs index c76105c46..b293a4314 100644 --- a/backend/src/Squidex.Infrastructure/Json/ClaimsPrinicpalSurrogate.cs +++ b/backend/src/Squidex.Infrastructure/Json/ClaimsPrincipalSurrogate.cs @@ -9,7 +9,7 @@ using System.Security.Claims; namespace Squidex.Infrastructure.Json { - public sealed class ClaimsPrinicpalSurrogate : List, ISurrogate + public sealed class ClaimsPrincipalSurrogate : List, ISurrogate { public void FromSource(ClaimsPrincipal source) { diff --git a/backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerServices.cs b/backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerServices.cs index b391d3a25..8dbed252c 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerServices.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerServices.cs @@ -121,8 +121,6 @@ namespace Squidex.Areas.IdentityServer.Config var issuerUrl = Constants.PrefixIdentityServer; - options.Issuer = new Uri(urlGenerator.BuildUrl(issuerUrl, false)); - options.AuthorizationEndpointUris.Add( new Uri(urlGenerator.BuildUrl($"{issuerUrl}/connect/authorize", false))); @@ -137,6 +135,8 @@ namespace Squidex.Areas.IdentityServer.Config options.UserinfoEndpointUris.Add( new Uri(urlGenerator.BuildUrl($"{issuerUrl}/connect/userinfo", false))); + + options.Issuer = new Uri(urlGenerator.BuildUrl(issuerUrl, false)); }); } } diff --git a/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs b/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs index cdb1ff43c..7f84179b7 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs @@ -251,8 +251,7 @@ namespace Squidex.Areas.IdentityServer.Controllers.Account user = await userService.CreateAsync(email, values, identityOptions.LockAutomatically, HttpContext.RequestAborted); - await userService.AddLoginAsync(user.Id, login, - HttpContext.RequestAborted); + await userService.AddLoginAsync(user.Id, login, HttpContext.RequestAborted); (isLoggedIn, var locked) = await LoginAsync(login); diff --git a/backend/src/Squidex/Areas/IdentityServer/Controllers/Connect/ConnectController.cs b/backend/src/Squidex/Areas/IdentityServer/Controllers/Connect/ConnectController.cs index 97c81a1d4..d26595371 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Controllers/Connect/ConnectController.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Controllers/Connect/ConnectController.cs @@ -43,7 +43,6 @@ namespace Notifo.Areas.Account.Controllers public async Task Exchange() { var request = HttpContext.GetOpenIddictServerRequest(); - if (request == null) { throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); @@ -52,14 +51,12 @@ namespace Notifo.Areas.Account.Controllers if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType() || request.IsImplicitFlow()) { var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal; - if (principal == null) { throw new InvalidOperationException("The user details cannot be retrieved."); } var user = await userService.GetAsync(principal, HttpContext.RequestAborted); - if (user == null) { return Forbid( @@ -104,7 +101,7 @@ namespace Notifo.Areas.Account.Controllers throw new InvalidOperationException("The application details cannot be found in the database."); } - var principal = await CreateApplicationPrinicpalAsync(request, application); + var principal = await CreateApplicationPrincipalAsync(request, application); return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } @@ -155,9 +152,7 @@ namespace Notifo.Areas.Account.Controllers throw new InvalidOperationException("The user details cannot be retrieved."); } - var principal = await SignInManager.CreateUserPrincipalAsync((IdentityUser)user.Identity); - - await EnrichPrincipalAsync(request, principal, false); + var principal = await CreatePrincipalAsync(request, user); return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } @@ -170,7 +165,14 @@ namespace Notifo.Areas.Account.Controllers return SignOut(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } - private async Task CreateApplicationPrinicpalAsync(OpenIddictRequest request, object application) + private async Task CreatePrincipalAsync(OpenIddictRequest request, Squidex.Shared.Users.IUser? user) + { + var principal = await SignInManager.CreateUserPrincipalAsync((IdentityUser)user.Identity); + + return await EnrichPrincipalAsync(principal, request, false); + } + + private async Task CreateApplicationPrincipalAsync(OpenIddictRequest request, object application) { var identity = new ClaimsIdentity( TokenValidationParameters.DefaultAuthenticationType, @@ -193,22 +195,24 @@ namespace Notifo.Areas.Account.Controllers identity.AddClaim(claim); } - await EnrichPrincipalAsync(request, principal, true); - - return principal; + return await EnrichPrincipalAsync(principal, request, true); } - private async Task EnrichPrincipalAsync(OpenIddictRequest request, ClaimsPrincipal principal, bool alwaysDeliverPermissions) + private async Task EnrichPrincipalAsync(ClaimsPrincipal principal, OpenIddictRequest request, bool alwaysDeliverPermissions) { var scopes = request.GetScopes(); + var resources = await scopeManager.ListResourcesAsync(scopes, HttpContext.RequestAborted).ToListAsync(HttpContext.RequestAborted); + principal.SetScopes(scopes); - principal.SetResources(await scopeManager.ListResourcesAsync(scopes, HttpContext.RequestAborted).ToListAsync(HttpContext.RequestAborted)); + principal.SetResources(resources); foreach (var claim in principal.Claims) { claim.SetDestinations(GetDestinations(claim, principal, alwaysDeliverPermissions)); } + + return principal; } private static IEnumerable GetDestinations(Claim claim, ClaimsPrincipal principal, bool alwaysDeliverPermissions) diff --git a/backend/src/Squidex/Config/Authentication/AuthenticationServices.cs b/backend/src/Squidex/Config/Authentication/AuthenticationServices.cs index ebeee7fb2..8ae5b9b08 100644 --- a/backend/src/Squidex/Config/Authentication/AuthenticationServices.cs +++ b/backend/src/Squidex/Config/Authentication/AuthenticationServices.cs @@ -31,6 +31,10 @@ namespace Squidex.Config.Authentication builder.Services.ConfigureApplicationCookie(options => { + options.AccessDeniedPath = "/identity-server/account/access-denied"; + options.LoginPath = "/identity-server/account/login"; + options.LogoutPath = "/identity-server/account/login"; + options.Cookie.Name = ".sq.auth"; if (urlsOptions.BaseUrl?.StartsWith("https://", StringComparison.OrdinalIgnoreCase) == true) diff --git a/backend/src/Squidex/Config/Domain/SerializationServices.cs b/backend/src/Squidex/Config/Domain/SerializationServices.cs index f88e3e59f..3db89eeca 100644 --- a/backend/src/Squidex/Config/Domain/SerializationServices.cs +++ b/backend/src/Squidex/Config/Domain/SerializationServices.cs @@ -46,7 +46,7 @@ namespace Squidex.Config.Domain new ExecutionResultJsonConverter(new ErrorInfoProvider()), new JsonValueConverter(), new StringEnumConverter(), - new SurrogateConverter(), + new SurrogateConverter(), new SurrogateConverter, JsonFilterSurrogate>(), new SurrogateConverter(), new SurrogateConverter(), diff --git a/backend/src/Squidex/Startup.cs b/backend/src/Squidex/Startup.cs index cad2ed919..b306df956 100644 --- a/backend/src/Squidex/Startup.cs +++ b/backend/src/Squidex/Startup.cs @@ -37,13 +37,17 @@ namespace Squidex services.AddDefaultWebServices(config); services.AddDefaultForwardRules(); + // They must be called in this order. + services.AddSquidexMvcWithPlugins(config); + services.AddSquidexIdentity(config); + services.AddSquidexIdentityServer(); + services.AddSquidexAuthentication(config); + services.AddSquidexImageResizing(config); services.AddSquidexAssetInfrastructure(config); services.AddSquidexSerializers(); - services.AddSquidexMvcWithPlugins(config); services.AddSquidexApps(config); services.AddSquidexAssets(config); - services.AddSquidexAuthentication(config); services.AddSquidexBackups(); services.AddSquidexCommands(config); services.AddSquidexComments(); @@ -54,8 +58,6 @@ namespace Squidex services.AddSquidexGraphQL(); services.AddSquidexHealthChecks(config); services.AddSquidexHistory(config); - services.AddSquidexIdentity(config); - services.AddSquidexIdentityServer(); services.AddSquidexInfrastructure(config); services.AddSquidexLocalization(); services.AddSquidexMigration(config); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs index 2ac83b295..8392c58ba 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs @@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Core.TestHelpers new EnvelopeHeadersConverter(), new JsonValueConverter(), new StringEnumConverter(), - new SurrogateConverter(), + new SurrogateConverter(), new SurrogateConverter, JsonFilterSurrogate>(), new SurrogateConverter(), new SurrogateConverter(), diff --git a/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/TestUtils.cs b/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/TestUtils.cs index 18a0ce487..067a91221 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/TestUtils.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/TestUtils.cs @@ -36,7 +36,7 @@ namespace Squidex.Infrastructure.TestHelpers SerializationBinder = new TypeNameSerializationBinder(typeNameRegistry ?? new TypeNameRegistry()), ContractResolver = new ConverterContractResolver( - new SurrogateConverter(), + new SurrogateConverter(), new EnvelopeHeadersConverter(), new JsonValueConverter(), new SurrogateConverter, JsonFilterSurrogate>(), diff --git a/backend/tests/Squidex.Web.Tests/Pipeline/ApiPermissionUnifierTests.cs b/backend/tests/Squidex.Web.Tests/Pipeline/ApiPermissionUnifierTests.cs index a56e9d621..7520f09f7 100644 --- a/backend/tests/Squidex.Web.Tests/Pipeline/ApiPermissionUnifierTests.cs +++ b/backend/tests/Squidex.Web.Tests/Pipeline/ApiPermissionUnifierTests.cs @@ -22,11 +22,11 @@ namespace Squidex.Web.Pipeline public async Task Should_add_admin_permission_if_user_is_in_role(string role) { var userIdentity = new ClaimsIdentity(); - var userPrinicpal = new ClaimsPrincipal(userIdentity); + var userPrincipal = new ClaimsPrincipal(userIdentity); userIdentity.AddClaim(new Claim(userIdentity.RoleClaimType, role)); - var result = await sut.TransformAsync(userPrinicpal); + var result = await sut.TransformAsync(userPrincipal); Assert.Equal(Permissions.Admin, result.Claims.FirstOrDefault(x => x.Type == SquidexClaimTypes.Permissions)?.Value); Assert.Equal(role, result.Claims.FirstOrDefault(x => x.Type == userIdentity.RoleClaimType)?.Value); @@ -36,11 +36,11 @@ namespace Squidex.Web.Pipeline public async Task Should_not_add_admin_persmission_if_user_has_other_role() { var userIdentity = new ClaimsIdentity(); - var userPrinicpal = new ClaimsPrincipal(userIdentity); + var userPrincipal = new ClaimsPrincipal(userIdentity); userIdentity.AddClaim(new Claim(userIdentity.RoleClaimType, "Developer")); - var result = await sut.TransformAsync(userPrinicpal); + var result = await sut.TransformAsync(userPrincipal); Assert.Single(result.Claims); } @@ -49,9 +49,9 @@ namespace Squidex.Web.Pipeline public async Task Should_not_add_admin_persmission_if_user_has_no_role() { var userIdentity = new ClaimsIdentity(); - var userPrinicpal = new ClaimsPrincipal(userIdentity); + var userPrincipal = new ClaimsPrincipal(userIdentity); - var result = await sut.TransformAsync(userPrinicpal); + var result = await sut.TransformAsync(userPrincipal); Assert.Empty(result.Claims); }