I decided to document some cool things about Azure AD Federated Credentials for future use:

  1. How to create credentials with script and action template (YAML) for GitHub using Azure CLI
  2. Example code snippet for node.js (btw... You don't need to use node if you just want to test the full example)
  • It is worth mentioning that there exists already some great documentation (mentioned in references) which I used as background to do the testing in this blog.

Use case?

The first thing mentioned here should be the use-case: Use case is establishing trust to another identity provider for non-user identities using OAuth2. The benefits of such flows are, that you don't need to maintain credentials in Azure AD, (no public key or password for Client Credential based flows) - Obviously you need in case of GitHub to ensure that the repo and actions are secured properly, as the credential now lives in GitHub.

Background

if you want to have overall view check MS Documentation

What OAuth2 flow?

The flow is technically same to the Azure AD Client Credentials flow with certificate using(urn:ietf:params:oauth:client-assertion-type:jwt-bearer) which I've written extensively about here

Differences compared to Azure AD Client Credentials flow with certificate?

  • The claims are different vs the claims given in Client Credential for certificate (in the requesting token)
  • Subject claim is used to match the repo and branch declaration
    • Correct token for github/jsa2/idtoken using main would be repo:jsa2/idtoken:ref:refs/heads/main

For reference: Client Credentials with certificate flow

"Configure a GitHub Actions workflow to exchange a token from GitHub for an access token from Microsoft identity platform and access Azure AD protected resources without needing to manage secrets"

Example to view the token in pipeline

Place this file into following directory .github\workflows structure and push to the repo

  • This example consists YAML definition shown in the references part

token.yaml

 name: OIDC-test  on:   push:     branches:     - '*'  jobs:   auth:      # Add "id-token" with the intended permissions.     permissions:       contents: 'read'       id-token: 'write'      runs-on: ubuntu-latest     steps:       - uses: actions/checkout@master         with:           fetch-depth: 1       - name: Dump env         run: env       - name: Post the token         run: |           OIDC_TOKEN=$(curl -sLS "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=api://AzureADTokenExchange" -H "User-Agent: actions/oidc-client" -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN"| jq -r '.value')           TOKEN=$(echo $OIDC_TOKEN | sed 's/./& /g')           jwtd() {             if [[ -x $(command -v jq) ]]; then                 jq -R 'split(".") | .[0],.[1] | @base64d | fromjson' <<< "${1}"                 echo "Signature: $(echo "${1}" | awk -F'.' '{print $3}')"             fi           }           jwtd $OIDC_TOKEN            echo "::set-output name=idToken::${OIDC_TOKEN}"             

Guide for workload federation

If you want to have GUI version, refer to the excellent guide on MS docs federated credentials

Prerequisites

  • Azure Cloud Shell (bash) (or local shell with deps installed)

Create the credential for Azure AD and workflow file

  GHWF=GH-account-$RANDOM REPO=repo:jsa2/idtoken:ref:refs/heads/main SUBID="3539c2a2-cd25-48c6-b295-14e59334ef1c"  CLIENTCREDENTIALS=$(az ad app create --display-name $GHWF \ -o tsv --query "objectId") APPID=$(az ad sp create --id $CLIENTCREDENTIALS -o tsv --query "appId") SPNOID=$(az ad sp show --id $APPID -o tsv --query "objectId") az role assignment create --role reader --scope "/subscriptions/$SUBID" --assignee-object-id  $SPNOID --assignee-principal-type ServicePrincipal  # az ad app delete --id $CLIENTCREDENTIALS   az rest --method POST --uri "https://graph.microsoft.com/beta/applications/$CLIENTCREDENTIALS/federatedIdentityCredentials" \ --body "{\"name\":\"$GHWF\",         \"issuer\":\"https://token.actions.githubusercontent.com\",         \"subject\":\"$REPO\"         ,\"description\":\"Testing\",         \"audiences\":[\"api://AzureADTokenExchange\"]}"  Tid=$(az account show -o tsv --query "homeTenantId" )   # No actual secrets are in the file. You can reference ClientID etc as secrets, but you should not assume this details private echo "name: Run Azure Login with OpenID Connect on: [push]  permissions:       id-token: write       contents: read        jobs:    build-and-deploy:     runs-on: ubuntu-latest     steps:     - name: 'Az CLI login'       uses: azure/login@v1       with:           client-id: $APPID           tenant-id: $Tid           subscription-id: $SUBID        - name: 'Run Azure CLI commands'       run: |           az account show           az group list           pwd " > workflow.yaml  
  • Push the repo to github
    • This example produces the following result

Node.js snippet for Azure Login using workload identity

  // Example based actions/core documentation const core = require('@actions/core'); const { decode } = require('jsonwebtoken'); const { axiosClient } = require('./axioshelp');  getIDTokenAction().catch((error) =>{   console.log('errors?',error) })  async function getIDTokenAction() {        const audience = core.getInput('audience', {required: false}) || "api://AzureADTokenExchange"    console.log('audience',audience)    const id_token2 = await core.getIDToken(audience).catch((error) => {    console.log('erros',error)})        console.log(decode(id_token2, {complete:true}))   var bearer = {   url: 'https://login.microsoftonline.com/033794f5-7c9d-4e98-923d-7b49114b7ac3/oauth2/v2.0/token',   json: true,   data: {     grant_type:"client_credentials",     client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',     client_id: '01cd25b5-4557-49c3-a1a2-b5d01bbb2c8b',     client_assertion: id_token2,     scope: 'https://graph.microsoft.com/.default',   } } var {data} = await axiosClient(bearer, true).catch((error) => {   console.log(error?.response?.data) })  console.log('aad response',data)  }  
  • This example produces the following result

References

There was some excellent information available when testing this solution, which was used to create the examples for Azure AD