I'm developing an Office add-in and am following an excellent blog post by Richard diZerega in order to leverage the O365 discovery service and enumerate the SharePoint sites to which a user has access. The discovery service is the fifth and final authentication scheme he discusses in his post.
Debugging locally, having configured the application in my development Azure AD tenant, it works. I get a response from the discovery service and am then able to initiate a search for SharePoint sites (just as Richard does in his sample code). The response looks like this:
{
"@odata.context": "https://api.office.com/discovery/v1.0/me/$metadata#allServices",
"value": [
{
"@odata.type": "#Microsoft.DiscoveryServices.ServiceInfo",
"@odata.id": "https://api.office.com/discovery/v1.0/me/services('MyFiles@O365_SHAREPOINT')",
"@odata.editLink": "services('MyFiles@O365_SHAREPOINT')",
"capability": "MyFiles",
"entityKey": "MyFiles@O365_SHAREPOINT",
"providerId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"providerName": "Microsoft",
"serviceAccountType": 2,
"serviceApiVersion": "v1.0",
"serviceEndpointUri": "https://[tenant]-my.sharepoint.com/_api/v1.0/me",
"serviceId": "O365_SHAREPOINT",
"serviceName": "Office 365 SharePoint",
"serviceResourceId": "https://[tenant]-my.sharepoint.com/"
},
{
"@odata.type": "#Microsoft.DiscoveryServices.ServiceInfo",
"@odata.id": "https://api.office.com/discovery/v1.0/me/services('RootSite@O365_SHAREPOINT')",
"@odata.editLink": "services('RootSite@O365_SHAREPOINT')",
"capability": "RootSite",
"entityKey": "RootSite@O365_SHAREPOINT",
"providerId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"providerName": "Microsoft",
"serviceAccountType": 2,
"serviceApiVersion": "v1.0",
"serviceEndpointUri": "https://[tenant].sharepoint.com/_api",
"serviceId": "O365_SHAREPOINT",
"serviceName": "Office 365 SharePoint",
"serviceResourceId": "https://[tenant].sharepoint.com/"
}
]
}
Peanut butter and jelly time? Well, sort of...
When testing the same add-in (with the same permissions in Azure AD) pointing to my client's SharePoint Online tenant, I receive a valid but empty response from the discovery service:
{
"@odata.context": "https://api.office.com/discovery/v1.0/me/$metadata#allServices",
"value": [ ]
}
For whatever reason, RootSite is not returned as it is when testing the add-in in my development environment. In fact, nothing is returned at all. I have confirmed that the OAuth token exchange is working in both environments thanks to the remote debugging capabilities available with Azure (in which the production web application is running). I just can't seem to get anything back from the discovery service when pointing to the client's SharePoint Online tenant/app catalog.
Here are the permissions for both the development and production applications in their respective Azure AD tenants:
- Microsoft Graph
- Delegated Permissions
- Sign users in
- Delegated Permissions
- Office 365 SharePoint Online
- Delegated Permissions
- Read managed metadata
- Run search queries as a user
- Read and write items in all site collections
- Delegated Permissions
- Windows Azure Active Directory
- Delegated Permissions
- Sign in and read user profile
- Delegated Permissions
What determines whether or not RootSite is returned from the discovery service?
Do the user's permissions within the root SharePoint Online site matter? Is there some configuration setting within either Azure AD or the SharePoint Online administration interface to "light up" the discovery service and the RootSite capability? Why would additional permissions, such as "Read items in all site collections" be necessary in production when they don't appear to be necessary in development? The client uses federated authentication; does/should that matter?
Note: Special thanks to Richard diZerega and Jeremy Thake for replying to me on Twitter =).
[Update - 12/21]
Granting the "Read items in all site collections" delegated permission on both the Microsoft Graph application and the Office 365 SharePoint Online application doesn't change anything--the response from the discovery service is still valid but empty.