I am struggling with getting mixed-mode authentication working in CMS12, we have an Identity Server that is accessed via OpenIdConnect for members to login via the website /login route, and we also need to retain the normal CMS ASP.Net Identity for logging into the CMS. I can get the members /login to work fine, but the login via the CMS then does not work, I see the login page but upon login I am just returned to the login page with no error messages shown.
The code I have in my startup class as a middleware extension is below:
namespace xxx.Web.Infrastructure.ServiceExtensions;
using System.Text;
using xxx.Features.Common.Configuration;
using EPiServer.Cms.UI.AspNetIdentity;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
public static class UserAuthenticationServiceExtensions
{
private const string AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme;
private const string ChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
public static IServiceCollection AddUserAuthentication(
this IServiceCollection services,
IWebHostEnvironment environment,
IConfiguration configuration)
{
services.AddCmsAspNetIdentity<ApplicationUser>();
services.AddIdentityServer(configuration);
return services;
}
/// <summary>
/// Sets up authentication based on Identity Server 4 using Open ID Connect
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <returns></returns>
public static void AddIdentityServer(this IServiceCollection services, IConfiguration configuration)
{
var identityServerSettings = configuration.GetSection(nameof(IdentityServerSettings)).Get<IdentityServerSettings>();
var authority = identityServerSettings?.Authority ?? string.Empty;
_ = bool.TryParse(identityServerSettings?.RequireHttpsMetadata ?? "true", out bool requireHttpsMetadata);
var clientId = identityServerSettings?.ClientId ?? string.Empty;
var clientSecret = identityServerSettings?.ClientSecret?? string.Empty;
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = AuthenticationScheme;
options.DefaultChallengeScheme = "policy-scheme";
})
.AddCookie(AuthenticationScheme, options =>
{
// Defines a path to redirect the user to if they don't have access to a page.
// This page should return a 200 response so as to not cause authentication loops.
options.AccessDeniedPath = new PathString("/no-access");
})
.AddOpenIdConnect(ChallengeScheme, options =>
{
options.SignInScheme = AuthenticationScheme;
options.SignOutScheme = AuthenticationScheme;
options.ResponseType = OpenIdConnectResponseType.Code;
options.CallbackPath = "/signin-oidc";
options.UsePkce = false;
options.Authority = authority;
options.RequireHttpsMetadata = requireHttpsMetadata;
options.ClientId = clientId;
options.ClientSecret = clientSecret;
options.Scope.Clear();
options.Scope.Add(OpenIdConnectScope.OpenId);
options.Scope.Add("xxx");
options.MapInboundClaims = false;
options.Events.OnRedirectToIdentityProvider = context =>
{
// Prevent redirect loop
if (context.Response.StatusCode == 401)
{
context.HandleResponse();
}
if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var idTokenHint = context.HttpContext.User.FindFirst("id_token");
if (idTokenHint != null)
{
context.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
}
return Task.CompletedTask;
};
options.Events.OnAuthenticationFailed = async context =>
{
context.HandleResponse();
await context.Response.BodyWriter.WriteAsync(Encoding.ASCII.GetBytes(context.Exception.Message));
};
})
.AddPolicyScheme("policy-scheme", null, options =>
{
options.ForwardDefaultSelector = ctx =>
{
if (ctx.Request.Path.StartsWithSegments("/episerver", StringComparison.OrdinalIgnoreCase))
{
return "Identity.Application";
}
return OpenIdConnectDefaults.AuthenticationScheme;
};
});
}
}
The Authentication controller for the front-end users has been decorated with the following attribute:
The code I have in my startup class as a middleware extension is below:
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
Does anyone have any experience of successfully implementing mixed-mode authentication in CMS12, or anyone who can provide any pointers/help.
Thanks in advance.