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.
- Read more on that in the Norce Commerce user documentation.
- Check out the examples in Postman as well.
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.
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
- A user choice or action starts the scenario, either from a checkout process or a decision to become a "member" in the site.
- After asking for personal information the back-end calls Norce Commerce´s Customer Service method RegisterCustomer3. Norce takes a
password
string, aCustomer
object with anAccount
object (otherwise only the customer is created and no account functionality is possible) and returns a newCustomer
object withAccount
and its internalId
that should be stored in a persisted way. - 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.
Norce does not validate password strength, this must be done in the backend.
Passwords are stored in Norce after being securely hashed to ensure they cannot be recognized.
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)
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)
- 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.
- 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 theCustomer
object, if it exists and is not disabled. - 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)
- 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.
- The backend calls Norce and updates the password. The method is UpdateCustomer3. Pass in the
Customer
object and the newpassword
string.
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.)
Notification with a temporary link
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; } }