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:
Project Merchello.FastTrack.Ui
Web.Config
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.
I have added three methods (SignIn, SignOut, EnSession) in this class:
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?
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:
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
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.
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:
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?
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();
}
}
}
So, which place should I update to ADCustomerContext?
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?
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
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.
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).
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.
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.
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:
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:
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?
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.
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:
Project Merchello.FastTrack.Ui
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.
Note: I have updated this file to add a button that can help user to login on Merchello using AD Authentication.
Project Merchello.FastTrack
I have added three methods (SignIn, SignOut, EnSession) in this class:
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?
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:
It said:
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
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.csYou 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:
Also, my guess is you'll want to use your own version of the CustomerMembershipController as well ...
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:
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?
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:In the Merchello.config
Hello Rusty Swayne,
I have three places that need to update Customer Context:
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?
namespace Merchello.Web { using Merchello.Web.Pluggable; using Umbraco.Web; using System.Web.Mvc;
}
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?
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
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
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:
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.
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.
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)
This task respects the value in the merchello.config file - by default deleting anonymous customer records older than 31 days.
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.
Hello Rusty,
I am currently facing an issue of @Html.AntiForgeryToken() when apply new CustomerContext. The error message:
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
I fixed this issue of (AntiForgeryToken) by adding this line of code to Configuration method from my Authentication Startup class:
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.
is working on a reply...