Try our conversational search powered by Generative AI!

Mixed-mode authentication - Identity Server/CMS Identity

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)

        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.MapInboundClaims = false;

                    options.Events.OnRedirectToIdentityProvider = context =>
                        // Prevent redirect loop
                        if (context.Response.StatusCode == 401)

                        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 =>

                        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:

[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]

I have referenced the following Optimizely page but after trying various ways I still cannot get it to allow me to login via the /login method as well as being able to login via the /episerver/cms method.

Does anyone have any experience of successfully implementing mixed-mode authentication in CMS12, or anyone who can provide any pointers/help.

Thanks in advance.
Jan 31, 2023 17:14

Hi Graham

Your code seems fine at first glance.

One thing to call out is that you should use "policy-scheme" for your default schemes as it is this custom policy scheme which then controls which authentication scheme to use

                    options.DefaultScheme = "policy-scheme";
                    options.DefaultChallengeScheme = "policy-scheme";
Feb 03, 2023 2:32

Hi Ron,

Many thanks for your reply, I have updated the default scheme as shown above but the issue still persists. The functionality to login via the FE via Identity Server works fine until you add the:


line and then it stops working, you can login via Identity Server fine but the user session/cookie is not retained. You can however login to the CMS no problem with it's own session/cookie being retained with no problem.



Feb 07, 2023 14:27

Did you find a solution?

I'm facing the same problem. Coming to the login page and loging in but then stay on the login page instead of redirecting to the actual page.

Oct 16, 2023 8:52

hi, I did find a solution in the end, and I wrote a blog post about it which can be viewed here hope that helps?

Oct 16, 2023 8:59

I've read your post, very nice! 

Unfortunatly it doesn't work for me although I don't have exactly the same setup.

I need to have azuread and aspnet identity. I followed your guide, my problem is that loging in with azuread into /episerver admin and edit is working well. Then I want some pages to have read access rights for a specific user in the db. So I set the access right in the tree in the admin.

Then when I try to access this page I get redirected to the login so far so good.

I then log in with the user credential, it worked BUT the redirect is not to the page but back to the login page?

Oct 16, 2023 9:06
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.