Try our conversational search powered by Generative AI!

Optimizely 12 - Separate admin login when using OpenIdConnect

Vote:
 
I want to use OpenID Connect for all end users and use standard Episerver login for all editors.
Is there any way to accomplish this in Optimizely 12?

This is my current setup. Now everyone is moving towards the external provider.
What I would like to achieve is that the url "/episerver/cms" uses the standard login.
services
            .AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddOpenIdConnect(
                options =>
                {
                    options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    options.ClientId = "####";
                    options.ClientSecret = "####";
                    options.Authority = "#####";
                    options.CallbackPath = "/callback";
                    options.ResponseType = "code";
                    options.GetClaimsFromUserInfoEndpoint = true;

                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = false,
                        RoleClaimType = ClaimTypes.Role,
                        NameClaimType = ClaimTypes.Email
                    };

                    options.Events.OnAuthenticationFailed = ctx =>
                    {
                        ctx.HandleResponse();
                        ctx.Response.BodyWriter.WriteAsync(Encoding.ASCII.GetBytes(ctx.Exception.Message));
                        return Task.FromResult(0);
                    };

                    options.Events.OnTokenValidated = (ctx) =>
                    {
                        var redirectUri = new Uri(ctx.Properties.RedirectUri, UriKind.RelativeOrAbsolute);
                        if (redirectUri.IsAbsoluteUri)
                        {
                            ctx.Properties.RedirectUri = redirectUri.PathAndQuery;
                        }

                        //Sync user and the roles to EPiServer in the background
                        //ServiceLocator.Current.GetInstance<ISynchronizingUserService>().SynchronizeAsync(ctx.Principal.Identity as ClaimsIdentity);
                        
                        return Task.FromResult(0);
                    };
                });
#271143
Edited, Feb 04, 2022 11:30
Vote:
 

Hello :-) Have you found the solution? I'm struggling with the same issue, the events are not triggered actually!

#275906
Mar 08, 2022 13:12
Vote:
 

I have got this working with Identity Server for the website user's logins and the standard ASPNet Identity for CMS logins, see my extension class below:

public static class UserAuthenticationServiceExtensions
{
    private const string AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme;

    private const string ChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;

    /// <summary>
    /// Sets up authentication based on one of the following schemes:
    /// Optimizely CMS Identities for the CMS login.
    /// Identity Server using Open ID connect for front-end user login.
    /// </summary>
    /// <param name="services"></param>
    /// <param name="environment"></param>
    /// <param name="configuration"></param>
    /// <returns></returns>
    public static IServiceCollection AddUserAuthentication(
        this IServiceCollection services,
        IWebHostEnvironment environment,
        IConfiguration configuration)
    {
        services.AddIdentityServer(configuration);
        services.AddCmsAspNetIdentity<ApplicationUser>();

        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.DefaultScheme = 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");
                })
                .AddCookie(ChallengeScheme)
                .AddOpenIdConnect(ConfigConstants.IdentityServerAuthenticationScheme, options =>
                {
                    options.SignInScheme = ChallengeScheme;
                    options.SignOutScheme = ChallengeScheme;
                    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.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) ||
                        ctx.Request.Path.StartsWithSegments("/util", StringComparison.OrdinalIgnoreCase))
                       {
                           return IdentityConstants.ApplicationScheme;
                       }

                       return ConfigConstants.IdentityServerAuthenticationScheme;
                   };
                });
    }

You then need to ensure you decorate your page controllers with the following attributes:

[Authorize(AuthenticationSchemes = AuthSchemes, Roles = AuthRoles)]
[AllowAnonymous]

with the constants for the above being as follows:

    private const string AuthSchemes = "IdentityServer,Identity.Application";
    private const string AuthRoles = "CmsAdmins,CmsEditors,Everyone";

You should be able to adapt the above to your needs, however the important parts for you are ensuring you have two separate cookies created, and also to ensure you create a policy scheme which allows you to route to the correct authentication scheme.

Hope this helps a bit?

#299510
Apr 04, 2023 13:14
* 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.