Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Nguyen Dung Tri 106 posts 606 karma points
    Sep 28, 2016 @ 08:18
    Nguyen Dung Tri
    0

    How to develop Active Directory Authentication on Merchello Front-End?

    I have used OpenID Connect to sign-in users from a single Azure Active Directory tenant on Merchello (The sample code is available here). I have implemented some changes on Merchello as follow:

    1. Project Merchello.FastTrack.Ui

      • Web.Config

    enter image description here

    • App_Start/MerchelloADAuthenticationStartup.cs

    Note: I create a new class in App_Start. The code in this class is same with Startup.Auth.cs from Azure Sample project from GitHub. enter image description here

    • App_Plugins/FastTrack/Views/CustomerMembership/LoginForm.cshtml

    Note: I have updated this file to add a button that can help user to login on Merchello using AD Authentication.

        @inherits Umbraco.Web.Mvc.UmbracoViewPage<Merchello.FastTrack.Models.Membership.LoginModel>
    @using System.Web.Mvc.Html
    @using Merchello.FastTrack.Controllers.Membership
    @using Merchello.FastTrack.Ui
    @using Umbraco.Web
    <div class="panel panel-default">
        <div class="panel-heading"><strong>Login to existing account</strong></div>
        <div class="panel-body">
            @if (Request.IsAuthenticated)
            {
                <ul class="nav navbar-nav navbar-right">
                    <li class="navbar-text">
                        Hello, @User.Identity.Name!
                    </li>
                    <li>
                        @using (Html.BeginUmbracoForm<CustomerMembershipController>("SignOut", new { area = "FastTrack" }))
                        {
                            <div class="form-group">
                                <input type="submit" value="Sign out" class="btn btn-default pull-right" />
                            </div>
                        }
                    </li>
                </ul>
            }
            else
            {
                using (Html.BeginUmbracoForm<CustomerMembershipController>("Login", new { area = "FastTrack" }))
                {
                    @Html.AntiForgeryToken()
                    <div class="row">
                        <div class="col-md-12 form-group">
                            @Html.LabelFor(x => x.Username)
                            @Html.TextBoxFor(x => x.Username, new { @placeholder = "Your email address", @class = "form-control" })
                            @Html.ValidationMessageFor(x => x.Username)
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-md-12 form-group">
                            @Html.LabelFor(x => x.Password)
                            @Html.PasswordFor(x => x.Password, new { @placeholder = "Your password", @class = "form-control" })
                            @Html.ValidationMessageFor(x => x.Password)
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-md-12 form-group checkbox">
                            <label>
                                @Html.CheckBoxFor(x => x.RememberMe) Remember Me
                            </label>
                            @Html.HiddenFor(x => x.SuccessRedirectUrl)
                            <input type="submit" value="Login" class="btn btn-default pull-right" />
                        </div>
                    </div>
                }
                <div class="row">
                    <div class="col-md-12 form-group">
                        @using (Html.BeginUmbracoForm<CustomerMembershipController>("SignIn", new { area = "FastTrack" }))
                        {
                            <input type="submit" value="Login with Active Directory" class="btn btn-default pull-right" />
                        }
                    </div>
                </div>
            }
        </div>
    
    </div>
    
    1. Project Merchello.FastTrack

      • Controllers/Membership/CustomerMembershipController.cs

    I have added three methods (SignIn, SignOut, EnSession) in this class: enter image description here

    1. Work follow

      • When I run Merchello.FastTrack.Ui project. The MerchelloADAuthenticationStartup will run first (I have set owin:appStartup in Web.Config to start up my Authentication class).

      • From Login Page on front-end of Merchello, I click button "Login with Active Directory" to call method "SignIn" from
        CustomerMembershipController. The method will redirect to Microsoft
        Login site.

      • When Microsoft user login successful and it redirect back to my
        website.

    My Target: I want to my Merchello site use Microsoft user as logged
    customer on my website. How can I do that?

  • Nguyen Dung Tri 106 posts 606 karma points
    Sep 28, 2016 @ 09:27
    Nguyen Dung Tri
    0

    When I click "Login with Active Directory" button and redirect to Microsoft Login Site. If user logged successful, it redirect to my Merchello site, then I try to end Login page again. It generated an error:

    enter image description here

    It said:

    A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' or 'http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider' was not present on the provided ClaimsIdentity. To enable anti-forgery token support with claims-based authentication, please verify that the configured claims provider is providing both of these claims on the ClaimsIdentity instances it generates. If the configured claims provider instead uses a different claim type as a unique identifier, it can be configured by setting the static property AntiForgeryConfig.UniqueClaimTypeIdentifier.
    

    This error occurred when the front-end load "Merchello.FastTrack.Ui\App_Plugins\FastTrack\Views\CustomerMembership\RegisterForm.cshtml" view. Somehow the authentication is not accepted in this context.

    I am still stuck at thist and dont' know how to implement Active Directory Authentication on Merchello Front-End

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Sep 28, 2016 @ 12:41
    Rusty Swayne
    1

    Hi Nguyen,

    By default, Merchello works with Umbraco's Membership provider and internally uses the Membership helper to determine the role of the user per request. When you swap out the provider you also need to create your own CustomerContext for the implementation.

    This is not hard to do.

    Create a new class that inherits from CustomerContextBase https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web/Pluggable/CustomerContextBase.cs

    You only have to implement three abstract methods which are generally pretty straight forward to do.

    You would then swap out the configuration for the CustomerContext in the Merchello.config to your new class:

      <pluggable>
        <object alias="CustomerContext" type="[YOUR NEW TYPE]" />
          ........
      </pluggable>
    

    Also, my guess is you'll want to use your own version of the CustomerMembershipController as well ...

  • Nguyen Dung Tri 106 posts 606 karma points
    Sep 28, 2016 @ 17:46
    Nguyen Dung Tri
    0

    Hello Rusty Swayne,

    To create my new customer context, I will create a new a new Customer Context class and name it as "ADCustomerContext.cs" in the same place with CustomerContext. In this step, I don't understand what are three abstract methods you told me to implement?

    After created my own Customer Context, I have looked for merchelo.config file. And I update CustomerContext to my new context. Something like:

    <pluggable>
        <object alias="ADCustomerContext" type="Merchello.Web.ADCustomerContext, Merchello.Web" />
    </pluggable>
    

    Also, If I want to use my own version of CustomerMembershipController, then should I create a new Controller at same place with CustomerMembershipController? Maybe I will name it as ADCustomerMembershipController.cs. After that, I will move the three methods SignIn, SignOut, EndSession to the new controller. Is that correct?

    I also have some new questions: What does new CustomerContext (ADCustomerContext.cs) do to my login using Active Directory? Why do we need to create a new one instead of modify the current CustomerContext to make it adaptable?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Sep 28, 2016 @ 20:33
    Rusty Swayne
    0

    HI Nguyen,

    You should not need to do anything at all with the Merchello libraries. If you make alterations to these you'll wind up with upgrade issues in the future.

    Your ADCustomerContext would look something like:

     namespace SomeLibrary.SomeNamespace
     {
          using Merchello.Web.Pluggable;
    
          public class ADCustomerContext : CustomerContextBase 
          {
              public ADCustomerContext(UmbracoContext umbracoContext)
                 : base(umbracoContext)
              {
              }
    
           protected override bool GetIsCurrentlyLoggedIn()
           {
               throw new System.NotImplementedException();
           }
    
           protected override string GetMembershipProviderUserName()
           {
               throw new System.NotImplementedException();
           }
    
           protected override string GetMembershipProviderKey()
           {
               throw new System.NotImplementedException();
            }
          }
    
     }
    

    In the Merchello.config

     <pluggable>
         <object alias="ADCustomerContext" type="SomeLibrary.SomeNamespace.ADCustomerContext, SomeLibrary" />
     </pluggable>
    
  • Nguyen Dung Tri 106 posts 606 karma points
    Sep 28, 2016 @ 22:23
    Nguyen Dung Tri
    0

    Hello Rusty Swayne,

    I have three places that need to update Customer Context:

    enter image description here

    • Merchello-merchello-dev\test\Merchello.Tests.PaymentProviders\app.config

    • Merchello-merchello-dev\src\Merchello.Web.UI.Client\src\config\merchello.config

    • Merchello-merchello-dev\src\Merchello.Web.UI.Client\build\App_Plugins\Merchello\config\merchello.config

    So, which place should I update to ADCustomerContext?

    1. For my ADCustomerContext:

    namespace Merchello.Web { using Merchello.Web.Pluggable; using Umbraco.Web; using System.Web.Mvc;

    public class ADCustomerContext : CustomerContextBase
    {
        public ADCustomerContext(UmbracoContext umbracoContext)
           : base(umbracoContext)
        {
        }
    
        protected override bool GetIsCurrentlyLoggedIn()
        {
            Return Request.IsAuthenticated;
        }
    
        protected override string GetMembershipProviderUserName()
        {
            throw new System.NotImplementedException();
        }
    
        protected override string GetMembershipProviderKey()
        {
            throw new System.NotImplementedException();
        }
    }
    

    }

    In method "GetIsCurrentlyLoggedIn", I have used Request.IsAuthenticated to check Active Directory Authentication on my website. But Request is generated an error, what library should I using in this context?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Sep 28, 2016 @ 22:29
    Rusty Swayne
    1

    None of those -

    The first is for testing merchello - should not affect anything since you'd have to modify the tests to work with your context.

    Second is the source location for Merchello build scripts.

    The third is just an intermediate build directory that gets deleted whenever you run the grunt script.

    You should really rethink building an app directly in the Merchello solution. It's not at all intended to be used for that and you'll very likely wind up other issues that will be very difficult for you to get help with since you've changed the base code.

    All of your work should be done in a separate project, with Merchello installed as a plugin.

    The only place you should need to update the Merchello.config file would be in

    /App_Plugins/Merchello/Config/Merchello.config

  • Nguyen Dung Tri 106 posts 606 karma points
    Sep 29, 2016 @ 21:39
    Nguyen Dung Tri
    0

    Hello Rusty Swayne,

    From the first post of this question, could you simulate my code on your Merchello solution? I still don't know why the authentication is success after login using Active Directory Authentication, but the page still rise a lot of error on the Authentication when access basket page, or product page.

    If you suggest me to create a new membership provider or customer context, then please give me an example.

    Regards, Dung Tri

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Sep 30, 2016 @ 15:27
    Rusty Swayne
    1

    Sorry bud - we don't really have the time nor resources to do that sort of thing at the moment. The stuff you are doing is pretty implementation specific and customized - both Merchello and Umbraco (membership).

    For the membership stuff, I think I'd look at Shannon's "UmbracoIdentity" project for some ideas on an integration: https://github.com/Shazwazza/UmbracoIdentity

    What caught my eye is the OwinStartup - https://github.com/Shazwazza/UmbracoIdentity/blob/master/src/UmbracoIdentity.Web/App_Code/UmbracoIdentityStartup.cs

    I try to stay away from App_Start so that UmbracoApplicationBase can do it's thing - not a requirement by any means, but IMO it makes sense to keep things sort of subordinate to Umbraco when extending Umbraco. You also have a better idea in the order things are being executed - said another way - let Umbraco tell you when it's ready =)

    This is sort of why the CustomerContext exists. Umbraco members (or in your case AD Membership Users) don't know anything about Merchello AND Merchello was never designed to even remotely care about membership authentication / security. From Merchello's perspective that is the job of the CMS and/or the implementation which is setup to do it much better.

    In Merchello there is a difference between an Anonyous customer (not logged in) and a Customer (logged in) ... we simply need an association so we can look up records like addresses and invoices associated with the customer.

    The tricky bits of CustomerContext classes are all handled in the base class (CustomerContextBase) - getting the CurrentCustomer based on information provided by whatever membership provider AND mapping baskets between anonymous and know customers (or back in the case of logging out) efficiently based on the current request.

    In the snippet of code I posted above it shows a scaffold of what you would need to implement for the AD Membership provider. Basically three methods:

     // Most often these are one line returns from the wrapped provider.
    
     // Ask the AD member is logged in 
     bool GetIsCurrentlyLoggedIn();
    
     // What username is the provider using (this is Merchello's loginName for the customer)
     string GetMembershipProviderUserName()
    
     // What is the unique key or id the provider is using for that member
     // Could be a GUID, email address, int Id ... whatever
     string GetMembershipProviderKey()
    

    FYI - There are a couple of programs in the works which should launch in a few weeks with paid options to help support and fund the Merchello.

  • Nguyen Dung Tri 106 posts 606 karma points
    Oct 01, 2016 @ 10:05
    Nguyen Dung Tri
    0

    So, do you mean the Merchello use member (Member of Umbraco) login as customer login? If so, then what I care about is the authentication of logged Member.

    Also, if people login with Active Directory Authentication, it doesn't create any Member with given data from AD Authentication (I mean it auto create a member with same username with Microsoft Account). I can take care of this now.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Oct 01, 2016 @ 13:27
    Rusty Swayne
    0

    Merchello has a merchCustomer table, but it has nothing at all to do with authentication. It's loosely associated with whatever membership provider by the loginName field (or username) in the CustomerContext.

    By default, Merchello uses the Umbraco "Member" not the "User" - User in Umbraco is generally thought to be for back office authentication.

    Merchello is concerned with whether or not the website visitor is logged in so it can determine whether or not to treat the visitor as an Anonymous customer IAnonymousCustomer (something that gets setup usually per session) and an "existing customer" ICustomer.

    If we can create a customer record, we do things like save addresses, baskets, wish lists and associate invoices with the customer. In the case of AnonymousCustomers there will be no customer record - so in general we will only have an invoice and the basket is lost after the cookie that contains the db key reference expires.

    You can schedule the cleanup of old anonymous customer data by setting up a scheduled task (umbracoSettings.config)

     <scheduledTasks>
          <!-- add tasks that should be called with an interval (seconds) -->
          <task log="true" alias="clearItemCache" interval="86400" url="http://localhost/umbraco/merchello/ScheduledTasksApi/RemoveAnonymousCustomers/"/>
      </scheduledTasks>
    

    This task respects the value in the merchello.config file - by default deleting anonymous customer records older than 31 days.

    <!--
    Setting introduced in version 1.3.0.  Value indicates the maximum number of days to store
    anonymous customer records.  After the number of days, a maintenance routine will remove the 
    records from the database.
    Requires that you add the the following scheduled task in umbracoSettings.config
    <task log="true" alias="removeAnonymousCustomers" interval="14400" url="http://localhost/umbraco/Merchello/ScheduledTasksApi/RemoveAnonymousCustomers/">
    -->
    <setting alias="AnonymousCustomersMaxDays" value="31" />
    

    By default Merchello customers are created in the background based off an event:

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web/UmbracoApplicationEventHandler.cs#L456

    Depending on your membership provider implementation, you may have to write your own handler to trigger the customer creation.

    You also may notice that the "First name and Last Name" are not copied at this point - since Merchello does not know anything about the actual Membership content type.

    In the FastTrack starter kit, we create a member type in the installer (which includes a firstName and lastName field). So in that implementation we can expect the fields to be there and the data is populated in by a second event handler:

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.FastTrack.Ui/UmbracoEventHandler.cs#L130

    The actual implementation of the membership provider is an Umbraco customization detail.

    There was an article in 24 days that may be helpful for you - http://24days.in/umbraco/2015/extending-membership/ - there are tons of other posts too.

  • Nguyen Dung Tri 106 posts 606 karma points
    Oct 03, 2016 @ 09:00
    Nguyen Dung Tri
    0

    Hello Rusty,

    I am currently facing an issue of @Html.AntiForgeryToken() when apply new CustomerContext. The error message:

    A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' or 'http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider' was not present on the provided ClaimsIdentity. To enable anti-forgery token support with claims-based authentication, please verify that the configured claims provider is providing both of these claims on the ClaimsIdentity instances it generates. If the configured claims provider instead uses a different claim type as a unique identifier, it can be configured by setting the static property AntiForgeryConfig.UniqueClaimTypeIdentifier.
    

    I've got this error message when trying to login with Active Directory. But if I remove @Html.AntiForgeryToken(), then this error gone. But I'm afraid that this can cause some other issues later. Could you tell me what AntiForgeryToken does on BillingAddressForm.cshtml and BasketForm.cshtml?

    Regards, Dung Tri

  • Nguyen Dung Tri 106 posts 606 karma points
    Oct 06, 2016 @ 08:30
    Nguyen Dung Tri
    100

    I fixed this issue of (AntiForgeryToken) by adding this line of code to Configuration method from my Authentication Startup class:

    AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
    

    public override void Configuration(IAppBuilder app)
            {
                //ensure the default options are configured
                base.Configuration(app);
    
                AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
    

    For everyone who want to develop a feature that allow member to login front-end of Merchello, please read the whole question and the answers till the end to understand. I my self was successful to develop one for me.

Please Sign in or register to post replies

Write your reply to:

Draft