0
votes

Trying to use MS Graph API from an OAuth 2.0 HTTP client authenticated against an Azure App Registration to list and create contact folders and contacts for any user, with an O365 license, in the same tenant. The Graph API calls, from Postman, return valid JSON results only when the target user is the Azure Global Administrator user that granted the MS Graph API permission's Admin consent for the entire Azure tenant. Trying to access the contacts or contact folders of any other tenant user results in an Access Denied JSON error response.

According to MS documentation, the Azure App Registration's, MS Graph API, Admin Consented, Application Type permission Contacts.ReadWrite should permit access to all users contact folders and contacts.

However, it only works to get the contact folders and contacts of the Admin user that Admin Consented the permission. The Admin Consented, Application Type permission is behaving like a non-Admin Consented, Delegated Type permission.

Shouldn't an App Registration with Admin Consented, Application Type Contacts permission allow access to all tenant user's contacts run without the need for each end user to consent access?

Is this a bug or expected behavior for accessing contacts?

Why is there no "All" constraint for the Contacts Read Write permission?

For permissions that do not offer an "All" constraint element on the permission name, does the App Registration ignore the Application Type Admin Consent and default to the user who consented to the permission, running as that user instead? Doesn't that go against the point of having Admin Consent and Application Type permissions?

Do we need to have each user delegate Contact Folder and Contacts access to the Admin user?

1) Created an Azure App Registration 2) Granted Microsoft Graph API, Application Type, permissions to that Azure App Registration for:

https://graph.microsoft.com/Group.ReadWrite.All https://graph.microsoft.com/Directory.Read.All https://graph.microsoft.com/Contacts.ReadWrite

3) Granted Microsoft Graph API, Delegated Type, permissions to that Azure App Registration for:

offline_access

4) Configured Postman OAuth 2.0 access token with scope:

offline_access https://graph.microsoft.com/Group.ReadWrite.All https://graph.microsoft.com/Directory.Read.All https://graph.microsoft.com/Contacts.ReadWrite

Data has been [OMITTED] from the example below.

Get the contacts of the Azure Global Administrator user (example: [email protected]) that granted Admin Consent to the MS Graph API Application Type Permissions:

REQUEST:

https://graph.microsoft.com/v1.0/users/[email protected]/contacts

RESPONSE:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('admin_user%40fake.com')/contacts",
    "value": [
        {
            "@odata.etag": "W/\"[OMITTED]\"",
            "id": "[OMITTED]",
            "createdDateTime": "2019-10-25T17:11:06Z",
            "lastModifiedDateTime": "2019-10-30T13:11:13Z",
            "changeKey": "[OMITTED]",
            "categories": [],
            "parentFolderId": "[OMITTED]",
            "birthday": null,
            "fileAs": "test, Test",
            "displayName": "Test test",
            "givenName": "Test",
            "initials": null,
            "middleName": null,
            "nickName": null,
            "surname": "test",
            "title": null,
            "yomiGivenName": null,
            "yomiSurname": null,
            "yomiCompanyName": null,
            "generation": null,
            "imAddresses": [],
            "jobTitle": null,
            "companyName": null,
            "department": null,
            "officeLocation": null,
            "profession": null,
            "businessHomePage": null,
            "assistantName": null,
            "manager": null,
            "homePhones": [
                "666-666-6666"
            ],
            "mobilePhone": "555-555-5555",
            "businessPhones": [
                "777-777-7777"
            ],
            "spouseName": null,
            "personalNotes": "Test",
            "children": [],
            "emailAddresses": [],
            "homeAddress": {},
            "businessAddress": {},
            "otherAddress": {}
        }
    ]
}

Try to get the contacts of any other user with an O365 License (example: [email protected]):

REQUEST:

https://graph.microsoft.com/v1.0/users/[email protected]/contacts

RESPONSE:

{
  "error": {
    "code": "ErrorAccessDenied",
    "message": "Access is denied. Check credentials and try again.",
    "innerError": {
      "request-id": "[OMITTED]",
      "date": "2019-11-04T16:40:58"
    }
  }
}

Get the contact folders of the Azure Global Administrator user (example: [email protected]) that granted Admin Consent to the MS Graph API Application Type Permissions:

REQUEST:

https://graph.microsoft.com/v1.0/users/[email protected]/contactfolders

RESPONSE:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('admin_user%40fake.com')/contactFolders",
    "value": [
        {
            "id": "[OMITTED]",
            "parentFolderId": "[OMITTED]",
            "displayName": "Test Contacts Folder"
        }
    ]
}

Try to get the contact folders of any other user with an O365 License (example: [email protected]):

REQUEST:

https://graph.microsoft.com/v1.0/users/[email protected]/contactfolders

RESPONSE:

{
  "error": {
    "code": "ErrorAccessDenied",
    "message": "Access is denied. Check credentials and try again.",
    "innerError": {
      "request-id": "[OMITTED]",
      "date": "2019-11-04T16:49:42"
    }
  }
}

Current perms on App Registration

1

1 Answers

0
votes

From the documentation:

There are two scenarios where an app can get contacts in another user's contact folder:

  • If the app has application permissions, or,
  • If the app has the appropriate delegated permissions from one user, and another user has shared a contact folder with that user, or, has given delegated access to that user. See details and an example.

In other words, when using Delegated scopes a user may only access their own Contacts or Contacts explicitly shared with them.

If you want to access any user's contacts, you need to use the Contacts.Read or Contacts.ReadWrite Application scope.

In general, Exchange-based resources do not use the .All conventions. They rely on the scope type (Application vs Delegated) to determine if the application can see all user or only the authenticated user's resources.