So struggled with the exact same problem earlier today: always got 401 Unauthorized or other errors. Then I ran across this answer: Building a multi-tenant app for SharePoint Online O365
That was it: it is extremely counter-intuitive but the answer was that after getting a token for the discovery URL and performing the service discovery you need to fetch the token for each serviceResourceId you want to call. There are two hugely important points here that almost 8 hours of reading documentation do not make blatantly clear.
EVERY SERVICE RESOURCE ID HAS A DIFFERENT TOKEN
The first point is very confusing: I'm assuming its done this way because individual tenant apps are run on separate clusters and Microsoft has opted not to have a single authority service. Every single other implementation that does multi-tenant (for example, the Google Apps implementation) gives you a single token that wraps ALL your permissions into a single ball.
YOU CAN CALL THE TOKEN RETRIEVAL SERVICE MULTIPLE TIMES WITH THE SAME CODE
This is INCREDIBLY counter-intuitive (I'm using bold caps on purpose). There are simply no other OAuth2 services anywhere else on the Internet (and I've personally written code for easily 30 OAuth2 implementations) where you can call the token retrieval service multiple times with the same code and not receive an error. This goes completely against all default expectations and it is a major documentation failure that its not more clearly spelled out as a deviation from standard practice.
I'll say that again: no where else on the entire Internet can you actually use the same OAuth2 code more than once to retrieve an access token. This is something that should be called out PROMINENTLY on the documentation and simply isn't.
If you continue to have this problem you should do this with the OAuth2 code returned:
- Get an access token token using the code and adding the request parameter "resource" = "https://api.office.com/discovery/" (the closing slash is important)
- Call the url https://api.office.com/discovery/v2.0/me/services" using the Authorization header set to the token received in step #1. This will return a JSON object, with a value field. The value field will be an array of services that this code will return access tokens for. Each object in the value array will have a serviceResourceId property.
- For each object you will have to get another access token using the SAME code you used in step #1 but with the resource set to the serviceResourceId.
The code from step #3 will actually grant you access to the tenant endpoint you want.
3.