An oddity with displaying permissions entries for Azure AD integrated applications

Few days back, while trying to answer a question about a consent prompt for some application, I opened the Azure AD blade and noticed something puzzling. I don’t remember if this was a deliberate choice or by chance, but I clicked on the “MS Tech Comm” application, the one enabling us to login with Office 365 accounts to the Microsoft Tech Community. On the Permissions tab for the app, under User Consent, an empty entry was visible. By empty, I mean this:

Permissions UI

Note the lack of Permission display name and Permission description values, pretty important ones if you ask me. And it turns out this isn’t a single/random occurrence, I was able to reproduce the exact same behavior by using a new tenant to access the MTC and provisioning the app. Moreover, other applications in my tenant seem to show the same trait, prime example being the Graph explorer application!

To try and understand whether this is a cosmetic issue or something more serious, I started looking into all the different methods we have to get the list of permissions granted to a given application. The good old MSOnline module can list the corresponding service principal object, but doesn’t have any method to display permissions. With the Azure AD module on the other hand we can leverage the Get-AzureADServicePrincipalOAuth2PermissionGrant cmdlet to get the permissions. I’ve even published a script that can help you enumerate all third party apps and their corresponding permissions. Here’s what I could get out of the Azure AD module in this case:

Scope :  User.Read openid email profile offline_access

At first glance, nothing interesting. The subtle difference is the presence of another leading space character, right after the semicolon character. Since space is the separator used when returning permissions entries, this indicates that another, “empty” permission entry is present. In contrast, here’s how the Scope output would look for service principal that doesn’t have such entries:

Scope : Directory.Read.All User.Read

It becomes a bit easier to understand if you use the Select -ExpandProperty method in PowerShell or get the property directly:

PS C:\> (Get-AzureADServicePrincipalOAuth2PermissionGrant -ObjectId 2447dc5e-44d0-46b2-9d0e-cd5572ea4ca2).Scope
 User.Read openid email profile offline_access

PS C:\> (Get-AzureADServicePrincipalOAuth2PermissionGrant -ObjectId f842c430-48bb-44d7-a67a-c0f60ce7d5f4).Scope
Directory.Read.All User.Read

In order to exclude the possibility that this is simply an oddity of the PowerShell representation of the permission object, I went and checked it with the Graph API directly. Here’s the result in JSON form:

{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#oauth2PermissionGrants",
    "value": [
        {
            "clientId": "5c5ee9b1-5343-434d-b683-8213a4d4267c",
            "consentType": "Principal",
            "expiryTime": "2019-09-28T17:34:31.6578813Z",
            "id": "seleXENTTUO2g4ITpNQmfJrNnWjE96ZNgP_OmB9amDbX3p6kjBWNTL7nhv-T0LWk",
            "principalId": "a49eded7-158c-4c8d-bee7-86ff93d0b5a4",
            "resourceId": "689dcd9a-f7c4-4da6-80ff-ce981f5a9836",
            "scope": " User.Read openid email profile offline_access",
            "startTime": "0001-01-01T00:00:00Z"
        }
    ]
}

Leading space again, so I’m not being crazy here. For comparison, another app representation via the Graph:

{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#oauth2PermissionGrants",
    "value": [
        {
            "clientId": "d6828122-1a97-46d8-be20-a72d7bf74604",
            "consentType": "Principal",
            "expiryTime": "2019-07-11T20:15:09.1639414Z",
            "id": "IoGC1pca2Ea-IKcte_dGBIm2UOKu5f1NjPfelFES7OLh7ZAh7hDjQq9EXxSU0hSV",
            "principalId": "2190ede1-10ee-42e3-af44-5f1494d21495",
            "resourceId": "e250b689-e5ae-4dfd-8cf7-de945112ece2",
            "scope": "User.Read Group.ReadWrite.All",
            "startTime": "0001-01-01T00:00:00Z"
        }
    ]
}

As already shown above, the Azure AD portal also treats this as an additional entry. On the other hand, the Cloud App Security portal seems to trim the leading space and only reports 5 permission entries (as opposed to the 6 entries visible in the Azure AD blade). The Azure AD Audit logs also seem to trim the leading space, so the mistery remains. The application consent screen itself only lists the default OIDC v2.0 scopes:

Consent dialog

A quick checkup against the rest of the Azure AD integrated applications in my tenant shows few other entries that display the same behavior. And just to make sure that this is not yet another oddity specific to my tenant, I run it against few more, including a demo tenant. Interestingly enough, all of apps showing this behavior are authored by Microsoft. Here’s how to generate a list of such applications:

PS C:> Get-AzureADOAuth2PermissionGrant -All $true | ? {$_.Scope.StartsWith(" ")}

Application    ConsentType User              Scope                                            
-----------    ----------- ----              -----                                            
MS Tech Comm   Principal   vasil@michev.info  User.Read openid email profile offline_access   
FastTrack      Principal   vasil@michev.info  profile openid                                  
Graph explorer Principal   vasil@michev.info  openid profile User.ReadWrite User.ReadBasic.All ...
OAuth Sandbox  Principal   pesho@michev.info  openid offline_access profile Mail.ReadWrite Mail.ReadWrite.Shared ...

I’m not inclined to believe that Microsoft somehow allowed a “blank” scope, with no description or display name, so this is probably some minor code error causing a valid permission entry (or perhaps some “internal” one?) to be replaced by empty string, without removing the separator as well. Like in this example:

PS C:\> "a b c d"
a b c d

PS C:\> "a b c d".Replace("a","")
 b c d

On the other hand, it might be something more serious, so I’ve probed some folks about it. Whatever this turns out to be, it would be nice if we had consistent display across all admin endpoints.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.