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

Vote:
 

Hi

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);
SaveCart(cart);

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.

#200939
Edited, Jan 31, 2019 11:59
Vote:
 

Hi Bo Brinch,

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

//Mounesh

#205774
Edited, Jul 23, 2019 17:04
Vote:
 

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.

#205795
Edited, Jul 24, 2019 7:27
Vote:
 

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 ?

#205799
Jul 24, 2019 8:04
Vote:
 

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.

#205800
Jul 24, 2019 8:19
Vote:
 

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 ?

#205802
Jul 24, 2019 9:26
Vote:
 

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.

#205804
Jul 24, 2019 9:41
Vote:
 

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

#205805
Jul 24, 2019 9:43
Vote:
 

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.

#205806
Jul 24, 2019 10:13
Vote:
 

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.

#205810
Jul 24, 2019 11:07
Vote:
 

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.

#205848
Jul 25, 2019 6:55
Vote:
 

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";
    contact.SaveChanges();
}

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);
#205849
Edited, Jul 25, 2019 7:47
Vote:
 

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.

#205850
Jul 25, 2019 7:51
Vote:
 

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).

#205851
Jul 25, 2019 8:06
Vote:
 

Hi Stefan,

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

#205852
Jul 25, 2019 9:01
Vote:
 

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.

#205855
Jul 25, 2019 12:56
Vote:
 

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. 

#205856
Jul 25, 2019 13:02
Vote:
 

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. 

#205857
Jul 25, 2019 13:22
* 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.