Synchronize users from Azure AD to WordPress
WPO365's Azure AD based User synchronization will help you to automate a number of otherwise error-prone and cumbersome tasks, for example:
- Creating new WordPress user accounts when onboarding users.
- Keeping existing WordPress user accounts up-to-date.
- Deleting or de-activating of WordPress user accounts when offboarding users.
When WPO365 creates / updates users, you can also configure it to enhance WordPress users.
- Roles + Access Assign WordPress roles based on the user’s Azure AD group memberships
- Custom User Fields Synchronize advanced user attributes from Azure AD e.g. mobile phone, employee ID
- Avatar Update a user’s WordPress avatar with the user’s Azure AD profile picture
Before you start
- You have installed the WPO365 | SYNC, WPO365 | INTRANET or WPO365 | CUSTOMERS bundle (plus the WPO365 | LOGIN plugin that provides the basic functionality needed for either bundle to operate as expected).
- You are a Global Administrator for your company’s Office 365 tenant / Azure AD directory (or have at least the ability to create and / or update Azure Active Directory App registrations).
- You are an Administrator for your WordPress website.
- You already configured Single Sign-on by following the steps documented in this article.
- You already configure the Integration portion by following the steps in this article.
Please check out this video https://youtu.be/kAmixcyvJCo for a video tutorial for this article.
Support for Azure AD B2C / Microsoft Entra External ID
If you are looking to synchronize users from Azure AD B2C or Microsoft Entra External ID we strongly recommend that you install the WPO365 | CUSTOMERS bundle.
Assuming that you already configure Azure AD B2C based Single Sign-on (SSO) for WordPress, you must update the corresponding App registration in Azure AD B2C by performing the following steps.
- Navigate to Azure Portal > Azure AD B2C > App registrations and select the application that you created to register the WordPress website.
- Continue to the API Permissions page.
- Click + Add a permission.
- Select Microsoft Graph > Application permissions > User.Read.All.
- Click Add permissions.
- Click Grant admin consent for ...
Then return to the plugin's Integration page.
- Click Use app-only token.
- Check Use existing App registration From the Single Sign-on tab.
- Click Save configuration.
With this configuration in place, you should be able to perform the steps in this article and configure user synchronization for Azure AD B2C.
How it works
Even when you are in a hurry, you should read this paragraph. It gives you a high level understanding, of how the WPO365 plugin works and how it decides what users it should (soft) delete, once a job completes successfully.
The WPO365 user synchronization feature helps you to create a job that will synchronize users. This job can be started in one of the following ways.
- Manually, by clicking Save + Run now.
- Scheduled, by selecting a (WordPress CRON based) schedule e.g. once per day at hh:mm and then clicking Save + Schedule.
- External link, by copying the link and call it from an external script / task service.
When a job is triggered, the plugin will retrieve a first batch of users from Microsoft Graph. To retrieve users from Microsoft Graph, the plugin needs a query. A query is a request, sent to Microsoft Graph, that will return users that match with the query's $filter parameter e.g. accountEnabled eq true. The plugin also expects the query to define at least two other parameters: $count (the total number of results, so that the plugin can track progress) and $top. A query can be as simple as users to get all users in the tenant. Or you can opt for a more complex query, to return all users in your organization that can currently sign in, as follows:
myorganization/users?$filter=accountEnabled eq true&$top=10&$count=true.
To prevent the plugin from taking too long to process all users, the size of a batch of users returned should be pretty small e.g. 10 users. If the batch is too big, the server will experience a time-out error and processing will stop. The size of the batch is defined by the $top parameter, e.g. $top=10.
After the plugin processed the first batch, it prepares itself for the next batch by creating a new WordPress CRON job. This job will be processed the next time that WordPress CRON is checking for jobs to execute. Since WordPress CRON is not very reliable, we strongly advise you to put measures in place that improve the reliability (see https://docs.wpo365.com/article/135-hooking-wp-cron-into-a-task-scheduling-service for details). The plugin will keep creating batches, until all users are retrieved from Microsoft Graph (or until an error occurs, in which case the job is stopped).
When the plugin processes a batch of users, it performs the following actions:
- It tries to look up the user in WordPress by Azure AD Object ID, username, email address or (optionally, not by default enabled) short login name (e.g. johnd for a user with Azure AD user name email@example.com).
- If it finds the corresponding WordPress user, it will update that user, if you configured the job to update users. Updating a user includes updating any automatically assigned roles, custom user attributes e.g. jobTitle or employeeID etc. and the WordPress avatar. Always when the plugin updates a user, it will tag the user with its Azure AD Object (OID).
- If it does not find a corresponding WordPress user, it will create a new user if you configured the job to create users. As soon as a new WordPress user is created, it will update that user accordingly.
- Finally, all WordPress users that the plugin finds and creates will be tagged (even if you did not check the option to update users in WordPress). This way, the plugin can tell, after the job completes eventually, which users were affected by it.
If the plugin has finished processing all users, it will complete the job by handling all WordPress users that were not affected by it. Starting with version 21 of the plugin, you can choose how the plugin decides what users should be (soft) deleted:
- If you configure the job so that it should not Skip (the) domain check, the plugin will check for each WordPress user the username and email address and if it has a domain suffix e.g. @domain.tld that matches with a custom domain that you entered into the Custom domains list on the plugin's User registration page. If a WordPress user appears to belong to any of the custom domains that you configured and if the user is not affected by the current job, then the plugin will (soft) delete the user. This was the default behavior until version 21.
- Opposite, if you choose to Skip (the) domain check, the plugin will select all WordPress users that have a tag with their Azure AD Object ID (OID) and that are not affected by the current job. All those users will then be (soft) deleted.
Important If you choose to skip the domain check, then on the first run of WPO365 you should
Failing to do so may cause WPO365 User synchronization to (soft) delete users that have not yet been tagged by WPO365 with their Azure AD Object ID.
To start synchronizing users it is important to ensure the correct configuration of the plugin. The table below summarizes all configuration options and their recommended value.
The correct configuration can be verified using the plugin's built-in self-test tool. To self-test the plugin go to WP Admin > WPO365 > Plugin self-test and click to Start self-test. The self-test offers to fix most of the issues for you. However, some options require you to copy data from Azure Active Directory.
|Enable user sync||User sync||Checked||Replace|
|Pages freed from authentication||Single Sign-on||/wp-json/wpo365/v1/||Add|
| Pages freed from authentication
|Prevent WordPress to send "email changed" emails||Miscellaneous||Checked||Replace|
|Custom Domains *||User registration||See Azure Portal||Add|
* The Custom domains list needs to be up-to-date with all possible login-domains if you configure WPO365 User synchronization so it won't skip the domain check.
Please note Starting with version 24.0 WPO365 no longer requires it REST API for Microsoft Graph to be enabled and configured. Those steps have therefore been removed from this guide.
It is recommend that you configure so-called application-level permissions to retrieve users from Microsoft's unified API Microsoft Graph.
Perform the following steps to assign application-level permissions.
- Go to the plugin's Single Sign-on page and click the link View in Azure Portal in front of the Application (Client) ID setting. This will take you to the registered application in Azure AD.
Please note If you didn't configure OpenID Connect based Single Sign-on, then you probably did not yet registered your application in Azure AD / Entra. Please refer to the following article for steps to create the necessary App registration.
- Continue to the App registration's API Permissions page and add the following permissions:
- Microsoft Graph > Application permissions > User.Read.All
- Microsoft Graph > Application permissions > GroupMember.Read.All (optional*)
- Once you added those permissions, you must Grant admin consent for your tenant and ensure that the status for the newly added permissions is Granted.
- Return to the plugin's Integration configuration page and check the option Use app-only token.
- Also check the option Use existing App registration From the Single Sign-on tab (assuming that you have added the permissions to the application that you registered to enable Microsoft based Single Sign-on).
- Scroll down to the bottom of the page and click Save configuration.
* Granting GroupMember.Read.All permissions is not optional if you intend to implement Azure AD group based access restrictions and / or want to dynamically assign WordPress roles based on Azure AD groups.
Important Version 24.0 still tests whether the WPO365 REST API for Microsoft has been enabled and configured. However, this configuration is no longer necessary and therefore any errors must be ignored. This will be fixed with the next minor patch / update.
At this point the plugin should be able to synchronize users. Perform the following steps to test your configuration.
- Navigate to WP Admin > WPO365 > Plugin self-test and click to Start self-test.
- Carefully inspect the section INSTALLED EXTENSIONS and make sure you are using the latest version of the WPO365 | SYNC, WPO365 | INTRANET or WPO365 | CUSTOMERS bundle.
- Scroll down to CONFIGURATION and make sure all but the following two tests are passed successfully:
- Debug log disabled It may even be helpful to enable the debug log in the beginning.
- Domain hint configured This configuration is not important for successfully synchronizing users.
- Continue scrolling so SYNC and make sure that all tests are passed successfully. You can click the link view in the Data column to see the test data retrieved by the plugin.
Please note At this point you'll may notice that the test WP-Cron is disabled did not pass successfully. Since User synchronization heavily relies on WP-Cron and WP-Cron not runs continuously it is strongly recommended that you hook WP-Cron into a n external task scheduling service. You can follow the steps explained in this article to achieve this.
Adding a new User synchronization job
Now that the plugin is configured for user synchronization you must add at least one User synchronization job. To do so go to WP Admin > WPO365 > User sync and click Add new job.
Perform the following steps to configure a User synchronization job.
- Give your job a meaning full name e.g. ALL INTERNAL EMPLOYEES or WEBSITE ADMINS.
- Update the query so it selects exactly those users that you want to synchronize to your WordPress website. You can click Show sample queries to see examples of such queries. Each time when you change your query you must test the query. You cannot save the job if the query is not tested successfully.
Please note that you can also test your query with the help of Microsoft's Graph Explorer.
The default query filters for userType eq 'member' but when you synchronize users from Active Directory (on-premise) the userType may actually be null and the query does not return any users.
You may notice that the plugin automatically adds a $count= parameter which you should not remove. The count (= total rows) helps the plugin being able to track progress
If you customize the query you must include the $top= query parameter e.g. $top=10 or $top=20. The $top parameter defines the page size and will force Microsoft Graph to return the full result in pages. The user sync processor is aware of such pages and after processing a result page will request the next page until there are no pages left. Omitting the query parameter or defining pages with too many results may cause a time-out error and the user sync job will not be completed as expected.
The plugin automatically detects a Microsoft Graph $count query and automatically adds the ConsistencyLevel = True header and thus allowing for advanced queries with $filter using endsWith and $search (see this article if you want to build advanced queries).This means that you can write a User sync query that includes all users from a specific domain as follows: myorganization/users?$count=true&$filter=endsWith(userPrincipalName,%firstname.lastname@example.org%27)&$top=10.
- Select the actions that should be performed when users are being synchronized e.g.
Please note that if you do not check any of the options then user synchronization will simply log / preview the action it would have otherwise applied (see the next paragraph on monitoring progress. When you configure user synchronization for the first time it is strongly recommended that you preview the results by leaving any of the actions unchecked.
- Create new users.
- Update (custom user fields and / or dynamically assigned WordPress roles of) existing users.
- Delete users.
- Soft-delete users (only available if you previously selected to delete users).
- Re-assign posts to (only available if you previously unchecked the option to soft-delete users and please note that you must have checked the user successfully to be able to save the job).
WordPress users are deleted at the end of a user synchronization job. The user synchronization processor then analyzes all WordPress users that were not affected by the job that is about to complete and for each of those users checks whether its user name or email address has a domain component that matches with any of the domains in the Custom domains list on the plugin's User registration configuration page.
- Decide whether or not to ignore external identities (= Azure AD guest users) that may be included in the query result.
- Select a trigger e.g.
- Run manually by clicking the Save + Run now button at the bottom of the job's form.
- External link which you can use to externally start user synchronization e.g. when you want to use an external scheduler. This is especially useful if you want to run the job more than once per day, which is not possible if you select WordPress Cron.
- WordPress Cron which allows you to define a schedule e.g. once per day at a specific time.
- Decide whether you want to send a log i.e. a summary each time the job completes. If you check this option, you'll be able to configure a specific email address where the log will be sent to. Also, since version 24.0, an option to only send the log in case of errors or failure has been added.
Once a user synchronization job is running or has finished you can monitor progress as shown below.
After a user synchronization job has finished you click the View results button at the bottom of the job's form.
The table with results provides you with the following information:
- All WordPress users.
- WordPress users that have been created during the last run.
- WordPress users that have been updated during the last run.
- WordPress users that have been earmarked as deactivated (= soft-deleted) during the last run.
- WordPress users that have been deleted during the last run.
- Azure AD users that are not synchronized due to an error during the last run.
- Logged Azure AD users that otherwise would have been created or updated or (soft-deleted) but none of those actions had been activated.
- The external link trigger may not work as expected if caching is enabled for your website. In that case the link will work for the very first time or each time after your purged / refreshed the cache. In such a case you should add a dynamic parameter to the URL with a value that is dynamically generated to break the cache.