Skip to content

Authorization scopes

An app registration in Azure only hands out the data it has permission for, and the connector only asks for the scopes it knows about. The base module and the submodules register the scopes they need themselves. When your own code calls an endpoint that none of them cover, you have to add the scope.

There are two places to do that, and you generally need both.

On the connector

For a one-off, type the scope into the Authorization scopes field on the connector at /admin/config/system/o365/settings/o365-connectors. It is a space separated list. This is configuration, so it exports with the rest of your config and travels between environments.

In code

For a scope that belongs to a module, so that enabling the module is enough and nobody has to remember to edit a field, implement hook_o365_auth_scopes():

<?php

use Drupal\o365\O365ConnectorInterface;

/**
 * Implements hook_o365_auth_scopes().
 */
function my_module_o365_auth_scopes(array &$scopes, ?O365ConnectorInterface $connector) {
  $scopes[] = 'Group.Read.All';
}

The second argument is the connector the scopes are being collected for. Use it when a scope should only be requested for one tenant:

function my_module_o365_auth_scopes(array &$scopes, ?O365ConnectorInterface $connector) {
  $scopes[] = 'Group.Read.All';

  if ($connector && $connector->id() === 'intranet') {
    $scopes[] = 'Sites.Read.All';
  }
}

It can be NULL, so check before you call anything on it. And an unsaved connector has no ID yet, which the $connector && above already covers but the id() comparison would not.

You do not have to worry about duplicates or about offline_access. \Drupal\o365\HelperService::getAuthScopes() runs the collected list through array_unique(), drops empty values, and adds offline_access if it is not there, because the connector cannot refresh its token without it.

The declaration in o365.api.php is the reference for the signature.

Then grant it in Azure

Adding a scope only changes what Drupal asks for. If the app registration does not have the matching delegated Microsoft Graph permission, the authorization request fails or the token comes back without the scope, and the call returns a 403 that does not mention scopes at all.

After adding a scope, open /admin/reports/o365-auth-scopes. That page shows the full list per connector, which is what you copy into API permissions in Azure. Some scopes, Group.Read.All and Sites.Read.All among them, need an administrator to grant consent for the tenant before anyone can use them.

Log out and back in while testing

Users who logged in before you added the scope keep their old token until it is refreshed. If the new scope seems to be ignored, that is usually why.