November Happy Hour will be moved to Thursday December 5th.

Multisite ADFS authentication not working

Vote:
 

Hi

I've referenced the below similar tickets on this topic to setup the ADFS Authentication for multisite :

https://world.optimizely.com/forum/developer-forum/CMS/Thread-Container/2020/12/sso-with-multisite/

https://world.optimizely.com/forum/developer-forum/CMS/Thread-Container/2020/2/how-to-integrate-adfs-with-episerver-in-case-run-as-multiple-sites/

However, these solutions have not worked for me so far. I can still get into the main site successfully, but if I try going to any other site, it tries to validate against ADFS and throws "Audience Validation Failed" error.

Few notes :

  1. The multisites are separate domains but hosted on same instance, so they share code and web.config.
  2. In ADFS, for troubleshooting purposes, we tried both types of setups : adding new domian to existing setup's relying party trust identifiers and setting up a new setuo for one of the domains. But neither seem to be working. They both throw the same "Audience Validation Failed" error stating that the only valid audience ADFS is looking at is the main site url and not the one we are passing now.
  3. I've requested IT to share screenshots of ADFS setup so those can be reviewed.

Here's my code snippet in Startup.cs :

public void Configuration(IAppBuilder app)
        {
            string WtRealmUrl = "";
            HomePage homePage = null;
            
            _contentLoader.TryGet(ContentReference.StartPage, out homePage);
            if (homePage != null)
            {
                var signInPage = homePage.SignInPage;
                if (signInPage != null)
                {
                    LoginPage = _urlResolver.GetUrl(homePage.SignInPage);
                }

                //get WtRealm url from HomePage
                var epiSection = System.Web.Configuration.WebConfigurationManager.GetSection("episerver");
                var enableScheduler = ((EPiServerSection)epiSection).ApplicationSettings.EnableScheduler;
                if (enableScheduler)
                {
                    if (!environment.Equals("INTE"))
                    {
                        WtRealmUrl = homePage.WtRealmSchedulerSite;
                    }
                    else
                    {
                        WtRealmUrl = SiteDefinition.Current.SiteUrl.AbsoluteUri;
                    }
                }
                else
                {
                    WtRealmUrl = homePage.WtRealmMainSite;
                }
            }

            if (String.IsNullOrEmpty(WtRealmUrl))
            {
                _logger.Error("WtRealm url not found on homepage, so setting from config.");
                WtRealmUrl = System.Web.Configuration.WebConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
            }
           

                //Enable federated authentication
                app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions()
                {
                    //Trusted URL to federation server meta data
                    MetadataAddress = "https://adfs.centurycommunities.com/federationmetadata/2007-06/federationmetadata.xml",
                    //Value of Wtreal must *exactly* match what is configured in the federation server
                    //Wtrealm = System.Web.Configuration.WebConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"],
                    Wtrealm = WtRealmUrl,
                    TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = false,
                        RoleClaimType = ClaimTypes.Role
                    },
                    Wreply = WtRealmUrl,
                    Notifications = new WsFederationAuthenticationNotifications()
                    {
                        RedirectToIdentityProvider = (ctx) =>
                        {
                            HandleMultiSitereturnUrl(ctx);                           

                            //To avoid a redirect loop to the federation server send 403 when user is authenticated but does not have access
                            if (ctx.OwinContext.Response.StatusCode == 401 && ctx.OwinContext.Authentication.User.Identity.IsAuthenticated)
                            {
                                ctx.OwinContext.Response.StatusCode = 403;
                                ctx.HandleResponse();
                            }


                            //XHR requests cannot handle redirects to a login screen, return 401
                            if (ctx.OwinContext.Response.StatusCode == 401 && IsXhrRequest(ctx.OwinContext.Request))
                            {
                                ctx.HandleResponse();
                            }
                            return Task.FromResult(0);
                        },
                        SecurityTokenValidated = (ctx) =>
                        {
                            //set role claim to lower case
                            var roleClaim = ctx.AuthenticationTicket.Identity.Claims.FirstOrDefault(x => x.Type == ctx.AuthenticationTicket.Identity.RoleClaimType);
                            if (roleClaim != null && roleClaim.Value.ToLower().StartsWith("secgrp"))
                            {
                                ctx.AuthenticationTicket.Identity.RemoveClaim(roleClaim);

                                var updatedRoleClaim = new Claim(ctx.AuthenticationTicket.Identity.RoleClaimType, roleClaim.Value.ToLower());
                                ctx.AuthenticationTicket.Identity.AddClaim(updatedRoleClaim);
                            }
                            try
                            {
                                using (var context = new EpiServerCMSDbContext())
                                {
                                    context.Database.ExecuteSqlCommand("update [tblContentAccess] set Name = LOWER(Name) where LOWER(Name) like 'secgrp%'");
                                }
                            }
                            catch (Exception ex)
                            {
                                _logger.Error($"Error when setting secgrp roles to LowerCase", ex);
                            }
                            bool isSalesAgent = false;
                            foreach (var claim in ctx.AuthenticationTicket.Identity.Claims)
                            {
                                 if (claim.Type.Equals(ctx.AuthenticationTicket.Identity.RoleClaimType) && claim.Value.ToLower().Contains("saleteams"))
                                {
                                    isSalesAgent = true;
                                }

                            }
                            //Sync user and the roles to EPiServer in the background
                            ServiceLocator.Current.GetInstance<ISynchronizingUserService>().SynchronizeAsync(ctx.AuthenticationTicket.Identity);
                            ctx.AuthenticationTicket.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddHours(2);

                            ctx.AuthenticationTicket.Properties.IsPersistent = true;

                            //Ignore scheme/host name in redirect Uri to make sure a redirect to HTTPS does not redirect back to HTTP
                            var redirectUri = new Uri(ctx.AuthenticationTicket.Properties.RedirectUri, UriKind.RelativeOrAbsolute);
                            if (redirectUri.IsAbsoluteUri)
                            {
                                ctx.AuthenticationTicket.Properties.RedirectUri = redirectUri.AbsoluteUri;//.PathAndQuery;
                                _logger.Error("Return url with Path and Query : " + ctx.AuthenticationTicket.Properties.RedirectUri);
                            }

                            if (isSalesAgent)
                            {
                                //change redirect login to site homepage instead
                                ctx.AuthenticationTicket.Properties.RedirectUri = WtRealmUrl;
                            }

                            return Task.FromResult(0);

                        }
                    }
                }) ;

                //Remap logout to a federated logout
                app.Map(LogoutPath, map =>
                {
                    map.Run(ctx =>
                    {
                        ctx.Authentication.SignOut();
                        return Task.FromResult(0);
                    });
                });          


            // Add stage marker to make sure WsFederation runs on Authenticate(before URL Authorization and virtual roles)
            app.UseStageMarker(PipelineStage.Authenticate);

            //Tell antiforgery to use the name claim
            AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;

        }

        private void HandleMultiSitereturnUrl(
                 RedirectToIdentityProviderNotification<Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage,
                                         WsFederationAuthenticationOptions> context)

        {
            context.Options.Wtrealm = "https://" + HttpContext.Current.Request.Url.Host;
            //context.Options.Wreply = "https://" + HttpContext.Current.Request.Url.Host;
        }

At this point, having tried multiple things, we aren't entirely sure whether we need to change something in code here, or some setup in ADFS. Any thoughts here would be really helpful.

Regards

Ritu

#268929
Dec 23, 2021 15:52
* 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.