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

Federated Security and Commerce Manager

Vote:
 

We're trying to implement a mixed mode authemtication scenario where CMS Editors/Admins login using Azure AD, but Commerce Customers log in using the Commerce database.

Our Startup.cs for the EPiServer CMS is as follows:

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

    public void Configuration(IAppBuilder app)
    {
      app.AddCmsAspNetIdentity<SiteUser>(new ApplicationOptions
      {
      });

      //// This will configure cookie authentication at the following urls.
      //// In those pages you are responsible for authentication and calling the OwinContext.Authentication.SignIn method to properly sign in the user
      //// and OwinContext.Authentication.SignOut to logout a user
      app.UseCookieAuthentication(new CookieAuthenticationOptions
      {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/login"),
        LogoutPath = new PathString("/logout"),
        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<SiteUser>, SiteUser>(
                                                                                                                     validateInterval: TimeSpan.FromMinutes(30),
                                                                                                                     regenerateIdentity: (manager, user) => manager.GenerateUserIdentityAsync(user)),
          OnApplyRedirect = context => context.Response.Redirect(context.RedirectUri),
          OnResponseSignOut = context => context.Response.Redirect(UrlResolver.Current.GetUrl(ContentReference.StartPage))
        }
      });

      // Enable cookie authentication, used to store the claims between requests 
      app.SetDefaultSignInAsAuthenticationType(
         WsFederationAuthenticationDefaults.AuthenticationType);
      app.UseCookieAuthentication(new CookieAuthenticationOptions
      {
        AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
      });

      // Enable federated authentication 
      app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
      {
        // Trusted URL to federation server meta data 
        MetadataAddress = ConfigurationManager.AppSettings["MetadataAddress"],

        // Value of Wtreal must *exactly* match what is configured in the federation server 
        Wtrealm = ConfigurationManager.AppSettings["Wtrealm"],
        Notifications = new WsFederationAuthenticationNotifications
        {
          RedirectToIdentityProvider = 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 = async 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;
            }

            // Create claims for roles
            await ServiceLocator.Current.GetInstance<AzureGraphService>()
               .CreateRoleClaimsAsync(ctx.AuthenticationTicket.Identity);

            try
            {
              // Sync user and add the roles to EPiServer in the background 
              await ServiceLocator.Current.GetInstance<ISynchronizingUserService>()
                 .SynchronizeAsync(ctx.AuthenticationTicket.Identity);
            }
            catch (Exception ex)
            {
              throw new Exception(
                            "Name Claim Type: "
                           + ctx.AuthenticationTicket.Identity.NameClaimType,
                            ex);
            }
          }
        }
      });

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

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

In the CMS site's web.config, we've set the authentication mode to "none" and disabled the membership and rolemanager settings.

This works fine when browsing to /episerver - the user is redirected to the authenticaiton provider and once logged in successfully is redirected to the episerver back-end.

Similarly, if a customer logs in to the site at the /Login page, they are prompted for their username and password and can log in OK.

However, when I click the Commerce Manager link in the back-end, I'm prompted to log in using the standard Commerce Admin login page even though my user is a member of the virtual role Commerce Admins - is this correct?

I assume that for Commerce Customers to log in, the Commerce Manager site needs to retain the SQL Server membership and role provider settings, but is it possible to disable authentication for the Commerce Manager site so we don't need to log in to the Commerce Manager back-end?

If so, do I need to provide the same/similar Startup.cs for the Commerce Manager site?

There seems to be some interaction between the MediaChase assemblies and the login porcess that I need to override, but it's not clear what...

#207387
Sep 18, 2019 11:54
Vote:
 

Worked it out: Commerce needs to use Asp.NET Identity only

For consistency, I've used the Commerce DB for all users created via Asp NET identity

#207431
Sep 19, 2019 18:12
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.