This guide explains how to implement customer login and account management in a frontend application using Norce Commerce Services. It covers common scenarios, best practices, and technical details for handling customer data, authentication, and password management.
Norce Commerce provides basic account management and password validation. Each account is linked to a Customer object, which can be used for segmentation, pricing, and offers.
- For more details, see the Norce Commerce user documentation.
- Example API requests are available in Postman.
Note:
If you use a third-party authentication provider, you do not need Norce accounts. Instead, match the authenticated identity to a Norce customer using the email address orCustomerCode
.
This document focuses on scenarios where Norce Commerce manages the account information.
Below are example representations of a customer in JSON and XML formats. These examples include both individual and company-related fields.
Example Customer (without company)
{
"Id": 3710298,
"Key": "264148a6-9c4c-4f7c-827b-8c43ac72a852",
"Code": "cust0043",
"Email": "test0043@tester.com",
"SSN": null,
"FirstName": "Test person 0043",
"LastName": null,
"Phone": null,
"CellPhone": null,
"ReferId": null,
"ReferUrl": null,
"Account": {
"Id": 2095010,
"Key": "f4026524-39b5-43fe-93b3-63da483a7597",
"LoginName": "test0043@tester.com",
"Name": "Test person 0043",
"Roles": [
43
],
"Authorizations": [],
"IsActive": true
},
"Companies": [
{
"Id": 334815,
"Key": "85abed80-7c24-4d26-8f4f-5ad96ca0f222",
"Code": "test0043",
"Name": "Test kund 0043",
"OrgNo": null,
"Phone": null,
"ReferId": null,
"ReferUrl": null,
"DeliveryAddresses": [],
"InvoiceAddress": {
"Id": 11115165,
"CareOf": null,
"Line1": "Testgatan 1",
"Line2": null,
"Zip": "123 45",
"City": "Stockholm",
"CountryId": 0,
"Country": null,
"Region": null,
"IsValidated": false,
"GlobalLocationNo": null,
"ShippingPhoneNumber": null
},
"UseInvoiceAddressAsDeliveryAddress": false,
"Info": [
{
"Id": 267,
"Value": "",
"Code": "A1"
}
],
"PricelistIds": [],
"ParentId": null,
"DeliveryMethodIds": [],
"PaymentMethodIds": [],
"Email": null,
"Flags": [
{
"Id": 118,
"Name": "Bronskund",
"Group": 37,
"IsSelected": true
}
],
"VatNo": null
}
],
"DeliveryAddresses": [],
"InvoiceAddress": {
"Id": 2608019,
"CareOf": null,
"Line1": null,
"Line2": null,
"Zip": null,
"City": null,
"CountryId": 0,
"Country": null,
"Region": null,
"IsValidated": false,
"GlobalLocationNo": null,
"ShippingPhoneNumber": null
},
"Flags": [
{
"Id": 117,
"Name": "Silverkund",
"Group": 37,
"IsSelected": true
}
],
"UseInvoiceAddressAsDeliveryAddress": false,
"Info": [
{
"Id": 237,
"Value": "",
"Code": "Xtra1"
}
],
"PricelistIds": [],
"CrmId": null,
"IsActive": true,
"Created": "/Date(1583484623047+0100)/",
"Updated": "/Date(1583484785267+0100)/"
}
<Customer xmlns="Enferno.Services.Contracts.Expose.Customers" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Id>3710298</Id>
<Key>264148a6-9c4c-4f7c-827b-8c43ac72a852</Key>
<Code>cust0043</Code>
<Email>test0043@tester.com</Email>
<SSN i:nil="true"/>
<FirstName>Test person 0043</FirstName>
<LastName i:nil="true"/>
<Phone i:nil="true"/>
<CellPhone i:nil="true"/>
<ReferId i:nil="true"/>
<ReferUrl i:nil="true"/>
<Account>
<Id>2095010</Id>
<Key>f4026524-39b5-43fe-93b3-63da483a7597</Key>
<LoginName>test0043@tester.com</LoginName>
<Name>Test person 0043</Name>
<Roles xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a:int>43</a:int>
</Roles>
<Authorizations xmlns:a="Enferno.Services.Contracts.Expose"/>
<IsActive>true</IsActive>
</Account>
<Companies>
<Company>
<Id>334815</Id>
<Key>85abed80-7c24-4d26-8f4f-5ad96ca0f222</Key>
<Code>test0043</Code>
<Name>Test kund 0043</Name>
<OrgNo i:nil="true"/>
<Phone i:nil="true"/>
<ReferId i:nil="true"/>
<ReferUrl i:nil="true"/>
<DeliveryAddresses/>
<InvoiceAddress>
<Id>11115165</Id>
<CareOf i:nil="true"/>
<Line1>Testgatan 1</Line1>
<Line2 i:nil="true"/>
<Zip>123 45</Zip>
<City>Stockholm</City>
<CountryId>0</CountryId>
<Country i:nil="true"/>
<Region i:nil="true"/>
<IsValidated>false</IsValidated>
<GlobalLocationNo i:nil="true"/>
<ShippingPhoneNumber i:nil="true"/>
</InvoiceAddress>
<UseInvoiceAddressAsDeliveryAddress>false</UseInvoiceAddressAsDeliveryAddress>
<Info xmlns:a="Enferno.Services.Contracts.Expose">
<a:Item>
<a:Id>267</a:Id>
<a:Value/>
<a:Code>A1</a:Code>
</a:Item>
</Info>
<PricelistIds xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
<ParentId i:nil="true"/>
<DeliveryMethodIds xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
<PaymentMethodIds xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
<Email i:nil="true"/>
<Flags>
<Flag>
<Id>118</Id>
<Name>Bronskund</Name>
<Group>37</Group>
<IsSelected>true</IsSelected>
</Flag>
</Flags>
<VatNo i:nil="true"/>
</Company>
</Companies>
<DeliveryAddresses/>
<InvoiceAddress>
<Id>2608019</Id>
<CareOf i:nil="true"/>
<Line1 i:nil="true"/>
<Line2 i:nil="true"/>
<Zip i:nil="true"/>
<City i:nil="true"/>
<CountryId>0</CountryId>
<Country i:nil="true"/>
<Region i:nil="true"/>
<IsValidated>false</IsValidated>
<GlobalLocationNo i:nil="true"/>
<ShippingPhoneNumber i:nil="true"/>
</InvoiceAddress>
<Flags>
<Flag>
<Id>117</Id>
<Name>Silverkund</Name>
<Group>37</Group>
<IsSelected>true</IsSelected>
</Flag>
</Flags>
<UseInvoiceAddressAsDeliveryAddress>false</UseInvoiceAddressAsDeliveryAddress>
<Info xmlns:a="Enferno.Services.Contracts.Expose">
<a:Item>
<a:Id>237</a:Id>
<a:Value/>
<a:Code>Xtra1</a:Code>
</a:Item>
</Info>
<PricelistIds xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
<CrmId i:nil="true"/>
<IsActive>true</IsActive>
<Created>2020-03-06T09:50:23.047</Created>
<Updated>2020-03-06T09:53:05.267</Updated>
</Customer>
Scenario: A user initiates account creation, either during checkout or by choosing to become a member.
Process:
- Collect personal information from the user.
- Call the Norce Commerce Customer Service method
RegisterCustomer3
with:- A
password
string. - A
Customer
object containing anAccount
object (required for account functionality).
- A
- Norce returns a new
Customer
object with anAccount
and internalId
. Store this information securely. - Send a confirmation email to the customer, either via your backend or using Norce's built-in functionality.
Illustration: Account creation process
Note:
Norce does not validate password strength. Implement password validation in your backend.
Technical Note:
Passwords are securely hashed before storage in Norce.
To authenticate a user:
- Call the
LoginPost
method withaccountname
andpassword
. - If credentials are correct, a
Customer
object is returned. Add this to the session and save to a cookie. - If authentication fails, an exception is returned. See the API reference for error details.
Illustration: Login process
When a user revisits the site and is already logged in (e.g., via a cookie with accountId
):
- Retrieve the
accountId
from the cookie. - Call
GetCustomerByAccountId
. - If the account is valid, a
Customer
object is returned. If not,null
is returned.
Illustration: Returning user session
Scenario: The user has forgotten their password or wants to change it.
Process:
- User provides account information (email, customer code, or login name).
- Backend fetches the
Customer
object using one of: - Backend generates an email with an encrypted link containing the account name and a validity timestamp.
- User clicks the link, which is decrypted and validated. User is prompted for a new password.
- Backend calls
UpdateCustomer3
with theCustomer
object and new password.
Illustration: Password reset process
Note:
Customer emails must be unique within Norce. Uniqueness can be scoped to the client or application.
When sending password reset links, encrypt the information to avoid exposing sensitive data. Below is a C# example for creating and resolving secure links:
C# Example: Password Reset Link
public class PasswordResetHelper
{
/// <summary>
/// Create the clickable link to send to the users registered email
/// The current timestamp is part of the token
/// </summary>
public string CreateLinkToTheMail(string loginName)
{
var token = $"{account.LoginName}|{DateTime.Now}";
token = RijndaelEncryption.EncryptToUrl(token);
return $"{PathHelper.FullBaseUrl()}login;token={token}";
}
/// <summary>
/// Resolve the login name from the token in the link.
/// </summary>
/// <returns>LoginName</returns>
public string ResolveLinkFromMail(string token)
{
token = RijndaelEncryption.DecryptFromUrl(token);
var parts = token.Split('|');
if (parts.Length != 2)
throw new Exception("Bad token");
var accountName = parts[0];
DateTime date;
if (!DateTime.TryParse(parts[1], out date))
{
throw new Exception("Bad token");
}
if (DateTime.Now - date > TimeSpan.FromHours(24))
{
throw new Exception("Change password mail is older than 24 hours.");
}
return accountName;
}
}