Acquiring Microsoft Graph API Access Token in PowerShell

Microsoft Graph API offers a single endpoint to manage Azure AD and Office 365. With it, you can create and remove users, assign permissions and licenses, and many more. But the common pitfall for those who are just starting to use Microsoft Graph API is acquiring the access token for authentication.

This article will show you the different methods of getting access tokens (aka. OAuth or OAuth2) from Microsoft Graph API using PowerShell.

What Will You Need?

This article assumes that you already have the following requirements.

  • An Azure tenant. You can register for a free Azure account if you don’t have one.
  • Your Azure AD user account must have a global administrator role. This role allows you to register an Azure AD app and provide consent to the Microsoft Graph API permissions.
  • A computer with Windows PowerShell 5.1.

Register a New App in Azure Active Directory

Before using PowerShell to get an access token, you must already have an Azure AD app with Microsoft Graph API permissions. This app is what you’ll use as the identity when acquiring the OAuth token.

In this section, you’ll register a new app called PowerShell get access token. This application will have Microsoft Graph API permissions to read all objects in the directory (Directory.Read.All).

Connect to Azure Active Directory PowerShell

First, open PowerShell as administrator and install the AzureAD module.

Install-Module AzureAD -Scope AllUsers

Next, connect to Azure AD PowerShell.

# Connect to Azure AD PowerShell 
Connect-AzureAD -TenantID <tenant>

microsoft graph api access token

Compose the Microsoft Graph API Permission Object

Run the code below to create the Microsoft Graph API permission object. The object includes the Directory.Read.All application (Role) permission.

#region Create the API permission for the new App 
$apiName = 'Microsoft Graph' 
$apiPermissionName = 'Directory.Read.All' 

$api = (Get-AzureADServicePrincipal -filter "DisplayName eq '$($apiName)'") 
$permission = $api.AppRoles | Where-Object { $_.Value -eq $apiPermissionName } 
$apiPermission = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]@{ 
ResourceAppId = $api.AppId ; 
ResourceAccess = [Microsoft.Open.AzureAD.Model.ResourceAccess]@{ 
Id = $permission.Id ; 
Type = "Role" 
} 
} 
#endregion

Register the App and Enable the Service Principal

Execute the commands below to register a new Azure AD application called PowerShell get access token.

# Create the new AzureAD Application and enable the service principal 
$appName = 'PowerShell get access token' 
$myApp = New-AzureADApplication -DisplayName $appName -RequiredResourceAccess $apiPermission 
$null = New-AzureADServicePrincipal -AppID $myApp.AppID 
# Display the new application details 
$myApp

Now, copy the AppId value. You will need this value when using PowerShell to get the OAuth token.

powershell get access token

Adding a Client Credential (Secret)

You’ve created the App to serve as the client when requesting the access token. You can think of the AppID as the username; to complete the authentication, you must create either a secret or a certificate.

To add a client secret key, run the below command in PowerShell. The secret key will be valid for 2 years. If you want to adjust the validity, change the value of the -EndDate parameter.

New-AzureADApplicationPasswordCredential ` 
-CustomKeyIdentifier 'secret key 1' ` 
-ObjectId $myApp.ObjectId ` 
-EndDate (Get-Date).AddYears(2)

Finally, copy the secret value. Remember to treat this secret like a password and keep it private and confidential.

azure powershell get access token

Adding a Client Credential (Certificate)

Another form for client credentials is a certificate. This credential type is more secure than the secret key in plain text. Below are the steps to creating a self-signed certificate and uploading it to your application in Azure AD.

Run the below command in PowerShell to generate a self-signed certificate on your Windows computer. This certificate is valid for 2 years.

# Generate a self-signed certificate 
$certSplat = @{ 
Subject = 'PowerShell get access token' 
NotBefore = ((Get-Date).AddDays(-1)) 
NotAfter = ((Get-Date).AddYears(2)) 
CertStoreLocation = "Cert:\CurrentUser\My" 
Provider = "Microsoft Enhanced RSA and AES Cryptographic Provider" 
HashAlgorithm = "SHA256" 
} 
$selfSignedCertificate = New-SelfSignedCertificate @certSplat 
# Display the certificate details 
$selfSignedCertificate | Format-List PSParentPath,ThumbPrint,Subject,NotAfter

The certificate now exists in your computer’s certificate store, specifically in the *CurrentUser.

powershell get oauth2 token azure ad

Finally, upload the certificate to the Azure AD App. You do not have have to change anything in the code below.

$base64Value = [System.Convert]::ToBase64String($($selfSignedCertificate.GetRawCertData())) 
$base64Thumbprint = [System.Convert]::ToBase64String($($selfSignedCertificate.GetCertHash())) 

New-AzureADApplicationKeyCredential ` 
-ObjectId $myApp.ObjectId ` 
-CustomKeyIdentifier $base64Thumbprint ` 
-Type AsymmetricX509Cert ` 
-Usage Verify ` 
-Value $base64Value ` 
-StartDate $selfSignedCertificate.NotBefore ` 
-EndDate $selfSignedCertificate.NotAfter

If the upload was successful, you should see a similar result to the screenshot below.

get azure ad token powershell

Granting Consent

You’ve finally finished creating the Azure AD app and configuring its permissions. The last step to fully enable the application is to grant the Global administrator’s consent to the app’s permissions.

To do so, first, get the admin consent URL for the application and copy it to the clipboard.

# Get your Azure AD Tenant ID 
$tenantID = (Get-AzureADTenantDetail).ObjectId 
# Generate the admin consent URL and copy it to the clipboard 
"https://login.microsoftonline.com/$tenantID/adminconsent?client_id=$($myApp.AppID)" | Set-Clipboard 
# Display the admin consent URL from the clipboard 
Get-Clipboard

The output below shows you the expected admin consent URL.

powershell get azure access token

Now, open your browser and paste the admin consent URL to the URL box. Sign in using your global administrator account.

powershell get oauth token

The next page shows you the permissions being requested by the application. To provide consent to these permissions, click Accept.

powershell get azure ad access token

Getting Access Tokens

The app with Microsoft Graph API permissions is ready. You can now use it to authenticate with Azure AD and acquire the access token.

Using Invoke-RestMethod (Secret Key)

With the Invoke-RestMethod cmdlet, you will not need any additional module to get the access token. The first example below uses secret key credential authentication.

First, define the application ID, client secret, and the tenant ID.

$appId = 'Application ID Here' 
$secretKey = 'Secret Key Here' 
$tenantId = 'Azure AD Tenant'

Next, create the body for the token request. You do not need to make any changes to the below code.

# Create the token request parameters 
$token_request = @{ 
Body = @{ 
grant_type = "client_credentials" 
scope = "https://graph.microsoft.com/.default" 
client_id = $appId 
client_secret = $secretKey 
} 
Method = 'POST' 
URI = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" 
}

Now, run the command below to get the OAuth token and store it in the $OAuth variable.

# Get the token 
$oAuth = Invoke-RestMethod @token_request 
$oAuth.access_token

That’s it! You’ve acquired the access token.

powershell get access token azure

Using Invoke-RestMethod (Certificate)

Another way to authenticate using Invoke-RestMethod is with the certificate you created and uploaded to the Azure AD application. In this case, the method is not as simple or short as secret key authentication.

This next takes a different approach and uses a PowerShell script that accepts parameters.

First, copy the below code, paste it into a new file, and save the script as Get-OAuthTokenByCertificate.ps1.

# Get-OAuthTokenByCertificate.ps1 

[cmdletbinding()] 
param ( 
[parameter(mandatory)] 
[string]$TenantID, 
[parameter(mandatory)] 
[string]$AppID, 
[parameter(mandatory)] 
[X509Certificate]$Certificate 
) 

# Create base64 hash of certificate 
$CertificateBase64Hash = [System.Convert]::ToBase64String($Certificate.GetCertHash()) 

# Create JWT timestamp for expiration 
$StartDate = (Get-Date "1970-01-01T00:00:00Z" ).ToUniversalTime() 
$JWTExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End (Get-Date).ToUniversalTime().AddMinutes(2)).TotalSeconds 
$JWTExpiration = [math]::Round($JWTExpirationTimeSpan, 0) 

# Create JWT validity start timestamp 
$NotBeforeExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End ((Get-Date).ToUniversalTime())).TotalSeconds 
$NotBefore = [math]::Round($NotBeforeExpirationTimeSpan, 0) 

# Create JWT header 
$JWTHeader = @{ 
alg = "RS256" 
typ = "JWT" 
# Use the CertificateBase64Hash and replace/strip to match web encoding of base64 
x5t = $CertificateBase64Hash -replace '\+', '-' -replace '/', '_' -replace '=' 
} 

# Create JWT payload 
$JWTPayLoad = @{ 
# What endpoint is allowed to use this JWT 
aud = "https://login.microsoftonline.com/$tenantId/oauth2/token" 
# Expiration timestamp 
exp = $JWTExpiration 
# Issuer = your application 
iss = $AppId 
# JWT ID: random guid 
jti = [guid]::NewGuid() 
# Not to be used before 
nbf = $NotBefore 
# JWT Subject 
sub = $AppId 
} 

# Convert header and payload to base64 
$JWTHeaderToByte = [System.Text.Encoding]::UTF8.GetBytes(($JWTHeader | ConvertTo-Json)) 
$EncodedHeader = [System.Convert]::ToBase64String($JWTHeaderToByte) 

$JWTPayLoadToByte = [System.Text.Encoding]::UTF8.GetBytes(($JWTPayload | ConvertTo-Json)) 
$EncodedPayload = [System.Convert]::ToBase64String($JWTPayLoadToByte) 

# Join header and Payload with "." to create a valid (unsigned) JWT 
$JWT = $EncodedHeader + "." + $EncodedPayload 

# Get the private key object of your certificate 
$PrivateKey = $Certificate.PrivateKey 

# Define RSA signature and hashing algorithm 
$RSAPadding = [Security.Cryptography.RSASignaturePadding]::Pkcs1 
$HashAlgorithm = [Security.Cryptography.HashAlgorithmName]::SHA256 

# Create a signature of the JWT 
$Signature = [Convert]::ToBase64String( 
$PrivateKey.SignData([System.Text.Encoding]::UTF8.GetBytes($JWT), $HashAlgorithm, $RSAPadding) 
) -replace '\+', '-' -replace '/', '_' -replace '=' 

# Join the signature to the JWT with "." 
$JWT = $JWT + "." + $Signature 

# Create a hash with body parameters 
$Body = @{ 
client_id = $AppId 
client_assertion = $JWT 
client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" 
scope = "https://graph.microsoft.com/.default" 
grant_type = "client_credentials" 

} 

$Url = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" 

# Use the self-generated JWT as Authorization 
$Header = @{ 
Authorization = "Bearer $JWT" 
} 

# Splat the parameters for Invoke-Restmethod for cleaner code 
$PostSplat = @{ 
ContentType = 'application/x-www-form-urlencoded' 
Method = 'POST' 
Body = $Body 
Uri = $Url 
Headers = $Header 
} 

return $(Invoke-RestMethod @PostSplat)

After saving the file, run the script using this syntax. The <certificate thumbprint> must be the thumbprint of the self-signed certificate you created.

$oAuth = .\Get-OAuthTokenByCertificate.ps1 ` 
-TenantID 'Azure AD Tenant' ` 
-AppID 'Application ID Here' ` 
-Certificate Get-Item Cert:\CurrentUser\My\<certificate thumbprint> 

$oAuth.access_token

powershell get access token azure

Validating the Token

At this point, the access token is saved to the $oAuth variable. How do you validate that it is correct when all it looks like is a bunch of gibberish? Well, what it is is called a JSON Web Token (JWT), and if you decode the token, you’ll see actual information.

To decode the access token, you’ll need the JWTDetails PowerShell module installed. Run the command below to install it.

Install-Module JWTDetails

Now, pipe the access token to the Get-JWTDetails cmdlet.

$oAuth.access_token | Get-JWTDetails

The decoded JWT below confirms the details of the access token. The details include the API permissions, tenant ID, and token expiration time.

powershell get access token azure

Making a Request using the Microsoft Graph API Access Token

Alright! Now that you know how to acquire access tokens, your last task is to use them to create a request. One of the quickest requests you can make is to get the list of users in Azure AD.

The request requires the Authorization header and the endpoint URL request. The command below will retrieve the information for the user itbro@lzex.ga. If you want to lookup a different user account, replace the user principal name.

# Define the authorization header 
$headerParams = @{'Authorization' = "Bearer $($oAuth.access_token)" } 
# Define the request URL 
$Url = 'https://graph.microsoft.com/v1.0/users/itbro@lzex.ga' 
# Submit the request 
Invoke-RestMethod -Method Get -Uri $Url -Headers $headerParams

powershell get access token azure ad

What happens should you perform a request, but the access token is not authorized? Remember, the access token here only has the Directory.Read.All permission. Let’s try to delete the same user with the same access token.

Invoke-RestMethod -Method DELETE -Uri $Url -Headers $headerParams

And because the access token does not include the Directory.ReadWrite.All permission, you will get a 403 error.

powershell get access token azure invoke

Cyril Kardashevsky

Leave a Reply

Your email address will not be published. Required fields are marked *