6

I am on Sitecore 9.1 Initial Release and SXA 1.8. Implemented a custom processor for Azure ADB2C Login. I can login perfectly and fetch the claims and stuff. But on redirect to /myaccount page , I need to check if Sitecore.Context.User.IsAuthenticated , but this is set to false. I added a B2CHelper to Create and Login Virtual User , but the Sitecore.Context.User.IsAuthenticated is always false.

1- How do I set the Context on redirect from IDP, so that I can get the details of logged in User.

2- Is the below required in Sitecore 9.1 Sitecore.Security.Authentication.AuthenticationManager.BuildVirtualUser and Sitecore.Security.Authentication.AuthenticationManager.LoginVirtualUser(virtualUser);

Have raised a Support ticket as well. Below code for Custom Processor

public class AzureAdB2CIdentityProviderProcessor : IdentityProvidersProcessor
    {
        // App config settings
        public static string ClientId = Sitecore.Configuration.Settings.GetSetting("ida:ClientId");
        public static string ClientSecret = Sitecore.Configuration.Settings.GetSetting("ida:ClientSecret");
        public static string AadInstance = Sitecore.Configuration.Settings.GetSetting("ida:AadInstance");
        public static string Tenant = Sitecore.Configuration.Settings.GetSetting("ida:Tenant");
        public static string RedirectUri = Sitecore.Configuration.Settings.GetSetting("ida:RedirectUri");

        // B2C policy identifiers
        public static string SignUpPolicyId = Sitecore.Configuration.Settings.GetSetting("ida:SignUpPolicyId");
        public static string SignInPolicyId = Sitecore.Configuration.Settings.GetSetting("ida:SignInPolicyId");
        public static string SignUpSignInPolicyId = Sitecore.Configuration.Settings.GetSetting("ida:SignUpSignInPolicyId");
        public static string EditProfilePolicyId = Sitecore.Configuration.Settings.GetSetting("ida:EditProfilePolicyId");
        public static string ResetPasswordPolicyId = Sitecore.Configuration.Settings.GetSetting("ida:ResetPasswordPolicyId");
        public static bool RequireHttps = bool.Parse(Sitecore.Configuration.Settings.GetSetting("RequireHttps"));

        public static string DefaultPolicy = SignInPolicyId;

        // API Scopes
        public static string ApiIdentifier = Sitecore.Configuration.Settings.GetSetting("api:ApiIdentifier");
        public static string ReadTasksScope = ApiIdentifier + Sitecore.Configuration.Settings.GetSetting("api:ReadScope");
        public static string WriteTasksScope = ApiIdentifier + Sitecore.Configuration.Settings.GetSetting("api:WriteScope");
        public static string[] Scopes = new string[] { ReadTasksScope, WriteTasksScope };

        // OWIN auth middleware constants
        public const string ObjectIdElement = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";

        // Authorities
        public static string Authority = String.Format(AadInstance, Tenant, DefaultPolicy);
        public static string WellKnownMetadata = $"{AadInstance}/v2.0/.well-known/openid-configuration";

        private static string[] redirectIgnoreUrls = new string[] { };

        public static string _defaultScope = String.Concat(ApiIdentifier, "/", Sitecore.Configuration.Settings.GetSetting("api:DefaultScope"));
        private static string[] _scopes = new string[] { _defaultScope }; //, ReadTasksScope, WriteTasksScope };

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

        }
        protected override string IdentityProviderName
        {
            get { return "AzureAdB2C"; }
        }

        protected override void ProcessCore(IdentityProvidersArgs args)
        {
            // Required for Azure webapps, as by default they force TLS 1.2 and this project attempts 1.0
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            Assert.ArgumentNotNull(args, nameof(args));
            var identityProvider = this.GetIdentityProvider();
            var authenticationType = this.GetAuthenticationType();

            args.App.UseCookieAuthentication(new CookieAuthenticationOptions());
            args.App.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(EditProfilePolicyId));
            args.App.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(ResetPasswordPolicyId));
            args.App.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignUpPolicyId));
            args.App.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignInPolicyId));

        }

        private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy)
        {
            var identityProvider = GetIdentityProvider();
            return new OpenIdConnectAuthenticationOptions
            {
                // For each policy, give OWIN the policy-specific metadata address, and
                // set the authentication type to the id of the policy
                MetadataAddress = String.Format(WellKnownMetadata, Tenant, policy),
                //AuthenticationType = GetAuthenticationType(),
                AuthenticationType = policy,
                AuthenticationMode = AuthenticationMode.Passive,
                RequireHttpsMetadata = RequireHttps,

                // These are standard OpenID Connect parameters, with values pulled from settings
                ClientId = ClientId,
                // Scope = "openid profile offline_access",
                Scope = $"openid profile offline_access { _defaultScope }",
                //ResponseType = "id_token",
                RedirectUri = RedirectUri, //SK RedirectIframeUri
                PostLogoutRedirectUri = RedirectUri,

                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    RedirectToIdentityProvider = OnRedirectToIdentityProvider,                    
                    AuthenticationFailed = OnAuthenticationFailed,
                    SecurityTokenValidated = OnSecurityTokenValidated,                   
                    AuthorizationCodeReceived = OnAuthorizationCodeReceived,
                },
                // This piece is optional - it is used for displaying the user's name in the navigation bar.
                TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",                    
                    ValidateIssuer = false
                },
            };
        }

        private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {
            var policy = notification.OwinContext.Get<string>("Policy");

            if (!string.IsNullOrEmpty(policy) && !policy.Equals(DefaultPolicy))
            {
                notification.ProtocolMessage.Scope = OpenIdConnectScope.OpenId;
                notification.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
                notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(DefaultPolicy.ToLower(), policy.ToLower());
                notification.ProtocolMessage.UiLocales = notification.ProtocolMessage.UiLocales ?? string.Empty;
            }
            return Task.FromResult(0);
        }

        private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
        {
            context.HandleResponse();            
            if (context.ProtocolMessage.ErrorDescription != null && context.ProtocolMessage.ErrorDescription.Contains("AADB2C90118"))
            {
                // If the user clicked the reset password link, redirect to the reset password route
                context.Response.Redirect("/Account/ResetPassword");
            }
            else if (context.Exception.Message == "access_denied")
            {
                // If the user canceled the sign in, redirect back to the home page
                context.Response.Redirect("/");
            }
            else if (context.Exception is HttpException httpException)
            {
                LogExceptionError("Startup", httpException);
                if (httpException?.WebEventCode == 404)
                {
                    context.Response.Redirect("/SplashPage");
                }
                context.Response.Redirect("/Error?ErrorMessage=" + httpException?.Message);
            }
            else
            {
                string errorMessage = (!String.IsNullOrEmpty(context.ProtocolMessage.Error)) ? context.ProtocolMessage.ErrorDescription : context.Exception.Message;
                LogExceptionError("Startup", new Exception(errorMessage, context.Exception));
                context.Response.Redirect("/Error?ErrorMessage=" + errorMessage);
            }
            return Task.FromResult(0);
        }

        private Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {
            //Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
            notification.AuthenticationTicket.Identity.AddClaim(new System.Security.Claims.Claim("idp", "AzureAdB2C"));

            // transform all claims
            ClaimsIdentity identity = notification.AuthenticationTicket.Identity;
            notification.AuthenticationTicket.Identity.ApplyClaimsTransformations(new TransformationContext(FederatedAuthenticationConfiguration, GetIdentityProvider()));

            return Task.FromResult(0);
        }
        private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
        {
            try
            {

                //Login User Virtually
                //We need to get the token and other post data that B2C sent us
                var idToken = HttpContext.Current.Request.Form["id_token"];
                var idstate = HttpContext.Current.Request.Form["state"];

                Dictionary<string, string> tokenClaims = new Dictionary<string, string>();
                foreach (var c in notification.AuthenticationTicket.Identity.Claims)
                {
                    tokenClaims.Add(c.Type, c.Value);
                }

                //bool loggedin = this.CreateAndLoginUser(tokenClaims["given_name"], tokenClaims["given_name"] + " " +tokenClaims["family_name"], tokenClaims);
                bool loggedin = B2CHelper.CreateAndLoginVirtualUser(tokenClaims["name"], tokenClaims["name"], tokenClaims, out Sitecore.Security.Accounts.User currentUser);                

            }
            catch (Exception ex)
            {
                LogExceptionError("AzureAbB2cIdentityProviderProcesssor ", ex);
                throw ex;
            }
        }

        private void LogExceptionError(string methodName, Exception ex)
        {
            string errorMessage = $"Caught internal exception: /n Calling method: {methodName} /n Exception Message: {ex.Message} /n Exception inner details: {ex.InnerException} /n Exception stacktrace: {ex.StackTrace}";
            Trace.TraceError(errorMessage);
        }
    }

//Patch config

<pipelines>
      <owin.identityProviders>
        <!--This is the custom processor that gets executed when Azure AD posts the token to Sitecore-->
        <processor type="Project.Feature.LoginForm.Pipelines.AzureAdB2CIdentityProviderProcessor, Project.Feature.LoginForm" resolve="true" />
      </owin.identityProviders>
    </pipelines>
    <services>
      <!--<configurator type= "Feature.AzureAdB2C.Models.AuthenticationConfigurator, Feature.AzureAdB2C"/>-->

      <register serviceType="Sitecore.Abstractions.BaseAuthenticationManager, Sitecore.Kernel"
                implementationType="Sitecore.Owin.Authentication.Security.AuthenticationManager, Sitecore.Owin.Authentication"
                lifetime="Singleton" />
      <register serviceType="Sitecore.Abstractions.BaseTicketManager, Sitecore.Kernel"
                implementationType="Sitecore.Owin.Authentication.Security.TicketManager, Sitecore.Owin.Authentication"
                lifetime="Singleton" />
      <register serviceType="Sitecore.Abstractions.BasePreviewManager, Sitecore.Kernel"
                implementationType="Sitecore.Owin.Authentication.Publishing.PreviewManager, Sitecore.Owin.Authentication"
                lifetime="Singleton" />
    </services>
    <federatedAuthentication type="Sitecore.Owin.Authentication.Configuration.FederatedAuthenticationConfiguration, Sitecore.Owin.Authentication">
      <!--Provider mappings to sites-->
      <!--<identityProvidersPerSites hint="list:AddIdentityProvidersPerSites">
      </identityProvidersPerSites>-->
      <identityProvidersPerSites hint="list:AddIdentityProvidersPerSites">
        <!--To add a providers per sites configuration you can use the custom config file in Include folder - just create new one or use existed. 
            Under configuration/sitecore/federatedAuthentication/identityProvidersPerSites create a new node with name mapEntry. Fill name, type attributes. 
            The value of the name attribute should be unique for each entry.
            Under created node fill child nodes: sites (list of sites where the provider(s) will work), identityProviders (list of providers), externalUserBuilder. 
            An example of map entry where the AzureAd provider could be used with shell, admin and website sites:-->

        <mapEntry name="0" type="Sitecore.Owin.Authentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.Owin.Authentication">
          <sites hint="list">
            <site>website</site>
          </sites>
          <identityProviders hint="list:AddIdentityProvider">
            <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='AzureAdB2C']" />
          </identityProviders>
          <externalUserBuilder type="Sitecore.Owin.Authentication.Services.DefaultExternalUserBuilder, Sitecore.Owin.Authentication">
            <param desc="isPersistentUser">true</param>
          </externalUserBuilder>
        </mapEntry>

        <!--See below how to add a provider.-->
      </identityProvidersPerSites>
      <!--Definitions of providers-->
      <identityProviders hint="list:AddIdentityProvider">
        <identityProvider id="AzureAdB2C" type="Sitecore.Owin.Authentication.Configuration.DefaultIdentityProvider, Sitecore.Owin.Authentication">
          <param desc="name">$(id)</param>
          <param desc="domainManager" type="Sitecore.Abstractions.BaseDomainManager" resolve="true" />
          <caption>AzureAdB2C</caption>
          <domain>AzureAdB2C</domain>
          <transformations hint="list:AddTransformation">
            <transformation name="Name Identifier Claim" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
              <sources hint="raw:AddSource">
                <claim name="http://schemas.microsoft.com/identity/claims/objectidentifier" />
              </sources>
              <targets hint="raw:AddTarget">
                <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" />
              </targets>
              <keepSource>true</keepSource>
            </transformation>
          </transformations>
        </identityProvider>
      </identityProviders>
      <!--List of all shared transformations-->
      <sharedTransformations>
      </sharedTransformations>
      <!--Property mappings initializer-->
      <propertyInitializer type="Sitecore.Owin.Authentication.Services.PropertyInitializer, Sitecore.Owin.Authentication">
      </propertyInitializer>
    </federatedAuthentication>
  • Have you managed to figure this out, or did Sitecore replied on this? I have the exact same challenge with Sitecore 9.1 :( [Treat it as a comment as i don't have enough reputation to add one] – damary Nov 15 '19 at 12:07
  • Hi, i manage this by creating a virtual user and loging io in. And used incognito mode – Abhay Dhar Nov 18 '19 at 5:25
  • Can you please add answer with your resolution @AbhayDhar with code samples? This will definitely help others. Thanks – Peter Procházka Nov 27 '19 at 19:31
2

Updated AdB2CProcessor File

public class AzureAdB2CIdentityProviderProcessor : IdentityProvidersProcessor
    {
        // App config settings
        public static string ClientId = Sitecore.Configuration.Settings.GetSetting("ida:ClientId");
        public static string ClientSecret = Sitecore.Configuration.Settings.GetSetting("ida:ClientSecret");
        public static string AadInstance = Sitecore.Configuration.Settings.GetSetting("ida:AadInstance");
        public static string Tenant = Sitecore.Configuration.Settings.GetSetting("ida:Tenant");
        public static string RedirectUri = Sitecore.Configuration.Settings.GetSetting("ida:RedirectUri");
        public static string LogOutUri = Sitecore.Configuration.Settings.GetSetting("ida:LogoutUrl");

        // B2C policy identifiers
        public static string SignUpPolicyId = Sitecore.Configuration.Settings.GetSetting("ida:SignUpPolicyId");
        public static string SignInPolicyId = Sitecore.Configuration.Settings.GetSetting("ida:SignInPolicyId");
        public static string SignUpSignInPolicyId = Sitecore.Configuration.Settings.GetSetting("ida:SignUpSignInPolicyId");
        public static string EditProfilePolicyId = Sitecore.Configuration.Settings.GetSetting("ida:EditProfilePolicyId");
        public static string PasswordResetPolicyId = Sitecore.Configuration.Settings.GetSetting("ida:PasswordResetPolicyId");
        public static string PasswordChangePolicyId = Sitecore.Configuration.Settings.GetSetting("ida:PasswordChangePolicyId");
        public static bool RequireHttps = bool.Parse(Sitecore.Configuration.Settings.GetSetting("RequireHttps"));

        public static string DefaultPolicy = SignUpSignInPolicyId;

        // API Scopes
        public static string ApiIdentifier = Sitecore.Configuration.Settings.GetSetting("api:ApiIdentifier");
        public static string ReadTasksScope = ApiIdentifier + Sitecore.Configuration.Settings.GetSetting("api:ReadScope");
        public static string WriteTasksScope = ApiIdentifier + Sitecore.Configuration.Settings.GetSetting("api:WriteScope");
       // public static string[] Scopes = new string[] { ReadTasksScope, WriteTasksScope };

        // OWIN auth middleware constants
        public const string ObjectIdElement = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";

        // Authorities
        public static string Authority = String.Format(AadInstance, Tenant, DefaultPolicy);
        public static string WellKnownMetadata = $"{AadInstance}/v2.0/.well-known/openid-configuration";

        private static string[] redirectIgnoreUrls = new string[] { };

        public static string _defaultScope = String.Concat(ApiIdentifier, "/", Sitecore.Configuration.Settings.GetSetting("api:DefaultScope"));
        private static string[] _scopes = new string[] { _defaultScope }; //, ReadTasksScope, WriteTasksScope };

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

        }
        protected override string IdentityProviderName
        {
            get { return "AzureAdB2C"; }
        }

        protected override void ProcessCore(IdentityProvidersArgs args)
        {
            // Required for Azure webapps, as by default they force TLS 1.2 and this project attempts 1.0
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            Assert.ArgumentNotNull(args, nameof(args));

            args.App.UseCookieAuthentication(new CookieAuthenticationOptions());
            args.App.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignUpPolicyId));
            args.App.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignInPolicyId));
            args.App.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(EditProfilePolicyId));
            args.App.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(PasswordResetPolicyId));
            args.App.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(PasswordChangePolicyId));
        }

        private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy)
        {
            var identityProvider = GetIdentityProvider();
            return new OpenIdConnectAuthenticationOptions
            {
                // For each policy, give OWIN the policy-specific metadata address, and
                // set the authentication type to the id of the policy
                MetadataAddress = String.Format(WellKnownMetadata, Tenant, policy),
                //AuthenticationType = GetAuthenticationType(),
                AuthenticationType = policy,
                //AuthenticationMode = AuthenticationMode.Passive,
                RequireHttpsMetadata = RequireHttps,

                // These are standard OpenID Connect parameters, with values pulled from settings
                ClientId = ClientId,
                Scope = $"openid profile offline_access { _defaultScope }",
                //ResponseType = "id_token",
                RedirectUri = RedirectUri, //SK RedirectIframeUri
                PostLogoutRedirectUri = RedirectUri,                

                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    RedirectToIdentityProvider = OnRedirectToIdentityProvider,
                    AuthorizationCodeReceived = OnAuthorizationCodeReceived,
                    AuthenticationFailed = OnAuthenticationFailed,
                    SecurityTokenValidated = OnSecurityTokenValidated,
                },
                // This piece is optional - it is used for displaying the user's name in the navigation bar.
                TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",                    
                    ValidateIssuer = false,
                    //SaveSigninToken = identityProvider.TriggerExternalSignOut
                },
            };
        }

        private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {
            var policy = notification.OwinContext.Get<string>("Policy");

            if (!string.IsNullOrEmpty(policy) && !policy.Equals(DefaultPolicy))
            {
                notification.ProtocolMessage.Scope = OpenIdConnectScope.OpenId;
                notification.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
                notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(DefaultPolicy.ToLower(), policy.ToLower());
                notification.ProtocolMessage.UiLocales = notification.ProtocolMessage.UiLocales ?? string.Empty;
            }
            //if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout && this.GetIdentityProvider().TriggerExternalSignOut)
            //{
            //    notification.ProtocolMessage.IdTokenHint = this.GetIdTokenHint(notification.OwinContext);
            //    //notification.ProtocolMessage.Nonce = this.get(notification.OwinContext);
            //    notification.ProtocolMessage.PostLogoutRedirectUri = "http://www.google.com";
            //}
            return Task.FromResult(0);
        }

        private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
        {
            context.HandleResponse();            
            if (context.ProtocolMessage.ErrorDescription != null && context.ProtocolMessage.ErrorDescription.Contains("AADB2C90118"))
            {
                // If the user clicked the reset password link, redirect to the reset password route
                context.Response.Redirect("/Account/ResetPassword");
            }
            else if (context.Exception.Message == "access_denied")
            {
                // If the user canceled the sign in, redirect back to the home page
                context.Response.Redirect("/");
            }
            else if (context.Exception is HttpException httpException)
            {
                LogExceptionError("Startup", httpException);
                if (httpException?.WebEventCode == 404)
                {
                    context.Response.Redirect("/SplashPage");
                }
                context.Response.Redirect("/Error?ErrorMessage=" + httpException?.Message);
            }
            else
            {
                string errorMessage = (!String.IsNullOrEmpty(context.ProtocolMessage.Error)) ? context.ProtocolMessage.ErrorDescription : context.Exception.Message;
                LogExceptionError("Startup", new Exception(errorMessage, context.Exception));
                context.Response.Redirect("/Error?ErrorMessage=" + errorMessage);
            }
            return Task.FromResult(0);
        }

        private Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {

            notification.AuthenticationTicket.Identity.AddClaim(new System.Security.Claims.Claim("idp", "AzureAdB2C"));

            var claimsIdentity = ClaimsPrincipal.Current.Claims;
            // transform all claims
            ClaimsIdentity identity = notification.AuthenticationTicket.Identity;
            notification.AuthenticationTicket.Identity.ApplyClaimsTransformations(new TransformationContext(FederatedAuthenticationConfiguration, GetIdentityProvider()));


            return Task.FromResult(0);
        }

        /*
         * Callback function when an authorization code is received 
         */
        private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
        {
            try
            {               
                IConfidentialClientApplication confidentialClient = MsalAppBuilder.BuildConfidentialClientApplication(new ClaimsPrincipal(notification.AuthenticationTicket.Identity));
                // Upon successful sign in, get & cache a token using MSAL
                AuthenticationResult result = await confidentialClient.AcquireTokenByAuthorizationCode(Globals.Scopes, notification.Code).ExecuteAsync();

                //Create a Virtual User and Login User Virtually
                B2CHelper.CreateAndLoginVirtualUser(tokenClaims["EmailId"], tokenClaims["name"], tokenClaims, out Sitecore.Security.Accounts.User currentUser);

            }
            catch (Exception ex)
            {
                LogExceptionError("AzureAbB2cIdentityProviderProcesssor ", ex);
                throw ex;
            }
        }    

        private void LogExceptionError(string methodName, Exception ex)
        {
            string errorMessage = $"Caught internal exception: /n Calling method: {methodName} /n Exception Message: {ex.Message} /n Exception inner details: {ex.InnerException} /n Exception stacktrace: {ex.StackTrace}";
            Trace.TraceError(errorMessage);
        }

    }

B2CHelper.cs

public class B2CHelper
    {
        private static ICustomLogger _customLogger = new CustomLogger();
        private static readonly ILogRepository _logRepository;
        public static void CreateAndLoginVirtualUser(string userName, string displayName, Dictionary<string, string> properties, out Sitecore.Security.Accounts.User user)
        {
            bool _res = false;
            user = null;
            try
            {   
                //Create virtual User
                User virtualUser = Sitecore.Security.Authentication.AuthenticationManager.BuildVirtualUser(userName, false);

                virtualUser.RuntimeSettings.Load();
                virtualUser.RuntimeSettings.AddedRoles.Clear();
                virtualUser.Roles.RemoveAll();

                // Optional: Add the user to an existing extranet role
                virtualUser.Roles.Add(Role.FromName("AzureAdB2C\\"));

                // set profile properties
                virtualUser.Profile.Email = userName;
                virtualUser.Profile.Name = displayName;
                virtualUser.Profile.FullName = displayName;
                virtualUser.Profile.Comment = "User Created from Azure AD B2C";
                foreach (var prop in properties)
                {
                    // virtualUser.Profile.SetCustomProperty(prop.Key, prop.Value);
                }
                virtualUser.Profile.Save();
                virtualUser.RuntimeSettings.IsVirtual = true;

                virtualUser.RuntimeSettings.Save();
                user = virtualUser;
                _res = true;
                virtualUser.Profile.Reload();

                // Login the virtual user
                Sitecore.Security.Authentication.AuthenticationManager.LoginVirtualUser(virtualUser);               
            }
            catch (Exception ex)
            {
                _customLogger.LogMessage(NESConstants.NesCustomLogFileAppender, ex.StackTrace + ex.Message, Logtype.ERROR);
                _logRepository.LogFormattedError(ex.StackTrace);
                _res = false;
            }            
        }       
}

Controller

public class LoginFormController : Controller
    {
        public ActionResult Index()
        {
            return PartialView("LoginForm");
        }

        [Route("LoginForm/SignIn")]
        public void SignIn()
        {
            if (!Request.IsAuthenticated)
            {
                HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties() { RedirectUri = "/myaccount" },
                    new string[] { "AzureAdB2C", Startup.SignInPolicyId });
            }
        }

        [Route("LoginForm/SignOut")]
        public void SignOut()
        {
            // To sign out the user, you should issue an OpenIDConnect sign out request.
            if (Sitecore.Context.User.IsAuthenticated)
            {
                //Session.Abandon();
                HttpContext.GetOwinContext().Authentication.SignOut(new AuthenticationProperties { }, new string[] { "AzureAdB2C", Startup.SignInPolicyId });
                AuthenticationManager.Logout();
            }
            //return Redirect("/");
        }

        [Route("LoginForm/Register")]
        public void Register()
        {
            if (!Request.IsAuthenticated)
            {
                HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties() { RedirectUri = "/" }, new string[] { "AzureAdB2C", Startup.SignUpPolicyId });
            }
        }
    }

Patch Config for AdB2CIdentityProviderProcessor

<pipelines>
      <owin.identityProviders>
        <!--This is the custom processor that gets executed when Azure AD posts the token to Sitecore-->
        <processor type="Client.Feature.LoginForm.Pipelines.AzureAdB2CIdentityProviderProcessor, Client.Feature.LoginForm" resolve="true" />
      </owin.identityProviders>
      <owin.initialize>
        <processor name="CookieAuthentication">
          <!-- Controls how much time the cookie will remain valid from the point it is created.
             Format: d:hh:mm:ss

             No matter the cookie is persistent or not, OWIN authentication will not pass the expired cookie.
             ExpireTimeSpan could be overwritten by the http://www.sitecore.net/identity/claims/cookieExp claim where
             the claim value is Unix time expressed as the number of seconds that have elapsed since 1970-01-01T00:00:00Z. -->
          <ExpireTimeSpan>00:15:00</ExpireTimeSpan>
          <SlidingExpiration>true</SlidingExpiration>
        </processor>
      </owin.initialize>      

    </pipelines>
    <services>
      <register serviceType="Sitecore.Abstractions.BaseAuthenticationManager, Sitecore.Kernel"
                implementationType="Sitecore.Owin.Authentication.Security.AuthenticationManager, Sitecore.Owin.Authentication"
                lifetime="Singleton" />
      <register serviceType="Sitecore.Abstractions.BaseTicketManager, Sitecore.Kernel"
                implementationType="Sitecore.Owin.Authentication.Security.TicketManager, Sitecore.Owin.Authentication"
                lifetime="Singleton" />
      <register serviceType="Sitecore.Abstractions.BasePreviewManager, Sitecore.Kernel"
                implementationType="Sitecore.Owin.Authentication.Publishing.PreviewManager, Sitecore.Owin.Authentication"
                lifetime="Singleton" />
    </services>
    <federatedAuthentication type="Sitecore.Owin.Authentication.Configuration.FederatedAuthenticationConfiguration, Sitecore.Owin.Authentication">
      <!--Provider mappings to sites-->
      <!--<identityProvidersPerSites hint="list:AddIdentityProvidersPerSites">
      </identityProvidersPerSites>-->
      <identityProvidersPerSites hint="list:AddIdentityProvidersPerSites">
        <!--To add a providers per sites configuration you can use the custom config file in Include folder - just create new one or use existed. 
            Under configuration/sitecore/federatedAuthentication/identityProvidersPerSites create a new node with name mapEntry. Fill name, type attributes. 
            The value of the name attribute should be unique for each entry.
            Under created node fill child nodes: sites (list of sites where the provider(s) will work), identityProviders (list of providers), externalUserBuilder. 
            An example of map entry where the AzureAd provider could be used with shell, admin and website sites:-->       
        <!--See below how to add a provider.-->
      </identityProvidersPerSites>
      <!--Definitions of providers-->
      <identityProviders hint="list:AddIdentityProvider">
        <identityProvider id="AzureAdB2C" type="Sitecore.Owin.Authentication.Configuration.DefaultIdentityProvider, Sitecore.Owin.Authentication">
          <param desc="name">$(id)</param>
          <param desc="domainManager" type="Sitecore.Abstractions.BaseDomainManager" resolve="true" />
          <caption>AzureAdB2C</caption>
          <domain>extranet</domain>
          <enabled>true</enabled>
          <triggerExternalSignOut>true</triggerExternalSignOut>
          <transformations hint="list:AddTransformation">
            <transformation name="Name Identifier Claim" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
              <sources hint="raw:AddSource">
                <claim name="http://schemas.microsoft.com/identity/claims/objectidentifier" />
              </sources>
              <targets hint="raw:AddTarget">
                <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" />
              </targets>
              <keepSource>true</keepSource>
            </transformation>
            <transformation name="Email Address" type="Sitecore.Owin.Authentication.Services.DefaultTransformation,Sitecore.Owin.Authentication">
              <sources hint="raw:AddSource">
                <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" />
              </sources>
              <targets hint="raw:AddTarget">
                <claim name="EmailId" />
              </targets>
            </transformation>
            <transformation name="nonce" type="Sitecore.Owin.Authentication.Services.DefaultTransformation,Sitecore.Owin.Authentication">
              <sources hint="raw:AddSource">
                <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nonce" />
              </sources>
              <targets hint="raw:AddTarget">
                <claim name="nonce" />
              </targets>
            </transformation>
            <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">
      </propertyInitializer>
    </federatedAuthentication>
| improve this answer | |
  • It would be helpful if you added a comment about exactly what you changed. – Hos Jan 15 at 19:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.