I am attempting to build a multi-tenant application for Office 365 which focuses on SharePoint Online and authenticates through Azure using OAuth2. The problem is specific to SharePoint access via the Azure login, but is only found when using this API to authenticate using OAuth2.
Many of the mechanics of registering the application properly and setting up users in Azure and Office, while somewhat complex, are conquerable with the right about of time investment.
Even the basic OAuth2 protocol usage with Azure works relatively smoothly. However, I'm thwarted in making my application truly multi-tenant by virtue of SharePoint's 'resource' parameter. This apparently requires my application to know the end-user's root SharePoint site URL before they complete the login sequence. I can't see how that is possible. Someone please point me in the right direction.
Here is a sample of the actual login sequence:
GET /common/oauth2/authorize?client_id=5cb5e93b-57f5-4e09-97c5-e0d20661c59a
&redirect_uri=https://myappdomain.com/v1/oauth2_redirect/
&response_type=code&prompt=login&state=D79E5777 HTTP/1.1
Host: login.windows.net
Cache-Control: no-cache
When the user authenticates, this results in a call to the redirect provided that looks like this:
https://myappdomain.com/v1/oauth2_redirect/?code=AAABAAAAvPM1KaPlrEq...{blah*3}
Great so far! The next step in the 3-legged authentication is a POST back to the /token endpoint to acquire the actual Bearer token to be used in all subsequent REST calls. This is just classic OAuth2...
POST /common/oauth2/token HTTP/1.1
Host: login.windows.net
Accept: text/json
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="grant_type"
authorization_code
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="code"
AAABAAAAvPM1KaPlrEq...{blah*3}
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="client_id"
5cb5e93b-57f5-4e09-97c5-e0d20661c59a
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="client_secret"
02{my little secret}I=
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="redirect_uri"
https://myappdomain.com/v1/oauth2_redirect/
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="resource"
https://contoso.sharepoint.com/
----WebKitFormBoundaryE19zNvXGzXaLvS5C
and here's where it gets sticky. The 'resource' parameter is required, and must point to the user-specific endpoint that you want access to. For Exchange or Azure, the endpoint is always the same. (https://graph.windows.net
or https://outlook.office365.com
) But SharePoint has a different endpoint for every tenancy. You haven't actually logged the user in yet, but already you need information about the user that you don't yet have..
If I deploy a version of my application that assumes 'contoso' as the tenant name (as above) then only users in the contoso tenancy will succeed using my app to read SharePoint data. As soon as another user in fabrikam tries to use it, my POST
to the /token
endpoint will ask for permission to the wrong site... and there's the rub.
How can I detect the correct endpoint to POST
to the /token
endpoint prior to the user actually logging in? Is there some hidden information that is given to me that I can use? Is there some kind of discovery possible to detect the tenant's root SharePoint URL? Or better yet, is there an endpoint that I can pass as the resource that can be representative of the tenant's home (something like https://office.microsoft.com/sharepoint
)? Then it could be potentially gleaned from the user_id JWT token returned. This would be similar to the other services, and quite simple for a client to manage. I don't see this, however.
Without a definitive answer to these questions, or workaround to these issues, I have to surmise that it is not possible to write a multi-tenant application that Authenticates into SharePoint Online O365... and that just doesn't seem right. Somebody please help!