Technology Solutions for Everyday Folks

Creating a Generic OAuth Token Request to Microsoft Graph API

After spending much of the month of August on a European vacation, I'm back and preparing for my sessions at MMS Flamingo Edition coming up in October. The last couple weeks I've been working on demos for my Admin Service and Graph API session in which much conversation (and many demos) will take place working with "read-only" data from both of these services to empower T1 folks. There's much more to that session, but in working through my demo materials I want/need to simplify the OAuth token procurement for Graph.

Why Roll My Own OAuth, Again?

As with many fully-baked APIs, libraries are available to help simplify the auth process (such as automatically generating and refreshing a Bearer token) and everyday/common tasks. Graph API is no different and there are libraries available for many languages. Those work great, so you should totally use them if it makes sense!

Since I'm using this for demonstration purposes I want to cut it down to the necessary bits:

  1. Installing libraries injects bloat. Sure they have language-specific helpers (often why you want to use them), but abstractions can add confusion (especially for folks new to the API/library/project).
  2. I like to learn (and share) about the underlying mechanics, and an abstraction layer gets in the way.
  3. Getting down to basic OAuth REST calls exposes the language-agnostic bits. Most folks would use Powershell for such calls, but knowing the underlying REST calls means you can quickly use other languages (or utilities such as cURL) to connect when there's no library available.
  4. Using the bare-minimum aspects (cURL or Invoke-RestMethod for example) can help in quick troubleshooting as you work through the project ("is it a problem with how I'm using a library, or with what I'm passing to the API?").

Distilling these demos down to their core components helps make them more understandable as folks learn or decide how/where to use these concepts in the future. If you always depend on a library, the absence of a library could get you "stuck" for a time.

The "Scope" In Question

My demos are all using "application" based authentication with a client secret, and not user-based OAuth. That's due to how these demos are intended to access Graph as a "service" and as such don't require a callback URL or the additional bits required for user-based authentication. Perhaps in a future post I'll dig into that and write the equivalent.

With that said, credentials are generated from the administrative interface (a client ID and secret) much like other services would expose for API access.

Graph API OAuth Is Simpler Than Some Others

I was surprised how simple Microsoft made Graph API authentication. Having worked with other services (looking at you, Twitter X), there's little to fiddle with in the OAuth request. All you need to have set up for a basic OAuth request is:

  • Your tenant ID (a long string that looks like 4616ff25-1234-ac32-e96266180fd9 (not an actual tenant ID));
  • Your application/client ID (string similar to tenant ID, created when you set up the application); and
  • Your client secret (string generated when you create a client secret).

 Armed with those three bits of information, the cURL equivalent to generate a Bearer token is pretty straightforward:

curl -L -X GET 'https://login.microsoftonline.com/[YOUR_TENANT_ID]/oauth2/v2.0/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' \
-d 'client_id=[YOUR_APPLICATION/CLIENT_ID]' \
-d 'client_secret=[YOUR_CLIENT_SECRET_STRING]' \
-d 'scope=https://graph.microsoft.com/.default'

When you run that with your values, you'll get a JSON response upon success with your Bearer token (good for 1 hour):

{
    "token_type": "Bearer",
    "expires_in": 3599,
    "ext_expires_in": 3599,
    "access_token": "SUPERDUPERLONGSTRINGWITHALLTHEBITSNECESSARYFORAGOODBEARERTOKENVALUEEMBEDDED"
}

You can then "save" the value returned in access_token and pass that string in future responses (until the token expires):

curl -L 'https://graph.microsoft.com/path/to/whatever' \
-H 'Authorization: Bearer SUPERDUPERLONGSTRINGWITHALLTHEBITSNECESSARYFORAGOODBEARERTOKENVALUEEMBEDDED'

And That's It!

Again there's no reason not to use the client libraries for Graph API, but ultimately they all boil down to handling basic REST calls which under certain circumstances may be useful to do without the help (and additional size/complexity) of a full-on library! Good luck!