Azure AD Workload Federation anywhere?

I decided to write an post into creation of Azure AD Workload Identity using the option ’Workloads running in compute platforms outside of Azure’ covered @ MS DOCS.

The ultimate simplification of workload federation is using Azure AD Client Credentials flow with client private key signed token, where metadata is provided to Azure AD via Discovery URL, and then public key which Azure AD uses to check the signature is provided by JWKS uri (discovered via metadata endpoint)

This felt quite trivial, as I’ve covered the use of Client Credentials flow with certificate in multiple posts. Why this matters, is that Azure AD Workload Federation is almost 1:1 to scenarios which I’ve covered previously – except you don’t store the public key in Azure, but rather provide it by metadata endpoint (.well-known/..) that points to the separate JWKS uri.

  • In my previous examples I’ve used Azure Functions to provide the metadata endpoint, but this time I am just going to use blob storage to achieve this as covered in this excellent guide by MS

Benefits

  • While not demonstrated in this example, you could use single endpoint to cater multiple workload identities
  • No need to upload public key Azure AD (ease of use)
    • Some might still prefer uploading the public key to Azure, when reliance for the external IDP is to be minimized, as even if the IDP would be down, any client credentials using client could could still request tokens from Azure AD without the external IDP being awake)

Previously covered

I included these posts here because they cover code, workflow diagram, and setting up OIDC metadata endpoint with GH actions which I’ve done previously

Azure AD Client Credentials with Certificate – Code Examples for Node.jshttps://securecloud.blog/2021/12/02/azure-app-service-authorize-custom-jwt-tokens-from-api-clients/embed/#?secret=wQNTqdgKQJ#?secret=7lwLK2UWF2https://securecloud.blog/2021/11/30/deep-diver-azure-ad-federated-credentials/embed/#?secret=XyOJxvn5JB#?secret=p63Gf6NkNx

Walktrough ( using blob storage example from AKS workload federation example )

For the discovery document (.well-known/..) I am going to use the example provided here (The example also covers the use and creation of JWKS document)

For the JWKS document I am going to use previous example I have created with Azure Functions, but upload the JWKS document this time to Azure Blob Storage instead of using functions.

Example of the endpoints

Azure AD Configuration

  • Issuer needs to match to that of configured for the endpoint.
  • You can use custom audience, but I opted for the familiar audience value
Settings under credentials – federated credentials

Getting AAD issued tokens for workload Identity

I used slightly modified version of Azure AD Client Credentials with Certificate – Code Examples for Node.js

Issued tokens

Token payload created by the client (signed by the private key)

subject, issuer and audience values are the critical ones here.

{
  header: { alg: 'RS256', typ: 'JWT', x5t: 'nnMrEpA2R_ECV1jBy-Wlq6qNvx0=' },
  payload: {
    aud: 'api://AzureADTokenExchange',
    iss: 'https://oidcissuerb128517a.blob.core.windows.net/oidc-test/',
    sub: 'oidcissuerb128517a',
    actor: 'jose@securecloud.blog (this value is not needed)',
    jti: 'f26d803a-1cdf-4a6c-bdb6-2bb3af5e303e',
    exp: 1653286228,
    iat: 1653282628
  },
  signature
}

Azure AD Issued Token

The request returned V2 token, so some of the attributes are bit different from V1 token

azp (click the link for more examples)- Only present in v2.0 tokens, a replacement for appid.

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "jS1Xo1OWDj_52vbwGNgvQO2VzMc"
}.{
  "aud": "fb60f99c-7a34-4190-8149-302f77469936",
  "iss": "https://login.microsoftonline.com/033794f5-7c9d-4e98-923d-7b49114b7ac3/v2.0",
  "iat": 1653282328,
  "nbf": 1653282328,
  "exp": 1653286228,
  "aio": "E2ZgYHg0Q/A7942d8/0eSS6q1az9BAA=",
  "azp": "a992567e-0dfe-4d37-9741-5a849cb6932d",
  "azpacr": "2",
  "oid": "03461c6e-600b-4f95-957f-f3a350098fad",
  "rh": "0.AYIA9ZQ3A518mE6SPXtJEUt6w5z5YPs0epBBgUkwL3dGmTaCAAA.",
  "sub": "03461c6e-600b-4f95-957f-f3a350098fad",
  "tid": "033794f5-7c9d-4e98-923d-7b49114b7ac3",
  "uti": "FCTEIjmgpkSkQD-AWA6EAA",
  "ver": "2.0"
}.[Signature]
  • I also tested requesting tokens for other resources, such as MS graph api

Code snippet (Does not include depedencies, but gives an idea of the flow)

const {createToken} = require('./src/getAADtokenWithCert')
const {getKey, jwtverify} = require('./src/helpers')
const { decode } = require('jsonwebtoken')
const { default: axios } = require('axios')
const { axiosClient } = require('./src/axioshelpers')
 
var priv = require('fs').readFileSync('./private1.pem').toString()
 
getToken().then((token) => {
    console.log(token)
    console.log('Success:',decode(token,{complete:true}))
 
})
 
async function getToken() {
 
    var claims = {
        "aud": `api://AzureADTokenExchange`,
        "iss": "https://oidcissuerb128517a.blob.core.windows.net/oidc-test/",
        "sub": "oidcissuerb128517a",
    }
 
// testing locally that signature matches
 
var det =await getKey(`https://oidcissuerb128517a.blob.core.windows.net/oidc-test/openid/v1/jwks`)    
 
var {x5t, key} = det
 
     var jwt = await createToken(x5t,priv,claims).catch((error) => {
        return error
      }
    )
 
    var res =await jwtverify(jwt,key)
    console.log(res)
 
// request token from Azure AD 
    let opt = {
        url:"https://login.microsoftonline.com/033794f5-7c9d-4e98-923d-7b49114b7ac3/oauth2/token",
        data:{
            grant_type: "client_credentials",
            client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
            client_id: "a992567e-0dfe-4d37-9741-5a849cb6932d",
            client_assertion: jwt,
            resource: 'https://graph.microsoft.com'
        }
    }
 
    let s = await axiosClient(opt,true).catch(error => {
        console.log(error?.response?.data)
    })
 
 
    
    console.log(s?.data)
 
 
}

Logging

You can see any request made by workload identity in the servicePrincipal logs

End of blog

I anticipate that we are going see to much more use for so called federated credentials in future – Especially in AKS

This blog featured as part of Azure Week. Find more great Azure content here.

About the Author:

Security Consultant, Hacker, NodeJS dev/fanboy and blogger!

I am security consultant doing security related work, and security tooling development side projects with approx. +10 years into IT industry

I specialize in Microsoft technologies; Main focus on Azure, Azure AD, and M365. NodeJS comes into picture, as its my nr. 1 tool for testing and developing solutions in Azure.

My work and interests consists heavily around Identity And Access management, and securing Azure Services, and developing new services with NodeJS.

In terms of recognition I am really honored and humbled to be selected as one of the Microsoft MVP’s globally for Azure Award category. I’ve been also publicly recognized by MSRC in 2019 for my security research efforts related to MSRC Bug Bounties

Reference:

Santasalo, J. (2022). Azure AD Workload Federation anywhere?Available at: https://securecloud.blog/2022/05/23/azure-ad-workload-federation-anywhere/ [Accessed: 12th July 2022].

Share this on...

Rate this Post:

Share:

Topics:

Azure

Tags: