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

  1. Next, click on Certificates and Secrets
  2. 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
  3. Next, click on API permissions, and then Add a permission
  4. Select Dynamics CRM as shown below:
  1. Next, search for “user_impersonation”, and then click add permissions as shown below:
  1. 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.

Create a 'Before registration' hook from the Microsoft Dynamics 365 plugin

Our plugin library contains an off-the-shelf Microsoft Dynamics 365 event hook template that jumpstarts your integration process and allows you to customize it to your needs.

  1. In the Admin Console, go to Lifecycle Event Hooks.
  2. Click ' Add plugin'. You will be redirected to the plugin library.
  3. Click on the Microsoft Dynamics 365 logo. You can find it in the CRM section.
  4. There will be a pre-select for the event hook, so you only need to click 'Add'.
  5. Wait for the Microsoft Dynamics 365 hook template to be added.

🚧

If the Admin Console doesn't want to add the hook, it's most likely that the name of the hook is already taken. Click 'Edit' and you can modify the name of the event hook. Then continue with 'Try again'.

  1. If the hook has been successfully added, you can return to the list view with 'Back to plugin library', then 'Back to event hooks'.

You can add the following to the event hook plugin:

  • 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
 */