volume_up

A critical vulnerability was discovered in React Server Components (Next.js). Our systems remain protected but we advise to update packages to newest version. Learn More

volume_up

A critical vulnerability was discovered in React Server Components (Next.js). Our systems remain protected but we advise to update packages to newest version. Learn More

impersonation during the login process

Hello,

We are using the EPi_AspNetIdentityUserProvider in order to set the policies to the pages, blocks etc.
But we want to use Azure as the ISP-Provider for Authentfication.
Later on maybe also some more other ISP-Provider

So I in the startup
I'm calling ...

        services
            .AddCmsAspNetIdentity<ApplicationUser>()

and later on i I setup the Azure - Authentification.
The problem came when the user  has been "azure - authentificated".

This is the code - snippet where the user get's  "azure - authentificated" within the OnTokenValidated-Event:

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

                    var claims = ctx.Principal.Identity as ClaimsIdentity;
                    var allClaims = claims.Claims.ToArray();

                    var emailClaim = claims.Claims.FirstOrDefault(claim => claim.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
                    var emailAddress = emailClaim?.Value ?? "";
                    var roles = AuthentificationRepository.GetUserRoles(aaOptions.AspNetConnectionString, emailAddress);
                    foreach (var role in roles)
                    {
                        Claim claim = new Claim("claims/role", role);
                        claims.AddClaim(claim);
                    }

                    ServiceLocator.Current.GetInstance<ISynchronizingUserService>().SynchronizeAsync(claims);
                    return Task.FromResult(0);
                };


I see two strategies:
-----------------------------
Strategy 1):
  With the "azure - authentificated user" I read the user's roles/claims from the EpiServerDb  and  add the EpiServer-roles/claims  to the "azure - authentificated user" - claims
  I tried this in the code snippet above without success.

Strategy 2):
  With the "azure - authentificated user"  I can archieve a impersonation to a "EPi_AspNetIdentityUserProvider - user"

Do you see any solution how to solve this issue ?

#296576
Feb 15, 2023 14:38

If using Azure AD or B2C your roles should be managed in that platform and then synched to CMS, (You can still set Access Rights on these Synched Roles). 

    /// <summary>
    /// Azure ad extension
    /// </summary>
    public static class AzureAdExtensions
    {
        /// <summary>
        /// Azure Ad authentication
        /// </summary>
        /// <param name="services"></param>
        /// <param name="configuration"></param>
        /// <param name="forceHttpsRedirect"></param>
        public static void ConfigureAzureAd(this IServiceCollection services, IConfiguration configuration, bool forceHttpsRedirect)
        {
            services
                .AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
                .AddMicrosoftIdentityWebApp(
                    identityOptions =>
                    {
                        configuration.Bind("AzureAd", identityOptions);

                        identityOptions.Scope.Add("email");
                        identityOptions.TokenValidationParameters = new TokenValidationParameters
                        {
                            RoleClaimType = ClaimTypes.Role,
                            NameClaimType = ClaimTypes.Email
                        };

                        identityOptions.Events ??= new OpenIdConnectEvents();
                        identityOptions.Events.OnTokenValidated = async (context) =>
                        {
                            if (context?.Principal == null)
                            {
                                await Task.FromResult(0);
                                return;
                            }

                            if (!string.IsNullOrEmpty(context.Properties.RedirectUri))
                            {
                                var redirectUri = new Uri(context.Properties.RedirectUri, UriKind.RelativeOrAbsolute);
                                if (redirectUri.IsAbsoluteUri)
                                {
                                    context.Properties.RedirectUri = redirectUri.PathAndQuery;
                                }
                            }

                            await ServiceLocator.Current.GetInstance<ISynchronizingUserService>().SynchronizeAsync(context.Principal?.Identity as ClaimsIdentity);
                        };

                        identityOptions.Events.OnRedirectToIdentityProvider = async (context) =>
                        {
                            if (forceHttpsRedirect)
                            {
                                context.ProtocolMessage.RedirectUri =
                                    context.ProtocolMessage.RedirectUri.Replace("http:", "https:");
                            }

                            await Task.FromResult(0);
                        };
                    },
                    null,
                    OpenIdConnectDefaults.AuthenticationScheme,
                    CookieAuthenticationDefaults.AuthenticationScheme,
                    true);
        }
        /// <summary>
        /// Configure cookie policy
        /// </summary>
        /// <param name="services"></param>
        /// <param name="setCookieDomain"></param>
        public static void ConfigureCookiePolicy(this IServiceCollection services, bool setCookieDomain)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
                options.OnAppendCookie = cookieContext =>
                {
                    if (cookieContext.CookieOptions.SameSite == SameSiteMode.None)
                    {
                        cookieContext.CookieOptions.SameSite = SameSiteMode.Unspecified;
                    }

                    // this should be environment specific
                    if (setCookieDomain)
                    {
                        cookieContext.CookieOptions.Domain = ".localhost";
                    }
                };

                options.OnDeleteCookie = cookieContext =>
                {
                    if (cookieContext.CookieOptions.SameSite == SameSiteMode.None)
                    {
                        cookieContext.CookieOptions.SameSite = SameSiteMode.Unspecified;
                    }
                };
            });
        }
    }

UI Role Provider 

    public class UIRoleProviderAad : UIRoleProvider
    {
        private EPiServer.ServiceLocation.Injected<SecurityEntityProvider> _uiRoleProvider;

        public override bool Enabled { get; set; } = true;

        public override string Name => "UIRoleProviderAAD";

        public override async IAsyncEnumerable<IUIRole> GetAllRolesAsync()
        {
            var uiRoles = await _uiRoleProvider.Service.SearchAsync(null, ClaimTypes.Role);

            foreach (var item in uiRoles.Select(r => new UIRoleImpl(r.Name, "DefaultProvider")))
            {
                yield return item;
            }
        }
    }

Role Model

    public class UIRoleImpl : IUIRole
    {
        public UIRoleImpl()
        {
        }

        public UIRoleImpl(string name, string provider)
        {
            Name = name;
            ProviderName = provider;
        }

        public string Name { get; set; }

        public string ProviderName { get; set; }
    }

Startup.cs

            services.ConfigureAzureAd(Configuration, !_environment.IsDevelopment());
            services.ConfigureCookiePolicy(!_environment.IsDevelopment());
            services.AddTransient<UIRoleProvider, UIRoleProviderAad>();

You can also then Map your roles to virtual roles in appsettings e.g. 

  "EPiServer": {
    "Cms": {
      "MappedRoles": {
        "Items": {
          "CmsEditors": {
            "MappedRoles": [ "WebEditors", "WebAdmins" ]
          },
          "CmsAdmins": {
            "MappedRoles": [ "WebAdmins" ]
          }
        }
      }
    }
#296577
Edited, Feb 15, 2023 15:59

Hi Minesh,

thanks a lot for your reply. I tested the solution and it already works on my side.

But I think  that the solution fails if you have two ISP-providers (e.g. Azure for inhouse colleques + Google for external employees), what is the future's plan.

Example:

Let's say we have to users:
   - user1 -> inhouse user authentificated in azure to the role ContentEditorGroup1 in azure
   - user2 -> external user authentificated in google to the role  ContentEditorGroup1 in google

Is it not the case that role mapping will fail in that case?

#296578
Feb 15, 2023 17:22

Have a look at Mixed Mode Authentication I believe if the roles have the same name it should work, although suggestion would be to use unique roles and than map to a virtual role 

https://docs.developers.optimizely.com/content-cloud/v12.0.0-content-cloud/docs/mixed-mode-authentication 

#296579
Feb 15, 2023 17:26
* 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.