If user is signed in with Owin, Commerce's _customerContext.CurrentContactId changes constantly for user. Why is that?



I have this method which checks if a cart already exists:

protected ICart GetExistingCart(string name) => _orderRepository.LoadCart<ICart>(_customerContext.CurrentContactId, name, _currentMarket.GetCurrentMarket().MarketId);

If the method returns null, I create a new cart:

cart = _orderRepository.Create<ICart>(_customerContext.CurrentContactId, name);

This is used in the start of, and during the checkout flow.

Now, the thing is that our site generally allows for a user to signin to access some non-public areas of the site (pages that are not related to the checkout flow). Signin is with Owin (in Azure).

If the user is logged in, and afterwards decides to go to the checkout flow, Commerce's _customerContext.CurrentContactId changes constantly, and thus every time I call GetExistingCart for the same user, it won't find any cart. If I debug the code in Visual Studio, I can see that for every line I step over, _customerContext.CurrentContactId changes.

If however, user is not signed in already, _customerContext.CurrentContactId stays the same, and cart can be created and retrieved as expected during checkout.

Anyone knows what the problem can be with this _customerContext.CurrentContactId and its constant change of value when signed in with Owin?

Commerce version is 11.8.3.

Edited, Jan 31, 2019 10:48

Hi Bo Brinch,

I am also facing same issue. May I know if you have found any resolutoin for this.


Edited, Jul 23, 2019 17:04

Hi Bo

Do you create/save a CustomerContact entity on the user's first login? If not, you seem to get a new dummy CustomerContact instance everytime you call CustomerContext.CurrentContactId. Because it cannot find a matching entity in the database, yet the user is not un-authenticated anymore.

Basically, make sure that the database contains a CustomerContact entity, with the UserId property matching the unique user ID of the Azure user.

For anonymous (non logged-in) users, the CurrentContactId is stored in the buiilt-in anonymous cookie (in ".ASPXANONYMOUS" by default). But as soon as the user logs in, CurrentContactId is found according to the above described logic.

Edited, Jul 24, 2019 7:27

Hi Stefan,

I am also facing same issue as Bo. 

Do you mean we have to store CustomerContact entity in EPiServer even after implementing SIngle-sign on ?

Jul 24, 2019 8:04

Exactly, Mounesh.

As part of the regular user registration process, we need to create a CustomerContact upon creating/saving a new user account.

However, when you do Single-Sign-On (SSO), there is no regular registration process.

In this case, I check whether a CustomerContact exists for the unique user ID that was returned from the SSO login. If it doesn't exist, simply create one on the login callback method (filling as many properties as possible). Remember to set the UserID property of the CustomerContact to the ID of the returned user.

Jul 24, 2019 8:19

Hi Stefan,

The required field is of type GuId. The unique ids we get from ADFS are like EmailId, USerId (string), will these ids work for the Cart and Orders ?

Jul 24, 2019 9:26

Hi Mounesh

CustomerContact.UserId is supposed to be a string. If you provide it with a string, the user ID is supposed to prefix it with "String:".

To make it easy, you can generate the value for the UserId property by calling MapUserKey.ToTypedString (use constructor injection of MapUserKey). It will take care of casting (in case the ID is not a string) and correctly prefixing the user ID.

Jul 24, 2019 9:41

Do you have either ServiceAPI installed, or do you have SuppressDefaultHostAuthentication called anywhere in your code? 

Jul 24, 2019 9:43

Hi Stefan, 

Thanks for you solution suggested I will try the solution.

Just for your information, I am using PrincipalInfo.CurrentPrincipal.GetContactId() and CustomerContext.Current.CurrentContactId which are of type GuId.

Jul 24, 2019 10:13

You are welcome, Mounesh. Please let me know how it works out for you.

Those two methods you refer to returns the Id of the CustomerContact entities. The UserId is a string property that defines the login user that the CustomerContact is linked to.

By the way. When you create a CustomerContact, the value of the UserId (besides the mentioned prefix) has to match the value of the logged-in user's Identity.Name property. That could be an email, a GUID value or something else, depending on your authentication provider.

Jul 24, 2019 11:07

Hi Stefan,

I have tried using UserId, even hardcoding "String:Email", howvever below function takes GuId as first parameter. So It's not working.

_orderRepository.LoadCart<ICart>(_customerContext.CurrentContactId, name, _currentMarket);

Please let me know if you have any comments or suggestions.

Jul 25, 2019 6:55

I think you misunderstood me, Mounesh.

After the visitor returns from Azure as a logged-in user, check if he/she has a CustomerContact already. If not, create one.

Below is a quick example. You can put it somewhere in your login callback. In short, it tries to load a CustomerContact for the logged-in user, and create/save one if necessary.

IPrincipal userPrincipal = PrincipalInfo.CurrentPrincipal;
CustomerContact contact = _customerContext.GetContactByUsername(userPrincipal.Identity.Name);
if (contact == null)
    // TODO: Get user's email from the identity provider or ClaimsIdentity.
    string email = "user@domain.tld";

    // Using this method (instead of the default) correctly sets the UserId for us, using the logged-in user's IPrincipal.
    contact = CustomerContact.CreateInstance(userPrincipal, null, email);

    // TODO: Fill as many fields as possible, using data from the identity provider.
    // This is just a hardcoded example.
    contact.FirstName = "FirstName";
    contact.LastName = "LastName";
    contact.FullName = "FirstName LastName";

You don't have to make any changes to the way you load the cart. The following line will work, since now the user has a CustomerContact.

ICart cart = _orderRepository.LoadCart<ICart>(_customerContext.CurrentContactId, name, _currentMarket);
Edited, Jul 25, 2019 7:44

Hi Stefan,

It is creating CustomerContact object. But the issue is _customerContext.CurrentContactId value is changing continuously. 

Lets assume When I add a prodcut to cart the value _customerContext.CurrentContactId is {1-2-3}, the product gets added successfully. When I try to retrieve the products from cart the value of _customerContext.CurrentContactId will be {4-5-6}. Then it returns empty cart for the user.

Jul 25, 2019 7:51

Did you create the object yourself (similarly to example)? Or did Commerce just create an object when you call _customerContext.CurrentContactId?

Basically what I'm asking is: Are you seeing an auto-generated/transient CustomerContact object? Can you find the contact using Commerce Manager?

If you see the transient object, it will be changed every time you call _customerContext.CurrentContactId. That is why I suggest that you both create and save the object yourself before using it (which is what my sample code shows).

Jul 25, 2019 8:06

Hi Stefan,

I have not created it manually. I will create one and check.

Jul 25, 2019 9:01

Hi Quan,

We have not installed ServiceAPI and dont have SuppressDefaultHostAuthentication.

Could you please tell where it is required and alos any documentation for this related to Single Sign On.

Jul 25, 2019 12:56

Are you in the same company with Bo - i.e. are you working with same problem?

It's not that ServiceAPI is required (nor SuppressDefaultHostAuthentication()). It's the way around, ServiceAPI, which calls SuppressDefaultHostAuthentication internally, remove the authentication cookie that required for customerContext.CurrentContactId to work properly. If you don't have those then I don't have a good explanation of why it does not work for you. 

Jul 25, 2019 13:02

Hi Quan,

Thanks for the explaination on the ServiceAPI.

Yes we both work for same company ( based on his profile details. I work for Capgemini India and he works for Capgemini Denmark. But not for same Project. I just came across this page when I was looking for some solution for the issue and found that, Bo also has faced it. 

Jul 25, 2019 13:22

Hi Guys,

Reviving this thread again.

I am experiencing the same issue, and it happens randomly for anonymous users. I do know "WHY" it's happening but don't know how to solve it.

The .ASPXANONYMOUS cookie gets randomly refreshed with a new value (notoriously iOS) which causes the cart to vanish because CustomerContext is generated off this cookie. This directly impacts the conversion rate, because the user needs to add all items back to the cart which is extremely annoying. NOTE: This only happens for guest checkout users (considering logged-in users don't look at the .ASPXANONYMOUS cookie). 

Also, we don't have SuppressDefaultHostAuthentication set anywhere.

Any help would be greatly appreciated.

Feb 11, 2022 15:37
Stefan Holm Olsen - Feb 11, 2022 15:53
Sounds like the . ASPXANONYMOUS cookie is being dropped by the client (is this a browser or an app?). Usually the server prolongs the lifetime of that cookie around halfway into its lifetime, as long as it is used.
You mention that it happens most often on iOS. Be aware that WebView browsers can be tricky on iOS, but the Safari browser is usually fine.

NOTE: SuppressDefaultHostAuthentication is mostly useful for API controllers, where you want to use another authentication mechanism (e.g. bearer token instead of cookie).

@Aniket, are you sure it's not connected to a missing machine key configuration? There's a risk that if you restart (for example by deploying code) your IIS website without having a specified machine key in the web.config, the website will generate a new machine key and thereby also create a new .ASPXANONYMOUS cookie (because the hashing algo changed).

Mar 22, 2022 11:42
* 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.