Secure Authentication from GitHub Actions to Microsoft Graph using OIDC
Passwordless authentication is a modern security best practice that eliminates the need to store and manage secrets in your CI/CD pipelines. In this post, I'll walk through how to set up secure, token-based authentication from GitHub Actions to Microsoft Graph API using OpenID Connect (OIDC).
Why OIDC for GitHub Actions?
Traditional authentication methods typically require storing API keys or service principal credentials in GitHub Secrets. While this works, it introduces security risks and maintenance overhead. With OIDC, GitHub Actions can request short-lived tokens on-demand, significantly enhancing your security posture.
Prerequisites
Step 1: Configure Microsoft Entra ID App Registration
First, you need to create an App Registration in Microsoft Entra ID:
After registration, make note of:
Step 2: Configure API Permissions
Now, configure the permissions your app needs:
Step 3: Set Up Federated Credentials
This is where the magic happens - connecting GitHub's identity system to Microsoft Entra ID:
Organization: Your GitHub organization/username
Repository: Your repository name
Entity type: Choose either "Environment", "Branch", or "Pull request" depending on your workflow
GitHub name: If you selected "Environment", enter your environment name (e.g., "production")
Name: A descriptive name for this credential
Recommended by LinkedIn
The system will generate a Subject identifier for you, which will look something like: repo:organization/repository:environment:environment-name
6. Important: Make sure the Audience field is set to api://AzureADTokenExchange
7. Click Add
Step 4: Create the GitHub Workflow
Now, let's create a GitHub workflow that uses OIDC to authenticate to Microsoft Graph:
name: Connect to Entra using OIDC
on:
workflow_dispatch:
permissions:
id-token: write # Required for OIDC
contents: read
jobs:
connect-entra:
runs-on: ubuntu-latest
environment: production
steps:
- name: Install Microsoft Entra PowerShell module
shell: pwsh
run: |
Install-Module -Name Microsoft.Entra -Repository PSGallery -Scope CurrentUser -Force -AllowClobber
- name: Get OIDC token and authenticate to Microsoft Graph
shell: pwsh
env:
TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
run: |
try {
# Request an OIDC token with the specific audience
$oidcRequestUrl = "$env:ACTIONS_ID_TOKEN_REQUEST_URL&audience=api://AzureADTokenExchange"
Write-Host "Requesting token from: $oidcRequestUrl"
$oidcResponse = Invoke-RestMethod -Uri $oidcRequestUrl -Headers @{Authorization = "Bearer $env:ACTIONS_ID_TOKEN_REQUEST_TOKEN"}
$oidcToken = $oidcResponse.value
# Exchange OIDC token for Graph access token
$tokenEndpoint = "https://login.microsoftonline.com/$env:TENANT_ID/oauth2/v2.0/token"
$tokenParams = @{
client_id = $env:CLIENT_ID
client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
client_assertion = $oidcToken
scope = "https://graph.microsoft.com/.default"
grant_type = "client_credentials"
}
Write-Host "Requesting access token from: $tokenEndpoint"
$tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenEndpoint -Body $tokenParams -ContentType "application/x-www-form-urlencoded"
Write-Host "Successfully obtained access token"
# Convert access token to SecureString
$secureAccessToken = ConvertTo-SecureString $tokenResponse.access_token -AsPlainText -Force
# Connect to Microsoft Graph using the token as SecureString
Connect-Entra -AccessToken $secureAccessToken
# Test the connection with a query
Get-EntraGroup -Top 5
}
catch {
Write-Error "Authentication failed: $_"
Write-Host "Error details: $($_.Exception | Format-List -Force | Out-String)"
exit 1
}
Step 5: Set Up Repository Secrets
Add the following secrets to your GitHub repository:
How It Works
Let's break down what's happening in this workflow:
Common Issues and Troubleshooting
Conclusion
Using OIDC with GitHub Actions and Microsoft Graph provides a secure, modern approach to service-to-service authentication without managing credentials. This passwordless approach reduces your attack surface and simplifies credential management.
Once set up, you can use this authentication method to automate various Microsoft Graph operations - from user management to reporting and beyond - all without storing any sensitive credentials in your GitHub repository.
Happy secure automating!