How to integrate ADFS with episerver and handle return uri based on multisite

Vote:
 

Hi,

My webapp host on DXC as mutiple sites and intergrated with ADFS.

I want to set Wtrealm property based on domain. But in startup it's init too early to catch domain name. So i can't handle return uri based on multisite

For examle:

if user access domain

  abc.com.sg then Wtrealm = abc.com.sg

  abc.com.vn then Wtrealm =   abc.com.vn

Code example

 public class Startup
    {
        const string LogoutUrl = "/util/logout.aspx";

        public void Configuration(IAppBuilder app)
        {
            // Add CMS integration for ASP.NET Identity
            app.AddCmsAspNetIdentity<SiteUser>();
            //app.AddCmsAspNetIdentity<SiteUser>(new ApplicationOptions()
            //{
            //    ConnectionStringName = _connectionStringHandler.Commerce.Name
            //});

            //federated authentication
            app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
            });
            app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions()
            {
                //URL to federation server meta data
                //AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
                MetadataAddress = ConfigurationManager.AppSettings["MetadataAddress"],
                //Value of Wtrealm must *exactly* match what is configured in the federation server
                Wtrealm = ConfigurationManager.AppSettings["AdfsWtRealm"],
                UseTokenLifetime = false,
                Notifications = new WsFederationAuthenticationNotifications()

Regards,

Thang Le

#216381
Edited, Feb 05, 2020 11:00
Vote:
 

Hi Le

As you wrote, when setting the Wtrealm property at startup, it is not in the context of any request. So it can only be set to a default value.

But you could try implementing a delegate method for the Notifications.RedirectToIdentityProvider property. That will be called when the middleware is just about to redirect. So there you could try getting the domain name of the current request and then set the Wtrealm property of that specific redirect action.

#216400
Feb 05, 2020 13:13
Vote:
 

Hi Stefan Holm Olsen,

Thank for your reply

Unfortunately, I tried but it didn't work and also throw an exception from adfs, i think MetadataAddress and Wtrealm have to set earlier than method Notifications

Now i just want adfs redirect correct the domain when user login from

for example:

- user access abc.com.sg and click login -> redirect to adfs -> enter username and pw -> adfs redicet back to abc.com.sg

- user access abc.com.vn and click login -> redirect to adfs -> enter username and pw -> adfs redicet back to abc.com.vn

Do you have any suggestion?

   public class Startup
    {
        const string LogoutUrl = "/util/logout.aspx";

        public void Configuration(IAppBuilder app)
        {
            // Add CMS integration for ASP.NET Identity
            app.AddCmsAspNetIdentity<SiteUser>();
            //app.AddCmsAspNetIdentity<SiteUser>(new ApplicationOptions()
            //{
            //    ConnectionStringName = _connectionStringHandler.Commerce.Name
            //});

            //federated authentication
            app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
            });
            // au
            app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions()
            {
                //URL to federation server meta data
                //AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
                MetadataAddress = ConfigurationManager.AppSettings["MetadataAddress"],
                //Value of Wtrealm must *exactly* match what is configured in the federation server
                Wtrealm = ConfigurationManager.AppSettings["AdfsWtRealm"],
                UseTokenLifetime = false,
                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();
                        }
                        return Task.FromResult(0);
                    },
                    SecurityTokenValidated = (ctx) =>
                    {
                        //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.PathAndQuery;
                        }
                        //Sync user and the roles to EPiServer in the background
                        ServiceLocator.Current.GetInstance<ISynchronizingUserService>().SynchronizeAsync(ctx.AuthenticationTicket.Identity);
                        return Task.FromResult(0);
                    }
                },
                
            });

            // local Episerver authentication
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Login"),
                Provider = new CookieAuthenticationProvider
                {
                    // Enables the application to validate the security stamp when the user logs in.
                    // This is a security feature which is used when you change a password or add an external login to your account.
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager<ApplicationUser>, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentity: (manager, user) => manager.GenerateUserIdentityAsync(user))
                }
            });

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

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


            app.Use((context, task) =>
            {
                if (context.Request.Uri.PathAndQuery.Contains("remote.jpg.ashx"))
                    return task.Invoke();

                var customHeaders = ServiceLocator.Current.GetInstance<SiteSettingsHandler>().SiteSettings
                    .SecurityHeaders;
                
                if (customHeaders == null || !customHeaders.Any()) 
                    return task.Invoke();

                foreach (var securityHeader in customHeaders)
                {
                    if(!context.Response.Headers.ContainsKey((securityHeader.Name)))
                        context.Response.Headers.Add(securityHeader.Name, new[] { securityHeader.Value});
                }

                return task.Invoke();
            });


            //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;
        }
    }

Regards,

Thang Le

#216439
Edited, Feb 06, 2020 9:46
Stefan Holm Olsen - Feb 07, 2020 8:14
What is the exception that ADFS throws?
Vote:
 

Hi Stefan,

After tried some solution. I have solved OWIN for multiple sites by setting the Wreply property to the actual site URL before redirecting to ADFS.
I also have to specify all sites as Endpoints in the ADFS Relying Party Trust configuration for the installation. 
This way we can have only one IIS site for multiple EPiServer sites using ADFS.
All sites will use the same Relying Party Trust definition in ADFS som the same set of claims will be issued for all sites.

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

        {
            context.ProtocolMessage.Wreply = SiteDefinition.Current.SiteUrl.ToString().TrimEnd('/');
        }

Regards,

Thang Le

#216495
Edited, Feb 07, 2020 8:24
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.