Last updated

Working with Customers and the login process

Norce Commerce can help when developing login functionality in your front end application. It is not a full-fledged identity provider but has the ability to store and manage accounts and validate passwords for you. The accounts in Norce will be associated with the Customer object that can be used for segmentation and specific pricing or offers.

Note

When using other solutions than Norce's for the authentication functionality, there are other aspects to think about, you won't need any accounts in Norce, instead you just look up the correct customer in Norce after the login process is finished.

Use the email address or CustomerCode to match an identity from the third party authenticator.

But this document explains the common scenarios for handling the account information in Norce Commerce Services.

The full Customer Schema

An 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>

Create Customer and Account

Create Account

  1. A user choice or action starts the scenario, either from a checkout process or a decision to become a "member" in the site.
  2. After asking for personal information the back-end calls Norce Commerce´s Customer Service method RegisterCustomer3. Norce takes a password string, a Customer object with an Account object (otherwise only the customer is created and no account functionality is possible) and returns a new Customer object with Account and its internal Id that should be stored in a persisted way.
  3. And a recommended practice is to send a confirmation email at this stage to the customer, or use Norce's build in functionality for that.
Note

Norce does not validate password strength, this must be done in the backend.

Technical Note

Passwords are stored in Norce after being securely hashed to ensure they cannot be recognized.

Login

Login

To login, you only need to call the LoginPost method in Norce Commerce, passing in accountname and password. If credentials is correct a Customer object is returned the user is authenticated and can be added to the session and saved to a cookie. Otherwise, you get an exception. See api Reference documentation on the supported errors.

Re-visit the site

(already logged in)

Revisit

When a user revisits a site where they are already logged in, and the accountId is saved in a persisted way, for example in a cookie.

Resolve the accountId from the cookie and call GetCustomerByAccountId. The method returns a Customer object if everything is alright and null if the account has been disabled or does not exist.

Forgotten password

(or change a password)

Forgotten password

  1. When the user has forgotten their password they visit a page where they provide some account information. It could be the email address, customer code or the login name.
  2. The back-end calls Norce Commerce Customer Service to fetch the Customer object. Use GetCustomerByEmail, GetCustomerByCode or GetCustomerByLoginName depending on what data provided by the user. The methods will return the Customer object, if it exists and is not disabled.
  3. Your backend generate an email with an encrypted link, containing account name and a valid to timestamp (determines how long will the link be valid)
  4. The user click on the link in the email and lands on a page where the link information is decrypted and validated. The user is asked for a new password.
  5. The backend calls Norce and updates the password. The method is UpdateCustomer3. Pass in the Customer object and the new password string.
Note

Customer emails must be unique in Norce. It is not allowed to have two customers with the same email within the same client or application. (There is a setting to define the scope of uniqueness: customers can either be shared across all applications or be unique within each application.)

When sending out a link to the user where they can change their password, there is a recommended practice to encrypt the information so that it is not sent as clear texts. Here is an example of how it could look in C#.

Here is an example of how it could look in C#.

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;
    }
}

Suggested further reading