Friday, May 10, 2019

Federated Authentication with Insite Identity Server - Part 2

Customizing Insite Identity Server 

On the Insite side, we had to change the Identity Server ApplicationOptions to honor parameters that are being passed in from Sitecore:

EnableSignOutPrompt = false
EnablePostSignOutAutoRedirect = true 

These parameters the way they are set above allow for the log out redirect to bring the user back to Sitecore after the log out on the Insite side.

To make this happen we created the following class:

[BootStrapperOrder(26)]
    public class ConfigureIdentityServer : IStartupTask, IMultiInstanceDependency, IDependency, IExtension
    {
        public ConfigureIdentityServer()
        {
            DbMigrations.Run("identity");
            LogProvider.SetCurrentLogProvider(new NoopLogProvider());
            CookieAuthenticationOptions authenticationOptions = new CookieAuthenticationOptions();
            authenticationOptions.AuthenticationType = "ApplicationCookie";
            authenticationOptions.LoginPath = new PathString("/RedirectTo/SignInPage");
            authenticationOptions.ExpireTimeSpan = TimeSpan.FromMinutes(20.0);
            authenticationOptions.Provider = new CookieAuthenticationProvider()
            {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<IdentityUserManager, IdentityUser>(TimeSpan.FromMinutes(20.0), (manager, user) => manager.CreateIdentityAsync(user, "ApplicationCookie"))
            };
            SecurityOptions.CookieOptions = authenticationOptions;
            ConfigureIdentityServer.ConfigureIdentityServerOptions();
            AntiForgeryConfig.UniqueClaimTypeIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name";
        }

        /// <summary>
        /// Configures the identity server options.
        /// </summary>
        private static void ConfigureIdentityServerOptions()
        {
            string str = AppSettingProvider.Current["IdentityServerUrl"];
            SecurityOptions.IssuerUri = str.IsBlank() ? "http://www.dummy.com" : str;
            SecurityOptions.RequireSsl = AppSettingProvider.Current["IdentityServerRequireSsl"].EqualsIgnoreCase("true");
        }

        public void Run(IAppBuilder app, HttpConfiguration config)
        {
            app.CreatePerOwinContext(new Func<Insite.IdentityServer.AspNetIdentity.IdentityDbContext>(Insite.IdentityServer.AspNetIdentity.IdentityDbContext.Create));
            app.CreatePerOwinContext(new Func<IdentityFactoryOptions<IdentityUserManager>, IOwinContext, IdentityUserManager>(IdentityUserManager.Create));
            app.CreatePerOwinContext(new Func<IdentityFactoryOptions<IdentitySignInManager>, IOwinContext, IdentitySignInManager>(IdentitySignInManager.Create));
            app.UseKentorOwinCookieSaver();
            IdentityServerBearerTokenAuthenticationOptions adminTokenOptions = new IdentityServerBearerTokenAuthenticationOptions()
            {
                Authority = SecurityOptions.IssuerUri,
                RequiredScopes = new string[1]
              {
          "isc_admin_api"
              },
                NameClaimType = "preferred_username",
                RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
                TokenProvider = new MixedOAuthBearerAuthenticationProvider(),
                IssuerName = SecurityOptions.IssuerUri,
                SigningCertificate = Certificate.Get(),
                ValidationMode = ValidationMode.Local
            };
            MapExtensions.Map(app, "/admin", admin =>
            {
                admin.UseIdentityServerBearerTokenAuthentication(adminTokenOptions);
                admin.MapSignalR();
            });

            MapExtensions.Map(app, "/secureElmah", admin => admin.UseIdentityServerBearerTokenAuthentication(adminTokenOptions));
            MapExtensions.Map(app, "/userfiles/_system", admin => admin.UseIdentityServerBearerTokenAuthentication(adminTokenOptions));
            MapExtensions.Map(app, "/api/v1/admin", admin => admin.UseIdentityServerBearerTokenAuthentication(adminTokenOptions));
            MapExtensions.Map(app, "/contentadmin", admin => admin.UseIdentityServerBearerTokenAuthentication(adminTokenOptions));
            MapExtensions.Map(app, "/webpageconverter", admin => admin.UseIdentityServerBearerTokenAuthentication(adminTokenOptions));
            foreach (string micrositeName in GetMicrositeNames())
            {
                MapExtensions.Map(app, "/" + micrositeName + "/contentadmin", admin => admin.UseIdentityServerBearerTokenAuthentication(adminTokenOptions));
            }

            foreach (string authenticationPath in SiteStartup.Instance.GetAdditionalAdminAuthenticationPaths())
            {
                MapExtensions.Map(app, authenticationPath, admin => admin.UseIdentityServerBearerTokenAuthentication(adminTokenOptions));
            }

            app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions()
            {
                Authority = SecurityOptions.IssuerUri,
                RequiredScopes = new string[1]
              {
          "iscapi"
              },
                NameClaimType = "preferred_username",
                RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
                TokenProvider = new MixedOAuthBearerAuthenticationProvider(),
                IssuerName = SecurityOptions.IssuerUri,
                SigningCertificate = Certificate.Get(),
                ValidationMode = ValidationMode.Local
            });
            app.UseCookieAuthentication(SecurityOptions.CookieOptions);
            app.UseExternalSignInCookie("ExternalCookie");
            MapExtensions.Map(app, "/identity", identityApp => identityApp.UseIdentityServer(new IdentityServerOptions()
            {
                SiteName = "ATCC Identity Server",
                CspOptions = new CspOptions() { Enabled = false },
                SigningCertificate = Certificate.Get(),
                Factory = this.ConfigureCustomFactory(Factory.Configure(ConnectionStringProvider.Current.ConnectionStringName)),
                AuthenticationOptions = new AuthenticationOptions()
                {
                    IdentityProviders = new Action<IAppBuilder, string>(ConfigureIdentityProviders),
                    EnableSignOutPrompt = false,
                    EnablePostSignOutAutoRedirect = true
                },
                IssuerUri = SecurityOptions.IssuerUri,
                RequireSsl = SecurityOptions.RequireSsl
            }));
            IdentityUserManager.DataProtectionProvider = app.GetDataProtectionProvider();
        }

        private static IList<string> GetMicrositeNames()
        {
            return DependencyLocator.Current.GetInstance<IUnitOfWorkFactory>().GetUnitOfWork().GetRepository<Website>().GetTable().Select(o => o.MicroSiteIdentifiers).AsEnumerable().SelectMany(o => o.Split(',', ';')).Select(o => o.Trim()).Where(o => !o.IsBlank()).ToList();
        }

        private static void ConfigureIdentityProviders(IAppBuilder app, string signInAsType)
        {
            ConfigureIdentityServer.ConfigureFacebookLogin(app, signInAsType);
            ConfigureIdentityServer.ConfigureGoogleLogin(app, signInAsType);
            ConfigureIdentityServer.ConfigureWindowsAuthentication(app, signInAsType);
        }

        private static void ConfigureGoogleLogin(IAppBuilder app, string signInAsType)
        {
            GoogleSsoSettings googleSsoSettings = SettingsGroupProvider.Current.Get<GoogleSsoSettings>(default(Guid?));
            if (!googleSsoSettings.Enabled)
            {
                return;
            }

            string clientId = googleSsoSettings.ClientId;
            string clientSecret = googleSsoSettings.ClientSecret;
            if (clientId.IsBlank() || clientSecret.IsBlank())
            {
                return;
            }

            IAppBuilder app1 = app;
            GoogleOAuth2AuthenticationOptions options = new GoogleOAuth2AuthenticationOptions
            {
                Caption = "Google",
                ClientId = clientId,
                ClientSecret = clientSecret,
                SignInAsAuthenticationType = signInAsType,
                AuthenticationType = "Google"
            };
            app1.UseGoogleAuthentication(options);
        }

        private static void ConfigureFacebookLogin(IAppBuilder app, string signInAsType)
        {
            FacebookSsoSettings facebookSsoSettings = SettingsGroupProvider.Current.Get<FacebookSsoSettings>(default(Guid?));
            if (!facebookSsoSettings.Enabled)
            {
                return;
            }

            string appId = facebookSsoSettings.AppId;
            string appSecret = facebookSsoSettings.AppSecret;
            if (appId.IsBlank() || appSecret.IsBlank())
            {
                return;
            }

            FacebookAuthenticationOptions authenticationOptions = new FacebookAuthenticationOptions();
            authenticationOptions.AppId = appId;
            authenticationOptions.AppSecret = appSecret;
            authenticationOptions.Caption = "Facebook";
            authenticationOptions.SignInAsAuthenticationType = signInAsType;
            authenticationOptions.AuthenticationType = "Facebook";
            authenticationOptions.Provider = new FacebookAuthenticationProvider()
            {
                OnAuthenticated = async context =>
                {
                    foreach (KeyValuePair<string, JToken> keyValuePair in context.User)
                    {
                        string key = keyValuePair.Key;
                        if (key == "first_name")
                        {
                            context.Identity.AddClaim(new Claim("given_name", keyValuePair.Value.ToString(), "XmlSchemaString", "Facebook"));
                        }
                        else
                        {
                            if (key == "last_name")
                            {
                                context.Identity.AddClaim(new Claim("family_name", keyValuePair.Value.ToString(), "XmlSchemaString", "Facebook"));
                            }
                        }
                    }

                    int num = await Task.FromResult(false) ? 1 : 0;
                }
            };
            authenticationOptions.BackchannelHttpHandler = new ConfigureIdentityServer.FacebookBackChannelHandler();
            authenticationOptions.UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=id,name,email,first_name,last_name";
            FacebookAuthenticationOptions options = authenticationOptions;
            options.Scope.Add("email");
            options.Scope.Add("public_profile");
            app.UseFacebookAuthentication(options);
        }

        private static void ConfigureWindowsAuthentication(IAppBuilder app, string signInAsType)
        {
        }

        private IdentityServerServiceFactory ConfigureCustomFactory(IdentityServerServiceFactory identityServerServiceFactory)
        {
            identityServerServiceFactory.ViewService = (Registration<IViewService>)new CustomViewServiceRegistration<CustomViewService>();
            return identityServerServiceFactory;
        }

        private class FacebookBackChannelHandler : HttpClientHandler
        {
            protected override async Task<HttpResponseMessage> SendAsync(
              HttpRequestMessage request,
              CancellationToken cancellationToken)
            {
                if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
                {
                    request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.Replace("?access_token", "&access_token"));
                }

                return await base.SendAsync(request, cancellationToken);
            }
        }
    }

Previous: Federated Authentication with Insite Identity Server - Part 1

Federated Authentication with Insite Identity Server - Part 1

My co-worker Nick Agnostopolus and I just went through a process of figuring out how to configure Sitecore Federated Authentication in 9.1.1 to use Insite Identity Server as an OpenID Connect provider. After a few long days we spent reflecting the Insite and Sitecore code, we have arrived at the solution. A few customizations had to be done on Insite side to make the whole thing work. Below is a detailed overview of what was done.

Configuring Insite Identity Provider in Sitecore

InsiteIdentity pipeline processor

First, a pipeline processor for Insite Identity had to be created. This is the class where most of the OpenID Connect magic happens.

    public class InsiteIdentity : IdentityProvidersProcessor
    {
        public const string PostLogoutEndPointName = "postinsitelogout";

        private readonly string oauthTokenEndpoint = "connect/token";

        private readonly string oauthUserInfoEndpoint = "connect/userinfo";

        public InsiteIdentity(
            FederatedAuthenticationConfiguration federatedAuthenticationConfiguration,
            ICookieManager cookieManager,
            BaseSettings settings)
            : base(federatedAuthenticationConfiguration, cookieManager, settings)
        {
        }

        public Collection<string> Scopes { get; } = new Collection<string>();

        protected override string IdentityProviderName => "InsiteIdentity";

        protected IdentityProvider IdentityProvider { get; set; }

        private string ClientId
        {
            get
            {
                return Settings.GetSetting("InsiteIdentity.ClientId", "Sitecore");
            }
        }

        private string ClientSecret
        {
            get
            {
                return Settings.GetSetting("InsiteIdentity.ClientSecret");
            }
        }

        private string Authority
        {
            get
            {
                return Settings.GetSetting("InsiteIdentity.Authority");
            }
        }

        private string CallbackAuthority
        {
            get
            {
                return Settings.GetSetting("InsiteIdentity.CallbackAuthority");
            }
        }

        protected override void ProcessCore([NotNull] IdentityProvidersArgs args)
        {
            Assert.ArgumentNotNull(args, nameof(args));

            this.IdentityProvider = this.GetIdentityProvider();
            var authenticationType = this.GetAuthenticationType();

            args.App.UseOpenIdConnectAuthentication(this.CreateOpenIdConnectAuthenticationOptions(authenticationType, this.IdentityProvider));
        }

        protected string BuildPostLogoutRedirectUri(IOwinContext owinContext, string authority)
        {
            Assert.ArgumentNotNull((object)owinContext, nameof(owinContext));
            Uri uri = owinContext.Request.Uri;
            if (string.IsNullOrEmpty(authority))
            {
                authority = uri.GetLeftPart(UriPartial.Authority);
            }

            return InsiteIdentity.CombineUrlPath(authority, this.Settings.IdentityProcessingPathPrefix(), InsiteIdentity.PostLogoutEndPointName) + "?ReturnUrl" + "=" + HttpUtility.UrlEncode(uri.ToString());
        }

        private static string CombineUrlPath(params string[] parts)
        {
            List<string> list = ((IEnumerable<string>)parts).Where<string>((Func<string, bool>)(p => !string.IsNullOrEmpty(p))).ToList<string>();
            string str = string.Join("/", list.Select<string, string>((Func<string, string>)(p => p.Trim('/'))));
            if (list.First<string>()[0] == '/')
            {
                str = "/" + str;
            }

            if (list.Last<string>()[0] == '/')
            {
                str += "/";
            }

            return str;
        }

        private OpenIdConnectAuthenticationOptions CreateOpenIdConnectAuthenticationOptions(string authenticationType, IdentityProvider identityProvider)
        {
            var options = new OpenIdConnectAuthenticationOptions
            {
                Caption = this.IdentityProvider.Caption,
                AuthenticationType = authenticationType,
                AuthenticationMode = AuthenticationMode.Passive,
                ClientId = this.ClientId,
                ClientSecret = this.ClientSecret,
                Authority = this.Authority,
                ResponseType = OpenIdConnectResponseType.CodeIdTokenToken,
                Scope = string.Join(" ", (IEnumerable<string>)this.Scopes),
                UseTokenLifetime = false,
                CookieManager = this.CookieManager,

                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    SecurityTokenValidated = async notification =>
                    {
                        await SecurityTokenValidated(notification);
                    },
                    RedirectToIdentityProvider = notification =>
                    {
                        return RedirectToIdentityProvider(notification);
                    },
                }
            };

            options.TokenValidationParameters.SaveSigninToken = identityProvider.TriggerExternalSignOut;
            return options;
        }

        private async Task SecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {
            UserInfoResponse userInfoResponse;
            using (UserInfoClient userInfoClient = new UserInfoClient(notification.Options.Authority.EnsureTrailingSlash() + this.oauthUserInfoEndpoint))
            {
                LogManager.Diagnostic("[Sitecore Identity] auth_code: " + notification.ProtocolMessage.AccessToken, this);
                userInfoResponse = await userInfoClient.GetAsync(notification.ProtocolMessage.AccessToken, new CancellationToken()).ConfigureAwait(false);
            }

            var additionalClaims = new TransformationContextWithAdditionalClaims(this.FederatedAuthenticationConfiguration, this.IdentityProvider);
            if (userInfoResponse.IsError)
            {
                LogManager.Error("[Sitecore Identity] Unable to get user info - " + userInfoResponse.Error, userInfoResponse.Exception, this);
                LogManager.Diagnostic("[Sitecore Identity] userInfoResponse.Response.Raw: " + userInfoResponse.Raw, this);
            }
            else
            {
                additionalClaims.AdditionalClaims = userInfoResponse.Claims;
            }

            this.AddCustomClaims(notification, userInfoResponse);

            notification.AuthenticationTicket.Identity.ApplyClaimsTransformations((TransformationContext)additionalClaims);

            var transformationContext = new TransformationContextWithAdditionalClaims(this.FederatedAuthenticationConfiguration, this.IdentityProvider);
            foreach (Transformation transformation in (transformationContext.FederatedAuthenticationConfiguration?.SharedClaimsTransformations ?? Enumerable.Empty<Transformation>()).Concat((IEnumerable<Transformation>)transformationContext.IdentityProvider.Transformations))
            {
                transformation.Transform(notification.AuthenticationTicket.Identity, transformationContext);
            }
        }

        private void AddCustomClaims(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification, UserInfoResponse userInfoResponse)
        {
            var firstName = string.Empty;
            var lastName = string.Empty;

            ////Retrieve the claim given_name, and assign to first_name
            if (userInfoResponse.Claims.Any(k => k.Type.Contains("given_name")))
            {
                firstName = userInfoResponse.Claims.FirstOrDefault(k => k.Type.Contains("given_name")).Value;
            }

            ////The claim "family_name" is what was getting returned from the info
            if (userInfoResponse.Claims.Any(k => k.Type.Contains("family_name")))
            {
                lastName = userInfoResponse.Claims.FirstOrDefault(k => k.Type.Contains("family_name")).Value;
            }

            ////Add a custom claim, which is then transformed to the Sitecore FullName field.
            notification.AuthenticationTicket.Identity.AddClaim(new Claim("ExternalFullName", firstName + " " + lastName));

            ////Add another custom claim for comments:
            notification.AuthenticationTicket.Identity.AddClaim(new Claim("xComment", "Insite User"));

            notification.AuthenticationTicket.Identity.AddClaim(new Claim("insite_role", @"CommerceUsers\\Extranet User"));
        }

        private Task RedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {
            var owinContext = notification.OwinContext;

            OpenIdConnectMessage protocolMessage = notification.ProtocolMessage;

            if (protocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
            {
                protocolMessage.AcrValues = this.GetAcrValues(owinContext);
                protocolMessage.RedirectUri = this.BuildRedirectUri(owinContext, "signin");
                protocolMessage.Prompt = this.GetSuitablePrompt(owinContext, "login");
            }
            else if (protocolMessage.RequestType == OpenIdConnectRequestType.Logout && this.GetIdentityProvider().TriggerExternalSignOut)
            {
                protocolMessage.IdTokenHint = this.GetIdTokenHint(owinContext);
                protocolMessage.PostLogoutRedirectUri = this.BuildPostLogoutRedirectUri(owinContext, string.Empty);
            }

            return Task.CompletedTask;
        }
    }

An InsiteIdentityProvider class was implemented:


    public class InsiteIdentityProvider : DefaultIdentityProvider
    {
        public InsiteIdentityProvider(BaseDomainManager domainManager, BaseCorePipelineManager corePipelineManager) : base("InsiteIdentity", domainManager)
        {
            Assert.ArgumentNotNull((object)corePipelineManager, nameof(corePipelineManager));
            this.CorePipelineManager = corePipelineManager;
        }

        protected BaseCorePipelineManager CorePipelineManager { get; }
    }

Foundation.Account.config file

The following configurations were added to Foundation.Account.config file.

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
    <sitecore>
        <sc.variable name="insiteIdentityServerAuthority" value="https://xxxinsite.local.com" />
        <settings>
            <setting name="InsiteIdentity.ClientId" value="xxx" />
            <setting name="InsiteIdentity.ClientSecret" value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
            <setting name="InsiteIdentity.OAuthRedirectUri" value="/identity/externallogincallback" />
            <setting name="InsiteIdentity.Authority" value="$(insiteIdentityServerAuthority)/identity" />
            <setting name="InsiteIdentity.CallbackAuthority" value="/identity/externallogincallback" />
        </settings>
        <domainManager>
            <domains>
                <domain id="modules" type="Sitecore.Security.Domains.Domain, Sitecore.Kernel">
                    <param desc="name">$(id)</param>
                    <ensureAnonymousUser>false</ensureAnonymousUser>
                </domain>
            </domains>
        </domainManager>
        <pipelines>
            <accounts.loggedIn />
            <accounts.loggedOut />
            <accounts.registered />
            <owin.initialize>
                <processor type="Sitecore.Owin.Authentication.IdentityServer.Pipelines.Initialize.JwtBearerAuthentication, Sitecore.Owin.Authentication.IdentityServer"
                           patch:before="processor[@method='Authenticate']" resolve="true" >
                    <identityProviderName>InsiteIdentity</identityProviderName>
                    <audiences hint="raw:AddAudience">
                        <audience value="$(insiteIdentityServerAuthority)/resources"/>
                    </audiences>
                    <issuers hint="list">
                        <issuer>$(insiteIdentityServerAuthority)</issuer>
                    </issuers>
                </processor>
                <processor patch:before="*[@type='Sitecore.Owin.Authentication.Pipelines.Initialize.HandlePostLogoutUrl, Sitecore.Owin.Authentication']" mode="on" type="Xc.Foundation.Account.Infrastructure.Pipelines.Initialize.HandlePostLogoutUrl, Xc.Foundation.Account" resolve="true"/>
            </owin.initialize>
            <owin.identityProviders>
                <processor id="InsiteIdentity" type="Xc.Foundation.Account.Infrastructure.Pipelines.IdentityProviders.InsiteIdentity, Xc.Foundation.Account" resolve="true">
                    <scopes hint="list">
                        <scope name="openid">openid</scope>
                        <scope name="profile">profile</scope>
                        <scope name="iscapi">iscapi</scope>
                    </scopes>
                </processor>
            </owin.identityProviders>
        </pipelines>

        <federatedAuthentication type="Sitecore.Owin.Authentication.Configuration.FederatedAuthenticationConfiguration, Sitecore.Owin.Authentication">
            <!--Provider mappings to sites-->
            <identityProvidersPerSites hint="list:AddIdentityProvidersPerSites">
            </identityProvidersPerSites>

            <!--Definitions of providers-->
            <identityProviders hint="list:AddIdentityProvider">

                <identityProvider id="InsiteIdentity" type="Xc.Foundation.Account.Infrastructure.Providers.InsiteIdentityProvider, Xc.Foundation.Account" resolve="true">
                    <caption>Sign In</caption>
                    <domain>CommerceUsers</domain>
                    <enabled>true</enabled>
                    <triggerExternalSignOut>true</triggerExternalSignOut>

                    <transformations hint="list:AddTransformation">
                        <transformation name="apply additional claims" type="Sitecore.Owin.Authentication.IdentityServer.Transformations.ApplyAdditionalClaims, Sitecore.Owin.Authentication.IdentityServer" resolve="true"/>
                        <transformation name="name to long name" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
                            <sources hint="raw:AddSource">
                                <claim name="preferred_username"/>
                            </sources>
                            <targets hint="raw:AddTarget">
                                <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"/>
                            </targets>
                            <keepSource>true</keepSource>
                        </transformation>
                        <transformation name="role to long role" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
                            <sources hint="raw:AddSource">
                                <claim name="insite_role"/>
                            </sources>
                            <targets hint="raw:AddTarget">
                                <claim name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" value="CommerceUsers\Extranet User"/>
                            </targets>
                            <keepSource>false</keepSource>
                        </transformation>
                        <transformation name="set ShadowUser" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
                            <sources hint="raw:AddSource">
                                <claim name="http://schemas.microsoft.com/identity/claims/identityprovider" value="local"/>
                            </sources>
                            <targets hint="raw:AddTarget">
                                <claim name="http://www.sitecore.net/identity/claims/shadowuser" value="true"/>
                            </targets>
                            <keepSource>true</keepSource>
                        </transformation>
                        <!--
                            owin.cookieAuthentication.signIn pipeline uses http://www.sitecore.net/identity/claims/cookieExp claim to override authentication cookie expiration.
                                         'exp' claim value can be configured on Sitecore Identity server on the client configuration by IdentityTokenLifetimeInSeconds setting.
                                         Note: Claim value is Unix time expressed as the number of seconds that have elapsed since 1970-01-01T00:00:00Z 
                        -->
                        <transformation name="use exp claim for authentication cookie expiration" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
                            <sources hint="raw:AddSource">
                                <claim name="exp"/>
                            </sources>
                            <targets hint="raw:AddTarget">
                                <claim name="http://www.sitecore.net/identity/claims/cookieExp"/>
                            </targets>
                            <keepSource>true</keepSource>
                        </transformation>
                        <!--<transformation name="remove local role claims" type="Sitecore.Owin.Authentication.IdentityServer.Transformations.RemoveLocalRoles, Sitecore.Owin.Authentication.IdentityServer"/>-->
                        <transformation name="adjust NameIdentifier claim" type="Sitecore.Owin.Authentication.IdentityServer.Transformations.AdjustNameIdentifierClaim, Sitecore.Owin.Authentication.IdentityServer" resolve="true"/>
                    </transformations>
                </identityProvider>
            </identityProviders>

            <!--List of all shared transformations-->
            <sharedTransformations>
            </sharedTransformations>

            <!--Property mappings initializer-->
            <propertyInitializer type="Sitecore.Owin.Authentication.Services.PropertyInitializer, Sitecore.Owin.Authentication">
                <!--List of property mappings
                Note that all mappings from the list will be applied to each providers-->
                <maps hint="list">
                    <!--The mapping sets the Email property of the user profile from emailaddress claim-->
                    <map name="email claim" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true">
                        <data hint="raw:AddData">
                            <!--claim name-->
                            <source name="preferred_username" />
                            <!--property name-->
                            <target name="Email" />
                        </data>
                    </map>
                    <map name="first_name" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true">
                        <data hint="raw:AddData">
                            <source name="given_name" />
                            <target name="FirstName" />
                        </data>
                    </map>
                    <map name="full name" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true">
                        <data hint="raw:AddData">
                            <source name="ExternalFullName" />
                            <target name="FullName" />
                        </data>
                    </map>
                    <map name="comment" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true">
                        <data hint="raw:AddData">
                            <source name="xComment" />
                            <target name="Comment" />
                        </data>
                    </map>
                    <map name="role" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true">
                        <data hint="raw:AddData">
                            <source name="insite_role" />
                            <target name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" />
                        </data>
                    </map>
                </maps>
            </propertyInitializer>
        </federatedAuthentication>
    </sitecore>
</configuration>

Project.XXX.Website.config

In the Project layer, I added the following configurations:

        <federatedAuthentication  role:require="ContentDelivery or Standalone">
            <identityProvidersPerSites>
                <mapEntry name="atccorg" type="Sitecore.Owin.Authentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.Owin.Authentication">
                    <sites hint="list">
                        <site>xxx</site>
                    </sites>
                    <identityProviders hint="list:AddIdentityProvider">
                        <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='InsiteIdentity']"  id="InsiteIdentity"  />
                    </identityProviders>
                    <externalUserBuilder type="Xc.Foundation.Account.Services.InsiteUserBuilder, Xc.Foundation.Account">
                        <param desc="isPersistentUser">true</param> 
                    </externalUserBuilder>
                </mapEntry>
            </identityProvidersPerSites>
        </federatedAuthentication>

InsiteUserBuilder.cs

User builder class had to be overwritten as well:

    public class InsiteUserBuilder : Sitecore.Owin.Authentication.Services.DefaultExternalUserBuilder
    {
        public InsiteUserBuilder()
        {
        }

        public InsiteUserBuilder(bool isPersistentUser) : base(isPersistentUser)
        {
        }

        public InsiteUserBuilder(string isPersistentUser) : base(isPersistentUser)
        {
        }

        public override ApplicationUser BuildUser(UserManager<ApplicationUser> userManager, ExternalLoginInfo externalLoginInfo)
        {
            ApplicationUser user = this.ApplicationUserFactory.CreateUser(this.CreateUniqueUserName(userManager, externalLoginInfo));
            user.IsVirtual = !this.IsPersistentUser;
            return user;
        }

        protected override string CreateUniqueUserName(UserManager<ApplicationUser> userManager, ExternalLoginInfo externalLoginInfo)
        {
            IdentityProvider identityProvider = this.FederatedAuthenticationConfiguration.GetIdentityProvider(externalLoginInfo.ExternalIdentity);
            if (identityProvider == null)
            {
                throw new InvalidOperationException("Unable to retrieve an identity provider for the given identity");
            }

            return identityProvider.Domain + "\\" + externalLoginInfo.ExternalIdentity.Name;
        }
    }

HandlePostLogoutUrl.cs

    public class HandlePostLogoutUrl : InitializeProcessor
    {
        private readonly BaseSettings settings;

        private readonly ExternalLogoutManager externalLogoutManager;

        public HandlePostLogoutUrl(BaseSettings settings, ExternalLogoutManager externalLogoutManager)
        {
            Assert.ArgumentNotNull((object)settings, nameof(settings));
            Assert.ArgumentNotNull((object)externalLogoutManager, nameof(externalLogoutManager));
            this.settings = settings;
            this.externalLogoutManager = externalLogoutManager;
        }

        public override void Process(InitializeArgs args)
        {
            if (!this.settings.FederatedAuthenticationEnabled())
            {
                return;
            }

            this.HandlePostExternalLogoutUrl(args);
        }

        private void HandlePostExternalLogoutUrl(InitializeArgs args)
        {
            args.App.Map(
                this.settings.IdentityProcessingPathPrefix().EnsureTrailingSlash() + InsiteIdentity.PostLogoutEndPointName,
                (Action<IAppBuilder>)(app => this.RunUnRegisterExternalLogout(app)));
        }

        private void RunUnRegisterExternalLogout(IAppBuilder app)
        {
            app.Run((Func<IOwinContext, Task>)(context => { this.UnRegisterExternalLogout(context); return Task.CompletedTask; }));
        }

        private void UnRegisterExternalLogout(IOwinContext owinContext)
        {
            Assert.ArgumentNotNull((object)owinContext, nameof(owinContext));
            owinContext.Response.Cookies.Delete(
                Sitecore.Owin.Authentication.Constants.ExternalLogOutCookieName, 
                new CookieOptions()
            {
                HttpOnly = true,
                Path = "/"
            });
            string str = owinContext.Request.Query["ReturnUrl"];
            if (string.IsNullOrEmpty(str) || WebUtil.IsExternalUrl(str, owinContext.Request.Uri.Host))
            {
                owinContext.Response.Redirect("/");
            }

            owinContext.Response.Redirect(str);
        }
    }

Site Definition

In the site definition the following attribute was added:

loginPage="$(loginPath)[xxxsitename]/InsiteIdentity"

Login Button

Controller Rendering:

 public ActionResult SignIn()
        {
            var url = !string.IsNullOrEmpty(Request.QueryString[Constants.QueryString.Url])
                        ? HttpUtility.UrlDecode(Request.QueryString[Constants.QueryString.Url])
                        : Request.RawUrl;

            var model = new LoginInfo
            {
                ReturnUrl = !string.IsNullOrEmpty(Request.QueryString[Constants.QueryString.ReturnUrl])
                    ? HttpUtility.UrlDecode(Request.QueryString[Constants.QueryString.ReturnUrl])
                    : url,
                LoginButton = this.fedAuthLoginRepository.GetAll().FirstOrDefault()
            };

            return this.View(model);
        }

View:

In the view responsible for rendering of the login button, the following code was added:

        if (Model.LoginButton != null)
        {
            <form action="@Model.LoginButton.Href" method="post" class="form-signin">
                <button class="btn btn-block btn-secondary" type="submit">
                    @if (!string.IsNullOrWhiteSpace(Model.LoginButton.IconClass))
                    {
                        <i class="@Model.LoginButton.IconClass"></i>
                    }
                    <span>
                        @Model.LoginButton.Caption
                    </span>
                </button>
            </form>
        }

LogOut

Controller action:

       [HttpGet]
        public ActionResult SignOut()
        {
            this.accountRepository.Logout();
            return this.Redirect("/");
        }

In AccountRepository we have the following:

        public bool LogOut()
        {          
            AuthenticationManager.Logout();
            return true;
        }

Above code and configurations force the user to be redirected to Insite Identity Server for authentication and log out.

Special Thanks to the following blogs: