diff --git a/backend/src/Squidex.Infrastructure/Security/Extensions.cs b/backend/src/Squidex.Infrastructure/Security/Extensions.cs index bc06e33a1..d7fd08294 100644 --- a/backend/src/Squidex.Infrastructure/Security/Extensions.cs +++ b/backend/src/Squidex.Infrastructure/Security/Extensions.cs @@ -67,6 +67,12 @@ namespace Squidex.Infrastructure.Security return principal.Claims.FirstOrDefault(x => x.Type == OpenIdClaims.Email)?.Value; } + public static string? TryFindEmail(this ClaimsPrincipal principal) + { + return principal.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Email)?.Value ?? + principal.Claims.FirstOrDefault(x => x.Type == OpenIdClaims.Email)?.Value; + } + public static bool IsInClient(this ClaimsPrincipal principal, string client) { return principal.Claims.Any(x => x.Type == OpenIdClaims.ClientId && string.Equals(x.Value, client, StringComparison.OrdinalIgnoreCase)); diff --git a/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs b/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs index f7fb7c1ad..ccdb8f59a 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs @@ -294,7 +294,7 @@ namespace Squidex.Areas.IdentityServer.Controllers.Account } else { - var email = externalLogin.Principal.FindFirst(ClaimTypes.Email)?.Value!; + var email = externalLogin.Principal.TryFindEmail(); user = await userManager.FindByEmailWithClaimsAsync(email); diff --git a/backend/src/Squidex/Areas/IdentityServer/Controllers/Extensions.cs b/backend/src/Squidex/Areas/IdentityServer/Controllers/Extensions.cs index 42c250066..8a54c7232 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Controllers/Extensions.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Controllers/Extensions.cs @@ -12,6 +12,7 @@ using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Identity; +using Squidex.Infrastructure.Security; using Squidex.Web; namespace Squidex.Areas.IdentityServer.Controllers @@ -22,7 +23,7 @@ namespace Squidex.Areas.IdentityServer.Controllers { var externalLogin = await signInManager.GetExternalLoginInfoAsync(expectedXsrf); - var email = externalLogin.Principal.FindFirst(ClaimTypes.Email)?.Value; + var email = externalLogin.Principal.TryFindEmail(); if (string.IsNullOrWhiteSpace(email)) { diff --git a/backend/src/Squidex/Config/Authentication/OidcHandler.cs b/backend/src/Squidex/Config/Authentication/OidcHandler.cs index fcde1287f..b1f77deb8 100644 --- a/backend/src/Squidex/Config/Authentication/OidcHandler.cs +++ b/backend/src/Squidex/Config/Authentication/OidcHandler.cs @@ -40,5 +40,19 @@ namespace Squidex.Config.Authentication return base.TokenValidated(context); } + + public override Task RedirectToIdentityProviderForSignOut(RedirectContext context) + { + if (!string.IsNullOrEmpty(options.OidcOnSignoutRedirectUrl)) + { + var logoutUri = options.OidcOnSignoutRedirectUrl; + context.Response.Redirect(logoutUri); + context.HandleResponse(); + + return Task.CompletedTask; + } + + return base.RedirectToIdentityProviderForSignOut(context); + } } } diff --git a/backend/src/Squidex/Config/Authentication/OidcServices.cs b/backend/src/Squidex/Config/Authentication/OidcServices.cs index 00d82cbcc..1f6d4abf5 100644 --- a/backend/src/Squidex/Config/Authentication/OidcServices.cs +++ b/backend/src/Squidex/Config/Authentication/OidcServices.cs @@ -24,9 +24,21 @@ namespace Squidex.Config.Authentication options.Authority = identityOptions.OidcAuthority; options.ClientId = identityOptions.OidcClient; options.ClientSecret = identityOptions.OidcSecret; - options.RequireHttpsMetadata = false; + options.RequireHttpsMetadata = identityOptions.RequiresHttps; options.Events = new OidcHandler(identityOptions); + if (!string.IsNullOrEmpty(identityOptions.OidcMetadataAddress)) + { + options.MetadataAddress = identityOptions.OidcMetadataAddress; + } + + if (!string.IsNullOrEmpty(identityOptions.OidcResponseType)) + { + options.ResponseType = identityOptions.OidcResponseType; + } + + options.GetClaimsFromUserInfoEndpoint = identityOptions.OidcGetClaimsFromUserInfoEndpoint; + if (identityOptions.OidcScopes != null) { foreach (var scope in identityOptions.OidcScopes) diff --git a/backend/src/Squidex/Config/MyIdentityOptions.cs b/backend/src/Squidex/Config/MyIdentityOptions.cs index ba94ac6b3..05d5f7156 100644 --- a/backend/src/Squidex/Config/MyIdentityOptions.cs +++ b/backend/src/Squidex/Config/MyIdentityOptions.cs @@ -47,12 +47,20 @@ namespace Squidex.Config public string OidcAuthority { get; set; } + public string OidcMetadataAddress { get; set; } + public string OidcRoleClaimType { get; set; } public string[] OidcScopes { get; set; } + public string OidcResponseType { get; set; } + + public bool OidcGetClaimsFromUserInfoEndpoint { get; set; } + public Dictionary OidcRoleMapping { get; set; } + public string OidcOnSignoutRedirectUrl { get; set; } + public bool AdminRecreate { get; set; } public bool AllowPasswordAuth { get; set; } diff --git a/backend/src/Squidex/appsettings.json b/backend/src/Squidex/appsettings.json index 8c4e8db55..5a5e755de 100644 --- a/backend/src/Squidex/appsettings.json +++ b/backend/src/Squidex/appsettings.json @@ -648,9 +648,13 @@ "oidcAuthority": "", "oidcClient": "", "oidcSecret": "", + "oidcMetadataAddress": "", "oidcScopes": [ "email" ], + "oidcResponseType": "id_token", // or "code" + "oidcGetClaimsFromUserInfoEndpoint": false, + "oidcOnSignoutRedirectUrl": "", /* * Lock new users automatically, the administrator must unlock them.