function (user, context, callback) { // Require the Node.js packages that we are going to use. // Check this website for a complete list of the packages available: // https://auth0-extensions.github.io/canirequire/ var rp = require('request-promise'); var uuidv4 = require('uuid'); // The name of your Active Directory connection (if using one) var AUTH0_AD_CONNECTION = 'Travel0AD'; // The client_id of your Office 365 SSO integration // You can get it from the URL when editing the SSO integration, // it will look like // https://manage.auth0.com/#/externalapps/{the_client_id}/settings var AUTH0_OFFICE365_CLIENT_ID = configuration.AUTH0_OFFICE365_CLIENT_ID; // The main domain of our company. var YOUR_COMPANY_DOMAIN = 'mycompanyurl.com'; // Your Azure AD domain. var AAD_DOMAIN = configuration.AAD_DOMAIN; // The Application ID generated while creating the Azure AD app. var AAD_APPLICATION_ID = configuration.AAD_APPLICATION_ID; // The generated API key for the Azure AD app. var AAD_APPLICATION_API_KEY = configuration.AAD_APPLICATION_API_KEY; // The location of the users that are going to access Microsoft products. var AAD_USAGE_LOCATION = 'US'; // Azure AD doesn't recognize the user instantly, it needs a few seconds var AAD_USER_CREATE_DELAY = 15000; // The key that represents the license that we want to give the new user. // Take a look in the following URL for a list of the existing licenses: // https://gist.github.com/Lillecarl/3c4727e6dcd1334467e0 var OFFICE365_KEY = 'O365_BUSINESS'; // Only execute this rule for the Office 365 SSO integration. if (context.clientID !== AUTH0_OFFICE365_CLIENT_ID) { return callback(null, user, context); } // Skip custom provisioning for AD users. if (context.connection === AUTH0_AD_CONNECTION) { return callback(null, user, context); } // If the user is already provisioned on Microsoft AD, we skip // the rest of this rule user.app_metadata = user.app_metadata || {}; if (user.app_metadata.office365Provisioned) { return connectWithUser(); } // Global variables that we will use in the different steps while // provisioning a new user. var token; var userPrincipalName; var mailNickname = user.email.split('@')[0]; var uuid = uuidv4.v4(); var immutableId = new Buffer(uuid).toString('base64'); var userId; // All the steps performed to provision new Microsoft AD users. // The definition of each function are below. getAzureADToken() .then(createAzureADUser) .then(getAvailableLicenses) .then(assignOffice365License) .then(saveUserMetadata) .then(waitCreateDelay) .then(connectWithUser) .catch(callback); // Requests an Access Token to interact with Windows Graph API. function getAzureADToken() { var options = { method: 'POST', url: 'https://login.windows.net/' + AAD_DOMAIN + '/oauth2/token?api-version=1.5', headers: { 'Content-type': 'application/json', }, json: true, form: { client_id: AAD_APPLICATION_ID, client_secret: AAD_APPLICATION_API_KEY, grant_type: 'client_credentials', resource: 'https://graph.windows.net' }, }; return rp(options); } // Gets the Access Token requested above and assembles a new request // to provision the new Microsoft AD user. function createAzureADUser(response) { token = response.access_token; userPrincipalName = 'auth0-' + uuid + '@' + YOUR_COMPANY_DOMAIN; var options = { url: 'https://graph.windows.net/' + AAD_DOMAIN + '/users?api-version=1.6', headers: { 'Content-type': 'application/json', 'Authorization': 'Bearer ' + token }, json: true, body: { accountEnabled: true, displayName: user.nickname, mailNickname: mailNickname, userPrincipalName: userPrincipalName, passwordProfile: { password: immutableId, forceChangePasswordNextLogin: false }, immutableId: immutableId, usageLocation: AAD_USAGE_LOCATION }, }; return rp(options); } // After provisioning the user, we issue a request to get the list // of available Microsoft products licenses. function getAvailableLicenses(response) { userId = response.objectId; var options = { url: 'https://graph.windows.net/' + AAD_DOMAIN + '/subscribedSkus?api-version=1.6', json: true, headers: { 'Content-type': 'application/json', 'Authorization': 'Bearer ' + token } }; return rp(options); } // With the licenses list, we iterate over it to get the id (skuId) of the // license that we want to give to the new user (office 365 in this case). // We also issue a new request to the Graph API to tie the user and the // license together. function assignOffice365License(response) { var office365License; for (var i = 0; i < response.value.length; i++) { if (response.value[i].skuPartNumber === OFFICE365_KEY) { office365License = response.value[i].skuId; break; } } var options = { url: ' https://graph.windows.net/' + AAD_DOMAIN + '/users/' + userId + '/assignLicense?api-version=1.6', headers: { 'Content-type': 'application/json', 'Authorization': 'Bearer ' + token }, json: true, body: { 'addLicenses': [ { 'disabledPlans': [], 'skuId': office365License } ], 'removeLicenses': [] } }; return rp(options); } // After provisioning the user and giving a license to them, we record // (on Auth) that this Google Workspace user has already been provisioned. We // also record the user's principal username and immutableId to properly // redirect them on future logins. function saveUserMetadata() { user.app_metadata = user.app_metadata || {}; user.app_metadata.office365Provisioned = true; user.app_metadata.office365UPN = userPrincipalName; user.app_metadata.office365ImmutableId = immutableId; return auth0.users.updateAppMetadata(user.user_id, user.app_metadata); } // As mentioned, Windows Graph API needs around 10 seconds to finish // provisioning new users (even though it returns ok straight away) function waitCreateDelay() { return new Promise(function (resolve) { setTimeout(function() { resolve(); }, AAD_USER_CREATE_DELAY); }); } // Adds the principal username and immutableId to the user object and ends // the rule. function connectWithUser() { user.upn = user.app_metadata.office365UPN; user.inmutableid = user.app_metadata.office365ImmutableId; return callback(null, user, context); }}