Application Access Policies in Exchange Online
The Microsoft Graph has been around for a while now and is slowly turning into the de-facto standard API for any Office 365 developer, including those focused on Exchange Online. Microsoft has already announced plans to stop any feature development for the EWS API and focus on the Graph instead. While EWS will continue working for the foreseeable future, going forward you will have to switch to the Graph, so understanding how to control it is vital.
A not so short introduction to the Graph API
Switching to the Graph comes with somewhat of a learning curve. It’s beyond the scope of the article to give you a complete introduction, but in order to better understand the examples shown here, you will be required to have at least a basic understanding of the concepts behind OAuth, OIDC, application registration, permission roles and scopes, consent, access tokens and so on. Microsoft has published extensive documentation on all these topics and this article is a good starting point.
To give you the TL;DR version – the Graph allows you to perform various actions against different resources in Office 365, such as creating a message, deleting a file, or accessing a report. You can run those in the context of a given user, or as a background service. The authentication process is performed against the Microsoft Identity platform by means of obtaining tokens, which are then presented to the workload against which you want to perform a given operation. For authentication to happen, the app must be recognized by the Microsoft Identity platform, in other words an application registration must be performed by the developer first. To control which actions a given application can perform, the developer describes the permissions needed by the app and you, or an admin in the tenant, has to grant (“consent” to) the permissions.
Here lies one of the problems when using the Graph. As it tries to cover quite diverse set of resources, the permissions model used by the Graph doesn’t go into the peculiarities of each individual workload. Instead, the permission scopes introduced only cover the basic scenarios: access only your own resources, access resources shared with you, access all resources. For applications running in the scope of a given user, the so-called delegated permissions model, this is rarely a problem, as the workload can trim those permissions to cover just the resources the user has access to. For the so-called application permissions however, where the app runs without a signed in user, access is given to any and all resources.
To give a specific example, the Files.ReadWrite.All delegated permission will allow an application to perform all operations against all files a given user has access to. The permissions given to the user across SharePoint Online and OneDrive for Business site collections will be honored, so the user cannot just go a bulk-delete all files in another person’s OneDrive, unless he has been explicitly granted access to it. On the other hand, the Files.ReadWrite.All application permission will allow an application to perform all operations against all files in the tenant, period. While these permissions are only needed for a small set of operations and can only be granted by an administrator, every app out there can simply request them, and the fact that by default end users are allowed to add such apps to the directory doesn’t help either. In effect, it takes a single misplaced click to grant an app full access to all resources in your organization, and we have already seen bad actors exploiting this in Office 365.
Restricting Graph API calls via Application Access Policies
After this introduction, let’s talk about a cool new feature that allows us to mitigate threats and scope down the permissions given to only a subset of the mailboxes in our tenant. The feature is called Application Access Policies and, in a nutshell, represents a list of mailboxes a given application is allowed to run calls against. It is important to understand that this is a workload-specific feature, not a Graph one, the workload in question of course being Exchange Online. When Exchange Online receives a request to execute an operation from any of the Graph endpoints it exposes, the appID included as part of the access token, which in turn is included in the request headers, is checked against the Application Access Policies list and only allowed to execute against mailboxes included in the policy scope.
Let’s dig into some specifics. Consider an app registration in Azure AD for NewApp, to which I rather generously added the entire set of permissions available for Exchange Online, as illustrated on the screenshot below. Those include things like being able to access all items in all mailboxes in the tenant, change settings for any mailbox, being able to send messages as any user, etc. And since this is an web app type of application, it runs without a user context, and anyone that can run operations via this application will have practically unrestricted access to all mailboxes in the company, regardless of any roles he or she holds inside Office 365.
As an example of the things one can do with access to said application, here’s a PowerShell code snippet that gets a token for the NewApp application, then uses it to get a list of Inbox rules for one of the mailboxes in the company. The same example can be used against any mailbox thanks to the permissions granted on the app.
So how do we go about restricting this? As mentioned above, Applications Access Policies enforce a list of objects against which we can use a given application. For this specific example, we can configure an Application Access Policy that limits the NewApp application to just a subset of the mailboxes in the tenant, or similarly, prevents it from running against the most sensitive ones. Apart from the application identifier, we need to specify a group object, members of which will populate the restricted access list. Once we have all the needed information, we can run the New-ApplicationAccessPolicy cmdlet:
The moment the first policy is created, it becomes active almost immediately, as the restrictions are enforced directly at the Exchange layer. If we now try to run the same query against the Graph API, but for one of the mailboxes under the scope of the newly created policy, we would get the following:
The (403) Forbidden error can be generated due to variety of reasons, so if we want to be absolutely certain that the restrictions of the newly created policy apply, we will have to get the full server response. This is not as straightforward to do in PowerShell, but the following code snippet should help:
And there we have it – the “Access to OData is disabled.” error is thrown, as detailed in the documentation. In effect, the “NewApp” application is now restricted and cannot act against any of the mailboxes that are members of the “USG” group we specified when creating the policy. This is because we used the DenyAccess value for the –AccessRights parameter, which means that every request for accessing any of the members of the group will be denied. Alternatively, one can use RestrictAccess, which is an inclusion type of policy – the API calls will only be allowed to run against mailboxes that are members of the group.
Additional information about Application Access Policies
Here’s probably the correct place to detail the rules of applying Application Restriction Policies:
- The DenyAccess action has priority over the RestrictAccess action.
- If a DenyAccess policy exists for given Application and Target Mailbox pair, the app’s access request is denied.
- If a RestrictAccess policy exists for given Application and Target Mailbox pair, the app’s access request is granted.
- If a RestrictAccess policies exists for given Application, but does not match a Target Mailbox, the app’s access request is denied.
- If none of the above conditions are met, then the application is granted access to the requested target mailbox.
So, in a nutshell, for any given application you can create a policy with either a DenyAccess (exclusive filtering) action, or RestrictAccess (inclusive filtering), with the former having precedence. If a mailbox does not match any restrict policy, no restrictions are applied and thus the default level of access is not changed unless you create a policy. You can also create a policy with asterisk (“*”) used as the AppId – such catch-all policy will apply to all applications trying to run API calls against Exchange Online and is a great feature to have.
Let’s also look at the other cmdlets used for working with Application Restriction Policies. The Get-ApplicationAccessPolicy cmdlet can be used to list the policies configured in the tenant. As shown below, the Identity of each policy is constituted of the tenant identifier followed by the “\” char, the application id, a colon (“:”), the SID of the group, a semicolon (“;”) and the objectID of the group.
Although a Set-ApplicationAccessPolicy cmdlet exists, it can only be used to change the Description of a policy, so it’s mostly useless. Remove-ApplicationAccessPolicy can be used to delete any existing policies, and the Test-ApplicationAccessPolicy can be used to make sure the resultant set of policies configured for a given app will produce the desired outcome when applied to a given mailbox or group of mailboxes.
Some issues and limitations
Lastly, let’s also mention few issues you might run into when playing with application access policies. Despite what the documentation claims, you cannot use a regular DG or a dynamic DG for the –PolicyScopeGroupId parameter, only mail-enabled security groups are accepted. It’s also unfortunate that you cannot disable a given policy, you have to remove it instead. Speaking of removal, the action will not prompt for confirmation, so make sure you know what you are doing.
You might also experience some delays in applying the policies (about an hour), due to the caching mechanisms employed by Exchange Online for both the group membership and the policy objects. As the restrictions are applied at the Exchange server layer, obtaining a new token will not help speed things up, you have to wait for the policy changes to propagate. The Test–ApplicationAccessPolicy cmdlet will help you validate without having to wait.
It’s also important to understand that not all API calls are currently covered, just the ones included in the following scopes:
- Mail.Read
- Mail.ReadWrite
- Mail.Send
- MailboxSettings.Read
- MailboxSettings.ReadWrite
- Calendars.Read
- Calendars.ReadWrite
- Contacts.Read
- Contacts.ReadWrite
Probably the most important thing is that we cannot apply Application Access Policies against EWS impersonation granted as application permissions. This is unfortunate, as impersonation is one of the most powerful permissions you can have, especially when granted via application permissions (if granted via delegate permissions, we can control impersonation via RBAC scopes).
Overall though, this functionality is a great addition to the service, and something that should have been included from the get go. Kudos to the Exchange team for providing these controls!
Pingback: Microsoft Launches RBAC for Applications for Exchange Online
Pingback: How to Report Meeting Statistics for Room Mailboxes
Pingback: Moving on from Send-MailMessage: Sending Email from PowerShell using the Graph API
Pingback: Using Azure Automation to Detect and Report Microsoft 365 Audit Events
Hello Vasil ,
I have an important Question, Our company has been using the Application access policy for a while now ,and i get an error : “The total size of App Access Policies exceeded the limit of: 87040. Size 87062.” Can you provide a resolution for this.
Can I apply application access policy on a mail enabled security group synced from onprem?
If we scope the Application access Policy to specific mail enabled group and grant access only to the member of the group there are chances for any user id can be added to the group and leverge full access to the mailbox. What level of Security controls can be added to prevent this.
Create a group with closed membership, one that is managed by the IT support team and not end users.
any other way to restrict owner from adding users rather than group managed by IT team?
We are using Exchange online api with Application permission full acess .
Our objective is to restrict api permission to specific set users only .
Do you think there can be any way for that ?
FYI: the MS Exchange Team has enhanced Application Policy that can now be applied to Application permissions.
This is the source: https://techcommunity.microsoft.com/t5/exchange-team-blog/application-access-policy-support-in-ews/ba-p/2110361
I’ve tried and it’s working like a charm.
Thank you very much for this article.
Already covered here mate 🙂
https://www.practical365.com/blog/new-application-access-policies-extend-support-for-more-scenarios/
Is there a limit to how many such application access policy we can have in a tenant? also can we define more than one application in a policy if target group and scope are same?
I’m upgrading a system accessing Exchange calendar API using classic EWS access with impersonation to enumerate calendars and also create new appointments. To do so, I understand I am to create an app registration (The daemon app model) and make it request Calendar.Read.All and Calendar.ReadWrite.All permissions. So far, so good. You write however “we cannot apply Application Access Policies against EWS impersonation granted as application permissions”.
Does this mean, that the impersonation approach is unsupported?
Normally, I would request the Exchange admin to do the following:
New-ManagementScope -Name “My Impersonation Scope” -RecipientRestrictionFilter { RecipientTypeDetails -eq “RoomMailbox” }
New-ManagementRoleAssignment –Name “RoomSync-RoomImpersonation” –Role ApplicationImpersonation –User “serviceaccount@contosa.com” –CustomRecipientWriteScope “My Impersonation Scope”
So if I have a DistributionGroup (room-list) and I cannot make a role-assignment for the application, how does that work? Do I automatically get to access calendars and roomlists without the need for “impersonation”? I ask, since no experts have so far seemed to answer this question clearly on other threads.
If you are using the application permission model for you app (daemon app), you get unrestricted access to impersonate all mailboxes in the company, and you cannot use Application access policies to restrict this. If you are using the delegate permissions model for your app, you need to assign the ApplicationImpersonation role to the user who will be getting an access token. Furthermore, you can create custom management scopes to restrict the impersonation access to specific objects only, or use the Application access policies to do the same.
I am trying to run the New-ApplicationAccessPolicy command in order to scope permissions for mailboxes. I completely followed the steps from links:
https://docs.microsoft.com/en-us/powershell/exchange/exchange-online/connect-to-exchange-online-powershell/connect-to-exchange-online-powershell?view=exchange-ps and https://docs.microsoft.com/en-us/graph/auth-limit-mailbox-access.
I connected to exchange successfully and i am able to run some of the commands, like Get-EXOMailbox or Get-Mailbox.But when i tried other commands like New-ApplicationAccessPolicy or Get-ApplicationAccessPolicy, i receive an error:
”
The term ‘New-ApplicationAccessPolicy’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
”
Does it mean that i need additional modules to install and if yes, which ones. Please advice how to proceed.
Version of powershell is 5.1
Regards,
Elena
Hey Elena, as replied on the TechNet thread, you need to have the necessary permissions in order to run these cmdlets (Organization Configuration role).
I have 15 email aliases on a mailbox. During the app authentication, will it ask for which email aliases that I can give access to. If so, can I give access to only two email aliases. Will I be able to get a different access token only for those two email aliases?. Or the access and refresh token is generated per mailbox
Answered over at the MTC
Hi,
I ‘m accessing Exchange API through OAuth access token. However I has to select
‘full_access_as_app by using Exchange Web Services with full access to all mailboxes’
Can we restrict the scope of mailboxes in this scenario?
Best,
Anurag
Ahem, did you read the full article?
“Probably the most important thing is that we cannot apply Application Access Policies against EWS impersonation granted as application permissions. This is unfortunate, as impersonation is one of the most powerful permissions you can have, especially when granted via application permissions (if granted via delegate permissions, we can control impersonation via RBAC scopes). “
FYI: the MS Exchange Team has enhanced Application Policy that can now be applied to Application permissions.
This is the source: https://techcommunity.microsoft.com/t5/exchange-team-blog/application-access-policy-support-in-ews/ba-p/2110361
I’ve tried and it’s working like a charm.
Thank you very much for this article.
Are you able to apply an application access policy scoped to just room mailboxes?
Nice write up. Thx! Seems a bit unsual that more granular controls are not possible at the graph API
Unfortunately the app policies are an “all or nothing” approach, you cannot granularly control which permissions are allowed. Microsoft is working on a “consent” role for Azure AD though, which might be just what we need in this regard. Though that’s just a hunch, as they havent really shared much details on it yet.
If I use the Catch-all rule to deny access, will that mean that new apps will be prevented access, and apps with more specific rules will work ?
The problem we are trying to solve is that 3rd party Apps are doing things the users are not allowed to let them do, due to legal constraints.
Say LinkedIn want to read contacts. But contacts is personal data, and giving a 3rd party access to the company contacts without a data processor agreement, article 30 document with risk assessment is at best a grayzone. Since everybody likely has the same issue, we are just trying to push to get this fixed, rather than having EU data authorities force us to shut down completely. Some 3rd party apps only processes data on the end user device, and are not a problem. Not sure with LinkedIn [We have users denying they gave the access to LinkedIn, but that is another issue, people do not see what they click], and Flow-e does processing on their servers and clearly needs a Data processing agreement before we can allow users to use that.
We want users to allow 3rd party apps to read their profile, for SSO, and nothing else. Everything else should be through admin approval.
We need a global mask to apply to all user introduced apps, and need to be able to limit access granted to existing apps in users context.