Microsoft Dynamics 365

Learn how to integrate your Microsoft Dynamics instance with Strivacity, allowing Strivacity to add, update and maintain customer information and use it as a source of entitlement.

Integrating Strivacity with Microsoft Dynamics provides the following benefits:

  • Use Microsoft Dynamics customer information as a source of entitlement during customer registration, login, or any step within the customer account lifecycle
  • Create new customer records, and synchronize/update those customer records with the latest customer account information (from Strivacity)
  • If required, migrate customer information from Microsoft Dynamics to Strivacity upon account registration

They're two steps to setting up Strivacity with Microsoft Dynamics 365:

1) Setup Microsoft Power Platform to register Strivacity as an Application and define API permissions for the application, and enable public client access

2) Setup a pre-registration hook within Strivacity

1) Setup Microsoft Power Platform to register Strivacity as an Application

1) From within the Azure Active Directory Dashboard, click on App registrations, and click + New registration

2) Define a name for your new application

3) Choose Accounts in any organizational directory (Any Azure AD directory - Multitenant)

4) Click the Register button. Your newly created application should be registered as shown in the screenshot below.

📘

Make a record of the ClientID so that you can use it at a later stage of the integration.

5) Next, click on Certificates and Secrets

6) Click on New client secret and copy it to a safe location for use later in this integration. Note you will not be able to copy the secret again later

7) Next, click on API permissions, and then Add a permission

8) Select Dynamics CRM as shown below:

9) Next, search for “user_impersonation”, and then click add permissions as shown below:

10) Now click on Manifest, and set allowPublicClient to true, as shown highlighted below:

This completes the initial configuration within Microsoft Dynamics 365 for use with Strivacity.

2) Setup a pre-registration hook within Strivacity

1) Log into the Strivacity Admin Console using an Admin role, or a role that has delegated access to create a Lifecycle Event Hook.

Using the pre-registration hook code below, do the following:

  • Define DYNAMICS_AUTHORITY_URL, DYNAMICS_RESOURCE, DYNAMICS_CLIENT_ID, and DYNAMICS_CLIENT_SECRET you noted from step 1 above.

This will search Microsoft Dynamics 365 for a contact matching the primary email address

Now add any additional logic as you see fit!

var dynamics = require('dynamics-web-api');
var AuthenticationContext = require('adal-node').AuthenticationContext;

// dynamics config
var DYNAMICS_AUTHORITY_URL = 'https://login.microsoftonline.com/<YOUR TENANT>/oauth2/token';
var DYNAMICS_RESOURCE = 'https://<YOUR DYNAMICS INSTANCE>.crm.dynamics.com/';
var DYNAMICS_CLIENT_ID = '<CLIENT ID>';
var DYNAMICS_CLIENT_SECRET = '<CLIENT SECRET>'

// get an adal context for use in auth
var adalContext = new AuthenticationContext(DYNAMICS_AUTHORITY_URL);

/** This function will be called from the Pre registration hook in a blocking manner.
 *
 * @param {Object}   args                                         Input arguments
 * @param {Object}   args.application                             Application related information
 * @param {string}   args.application.name                        Name
 * @param {string}   args.application.client_id                   OAuth client ID
 *
 * @param {Object}   args.oidc_context                            Information about the originating OpenID Connect request
 * @param {string[]} args.oidc_context.acr_values                 ACR values
 * @param {string[]} args.oidc_context.ui_locales                 UI locales
 *
 * @param {Object}   args.customer                                Customer related information
 * @param {string}   args.customer.ip_address                     HTTP client IP coming from the X-Forwarded-For header
 * @param {string}   args.customer.store                          Userstore
 * @param {Object}   args.customer.attributes                     User attributes provided during the registration
 * @param {Object}   args.customer.identifiers                    User identifiers provided during the registration
 * @param {Object[]} args.customer.consents                       User consents accepted during the registration
 * @param {string}   args.customer.location.city                  City of the customer
 * @param {string}   args.customer.location.state                 State of the customer
 * @param {string}   args.customer.location.country               Country of the customer
 * @param {string}   args.customer.location.country_code          Country code of the customer
 * @param {string}   args.customer.location.coordinates.latitude  Latitude coordinate of the customer
 * @param {string}   args.customer.location.coordinates.longitude Longitude coordinate of the customer
 *
 * @param {Object}   args.session                                 Session store
 * @param {Object}   args.continue_context                        Continue context
 *
 * @param {Object}   args.continue_request_parameters               Continue request parameters
 * @param {string}   args.continue_request_parameters.callback_url  Callback url to use after a continue call
 * @param {string}   args.continue_request_parameters.state         State parameter to use after a continue call
 * @param {preRegistrationCallback} callback
 * @param {denyRequestCallback} error
 */
module.exports = async function ({ application, oidc_context, customer, session, continue_context, continue_request_parameters }, callback, error) {
    console.log(customer)
    
    var client = new dynamics({
    	webApiUrl: DYNAMICS_RESOURCE + 'api/data/v9.1/',
    	onTokenRefresh: acquireToken
	});

    var records = await client.retrieveMultiple("contacts", ["fullname"], `emailaddress1 eq '${customer.attributes.emails.primaryEmail}'`)
    console.log(records)

	// the following function call does not modify the registering user
	callback(new RegistrationData(customer.attributes, [], session));
};

/**
 * Callback for acquiring a token via ADAL
 */
function acquireToken(callback) {
    function adalcb(error, token) {
        if (!error) {
            callback(token);
        } else {
            throw new Error(error)
        }
    }

    // get the token
    adalContext.acquireTokenWithClientCredentials(DYNAMICS_RESOURCE, DYNAMICS_CLIENT_ID, DYNAMICS_CLIENT_SECRET, adalcb);
}

/** Allow registration
 *
 * @callback preRegistrationCallback
 * @param {RegistrationData|RedirectRequest|ShowErrorMessage} token
 */

/** Deny registration
 *
 * @callback denyRequestCallback
 * @param {ErrorDenyRequest} error
 */

/** RegistrationData */
class RegistrationData {
	attributes = {}
	additionalAuthenticators = []
	session = {}

	/**
	 * @constructor
	 * @param {Object} attributes
	 * @param {AdditionalAuthenticator[]} additionalAuthenticators
	 * @param {Object} session
	 */
	constructor(attributes, additionalAuthenticators, session) {
		this.attributes = attributes;
		this.additionalAuthenticators = additionalAuthenticators;
		this.session = session;
	}
}

/** AdditionalAuthenticator */
class AdditionalAuthenticator {
	type = null;
	target = null;

	/**
	 * @constructor
	 * @param {"email"|"phone"} type
	 * @param {string} target
	 */
	constructor(type, target) {
		this.type = type;
		this.target = target;
	}
}

/** RedirectRequest is a global object
 * @constructor
 * @param {string} redirect_url
 * @param {Object} session
 */

/** ShowErrorMessage is a global object
 * @constructor
 * @param {string} error_message
 * @param {Object} session
 */

/** ErrorDenyRequest is a global object
 * @constructor
 * @param {string} description
 * @param {string} hint
 */