Table of Contents
We had a recently gotten some question’s on how to connect to Microsoft GraphAPI using certificate instead of an app secret. We have set this up before using the Microsoft Graph PowerShell SDK, but we wanted to test this using Invoke-RestMethod.
There is a PowerShell module that has been created that allow for much easier creation of authentication tokens using Microsoft Authentication Library.
Microsoft Authentication Library (MSAL)
The Microsoft Authentication Library (MSAL) enables developers to acquire tokens from the Microsoft identity platform in order to authenticate users and access secured web APIs.
It can be used to provide secure access to Microsoft Graph, other Microsoft APIs, third-party web APIs, or your own web API. MSAL supports many different application architectures and platforms including .NET, JavaScript, Java, Python, Android, and iOS.
MSAL gives you many ways to get tokens, with a consistent API for a number of platforms. Using MSAL provides the following benefits:
- No need to directly use the OAuth libraries or code against the protocol in your application.
- Acquires tokens on behalf of a user or on behalf of an application (when applicable to the platform).
- Maintains a token cache and refreshes tokens for you when they are close to expire. You don’t need to handle token expiration on your own.
- Helps you specify which audience you want your application to sign in (your org, several orgs, work, and school and Microsoft personal accounts, social identities with Entra ID B2C, users in sovereign, and national clouds).
- Helps you set up your application from configuration files.
- Helps you troubleshoot your app by exposing actionable exceptions, logging, and telemetry.
The PowerShell module that can be used to create tokens is called MSAL.PS and the latest version as of today is 4.37.0.0.
PS C:\> Get-Module MSAL.PS -ListAvailable
ModuleType Version Name
---------- ------- ----
Script 4.37.0.0 MSAL.PS
Connect to Microsoft Graph using MSAL with certificate
In this post we will be going through installing and using this module to generate an authentication token using a self-signed certificate and using that token to connect to Microsoft Graph.
1️⃣ The first step is to install the MSAL.PS module.
Install-Module -Name MSAL.PS
PS C:\> Get-InstalledModule -Name MSAL.PS
Version Name Repository
------- ---- ----------
4.37.0.0 MSAL.PS PSGallery
2️⃣ Once the module is installed we can generate the certificate that will be used to connect to Microsoft Graph.
New-SelfSignedCertificate `
-DnsName "anydomain.com" `
-CertStoreLocation "cert:\LocalMachine\My" `
-NotAfter (Get-Date).AddYears(5) `
-KeySpec KeyExchange `
-FriendlyName "MSAL_Cert"
Thumbprint Subject
---------- -------
0F85F152D164655328F8C5025CF0AFF804B33AF7 CN=anydomain.com
3️⃣ Once the cert has been created, export the cert so that it can be uploaded to the App registration.
$certThumbprint = '0F85F152D164655328F8C5025CF0AFF804B33AF7'
$cert = Get-ChildItem Cert:\LocalMachine\my\$CertThumbprint
$cert | Export-Certificate -FilePath C:\temp\Graph_MSAL_Cert.cer
Directory: C:\temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 10/5/2023 10:47 AM 808 Graph_MSAL_Cert.cer
4️⃣ Once the cert is exported, we can upload to the app registration that we created before. See previous post if app registration hasn’t been created in Azure yet for Microsoft Graph.
To add the cert, open the Microsoft Entra admin center > Applications > App registrations and select your app and go to Certificates & secrets
5️⃣ After the cert is upload, we should now be able to connect. We will need the tenantId, clientId and certificate. The tenantID and ClientId can be gotten from the app registration overview page and the cert can be gotten using Get-Item and the cert location / thumbprint.
PS C:\> Get-ChildItem Cert:\LocalMachine\my
PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\my
Thumbprint Subject
---------- -------
D4EFCB1A12145CF7F1EE26D2456F8E4C2795E683 CN=NVIDIA GameStream Server
0F85F152D164655328F8C5025CF0AFF804B33AF7 CN=anydomain.com
6️⃣ Get the access token then store it into a variable.
Import-Module MSAL.PS
$TenantId = "c032627b-6715-4e39-9990-bcf48ee5e0c5"
$ClientId = "ffb97f4f-cd58-4e4d-95ac-17081063c20b"
$CertificateThumbprint = '0F85F152D164655328F8C5025CF0AFF804B33AF7'
$ClientCert = Get-ChildItem "Cert:\LocalMachine\my\$CertificateThumbprint"
#$ClientCert = Get-ChildItem Cert:\LocalMachine\my | Where-Object { $_.Subject -match 'CN=anydomain.com'}
$MSToken = Get-MsalToken -ClientId $ClientId -TenantId $TenantId -ClientCertificate $ClientCert
PS C:\> $MSToken
AccessToken : eyJ0eXAiOiJKV1Qi....txlaTghtMw
IsExtendedLifeTimeToken : False
UniqueId :
ExpiresOn : 10/5/2023 12:10:57 PM +00:00
ExtendedExpiresOn : 10/5/2023 12:10:57 PM +00:00
TenantId :
Account :
IdToken :
Scopes : {https://graph.microsoft.com/.default}
CorrelationId : b3a3207e-232c-4d41-8f97-a98f3815078c
TokenType : Bearer
SpaAuthCode :
ClaimsPrincipal :
AuthenticationResultMetadata : Microsoft.Identity.Client.AuthenticationResultMetadata
User :
7️⃣ Once we have the token, it’s now the same process as we have used before to connect using Invoke-Restmethod but using the $MSToken.AccessToken we generated using the Get-MsalToken and our certificate.
For example, we’ll call to the groups endpoint then get the list of groups in our tenant.
$graphGroupUrl = 'https://graph.microsoft.com/v1.0/Groups/'
$headers = @{
Authorization = "Bearer $($MSToken.AccessToken)"
}
$results = (Invoke-RestMethod -Headers $headers -Uri $graphGroupUrl -Method Get).value
$results | select displayName, groupTypes, mail
displayName groupTypes mail
----------- ---------- ----
sg-Sales and Marketing {}
sg-Retail {}
Executives {} [email protected]
Northwind Traders {} [email protected]
Contoso Team {Unified} [email protected]
Another example to query the list of users in our tenant. This time, we called to the /v1.0/users endpoint instead of /v1.0/group endpoint.
$graphUsersUrl = 'https://graph.microsoft.com/v1.0/users/'
$headers = @{
Authorization = "Bearer $($MSToken.AccessToken)"
}
$results = (Invoke-RestMethod -Headers $headers -Uri $graphUsersUrl -Method Get).value
$results | select displayName, userPrincipalName, id
displayName userPrincipalName id
----------- ----------------- --
Adele Vance [email protected] cd90a87a-7156-4f6a-88b5-5ee908354b3c
MOD Administrator [email protected] 647fea69-afca-4001-af45-f0cc82a2fa41
Alex Wilber [email protected] a1ae71c5-a099-4368-8c9f-c1e24cb027fc
Allan Deyoung [email protected] 19d877b4-b2f8-456d-ad26-766dec8f5d74
Automate Bot [email protected] 7a8b00ac-6c46-48b3-bc0e-4fc0b20be29b
Bianca Pisani [email protected] 8e9ef693-5dbb-4310-85a8-89492ca7e33d
Connect to Microsoft Graph using MSAL with client secret
For testing purposes or working in a safe environtment. You can use the client secret with MSAL to get the access token as well.
Import-Module MSAL.PS
$tenantId = 'c032627b-6715-4e39-9990-bcf48ee5e0c5'
$clientId = 'ffb97f4f-cd58-4e4d-95ac-17081063c20b'
$clientSecret = '3EX8Q~8wPTZc6CFeHBvAoTKZSPpGwNnUynZAab8w'
$secureSecret = $clientSecret | ConvertTo-SecureString -AsPlainText -Force
$MSToken = Get-MsalToken -ClientId $clientId -TenantId $tenantId -ClientSecret $secureSecret
PS C:\> $MSToken
AccessToken : eyJ0eXAiOiJKV1QiL...cocSTlA
IsExtendedLifeTimeToken : False
UniqueId :
ExpiresOn : 10/5/2023 2:16:24 PM +00:00
ExtendedExpiresOn : 10/5/2023 2:16:24 PM +00:00
TenantId :
Account :
IdToken :
Scopes : {https://graph.microsoft.com/.default}
CorrelationId : 73471f71-7b71-4f63-b41f-2b1f4cf9bf18
TokenType : Bearer
SpaAuthCode :
ClaimsPrincipal :
AuthenticationResultMetadata : Microsoft.Identity.Client.AuthenticationResultMetadata
User
Use the access token with Microsoft Graph PowerShell SDK
The access token can be used to authenticate using Microsoft Graph PowerShell SDK.
$secureToken = ($MSToken.AccessToken | ConvertTo-SecureString -AsPlainText -Force)
Connect-Graph -AccessToken $secureToken
PS C:\> $secureToken = ($MSToken.AccessToken | ConvertTo-SecureString -AsPlainText -Force)
PS C:\> Connect-Graph -AccessToken $secureToken
Welcome to Microsoft Graph!
Connected via userprovidedaccesstoken access using 1ebf4d38-99d5-40a3-9220-1bf824ab605e
Readme: https://aka.ms/graph/sdk/powershell
SDK Docs: https://aka.ms/graph/sdk/powershell/docs
API Docs: https://aka.ms/graph/docs
NOTE: You can use the -NoWelcome parameter to suppress this message.
PS C:\Users\bonben> Get-MgContext
ClientId : 1ebf4d38-99d5-40a3-9220-1bf824ab605e
TenantId : c032627b-6715-4e39-9990-bcf48ee5e0c5
Scopes : {User.Read.All}
AuthType : UserProvidedAccessToken
TokenCredentialType : UserProvidedAccessToken
CertificateThumbprint :
CertificateSubjectName :
Account :
AppName : msal_test
ContextScope : Process
Certificate :
PSHostVersion : 5.1.22621.1778
ManagedIdentityId :
ClientSecret :
Environment : Global
Conclusion
Using MSAL.PS module provides a much quicker and easier way to generate access tokens for Microsoft Graph with the added security of using a certificate over having to put in any application secret.
Not a reader? Watch this related video tutorial: