Uncategorized

Microsoft Entra Workload Identities (Federated Credentials)

OAuth : App development working with Microsoft Entra ID (Azure AD)

  1. Build Standard Web or Native Application
  2. Build JavaScript Application
  3. Build Backend Application (Daemon app, Service app)
  4. How to Verify Token
  5. Build Your Custom API Application
  6. Workload Identity Federation

Up until now, your app should hold a secret or certificate in backend (daemon / service) application for accessing Entra ID (Azure AD) protected resources. (See here for Entra ID authorization with client credential flow.) As you know, this will risk your app in several cases.

From now on, you can now use Workload Identity Federation scenario written in this post. With the assertion (JWT token) in external identity (not in Entra ID), your app can now take Entra ID token more secure to mitigate these risks.

You can use this scenario in the application, such as, GitHub actions or workloads running on pods in Kubernetes hosted on any cloud or on-premises.
When you use GitHub actions which interacts with Entra ID protected resources, you don’t need to save secure secret or cert in GitHub.

In this post, I’ll describe how Microsoft Entra Workload Identities works, using GitHub actions.

Overlooking

Here I’ll show you overall flow of Entra Identities (federated credential).

This flow is the same kind of client credential flow.
However, unlike client credential flow, your app doesn’t need to hold Entra ID secured information, such as, a secret or a cert. Instead, your app should tightly be collaborated with external service (in this case, GitHub), and your app can then use this external identity for getting Entra ID token under the condition of trust relationship between this external identity and an app in Entra ID.

When you have trusted an external identity (in this case, GitHub identity) by registering “federated credential” in your app on Entra ID (see the step 1 in the following flow), you can take an Entra ID token using an external token (in this case, GitHub token) without using a secret or a certification in Entra ID (see the steps 4, 5, and 6).

Later, I’ll show you the detailed HTTP flow.

App Registration

First of all, register your application (service principal) in Entra ID as usual.
After you have registered your app, please copy an object id of your app.

Next let’s add a federated credential in your app on Entra ID.
This can be done with both Azure Portal UI and Azure CLI commands, and I’ll show you the example of CLI command as follows.
This is the example for integrating with GitHub actions for Azure.

# Login to Azure
az login
az account set -s {AZURE SUBSCRIPTION ID}

# Add a federated credential
az rest \
  --method POST \
  --uri 'https://graph.microsoft.com/beta/applications/{YOUR APP'S OBJECT ID}/federatedIdentityCredentials' \
  --body '{"name":"TestCred01","issuer":"https://token.actions.githubusercontent.com/","subject":"repo:{GITHUB ORG}/{GITHUB REPO}:{GITHUB ENTITY TYPE}","description":"Testing...","audiences":["api://AzureADTokenExchange"]}'

Note : With Azure Portal, select “Federated credentials” tab in “Certificates & secrets” blade, and click “add credential”. (See below.)

In above CLI command, you must take care to correctly define issuer, subject, and audiences.
As I will show you later, these values will be used in a JWT token of external identity (in this case, GitHub identity). If these values don’t match with a token passed by your application, the request will be rejected (failed) by Entra ID.

  • issuer :
    This value must match with iss in an external token. In this GitHub scenario, issuer is a fixed value “https://token.actions.githubusercontent.com/“.
  • subject :
    This value must match with sub in an external token. In this GitHub scenario, this value differs on each workflows. If the workflow in my GitHub repository “tsmatz/testrepo01” have been originated from a job that has an environment named Production, the subject will yield to “repo:tsmatz/testrepo01:environment:Production” and I should then use this value for subject.
    Please see here (GitHub docs) for more information about subject used in GitHub workflow.
  • audiences :
    The aud in an external token must be included in this audiences collection. In this GitHub scenario, api://AzureADTokenExchange is set as audience name in the built-in “Azure/login” GitHub action. You should then use this value if you reuse this GitHub actions. (Otherwise, you can use your custom audience. See here for registering your custom API (audience).)

Finally, set permissions to this application (service principal) depending on what you want to do.
For instance, in the case of GitHub actions for Azure, you should set appropriate RBAC role assignments for this service principal (application) in Azure.
See here for details about setting other permissions (such as, Entra ID application permissions) in client credential.

By registering this federated credential, it means that the trust is established between this external identity (your GitHub identity) and an app registered in Entra ID.

HTTP Flow

Now let’s see how Entra Identities works with external identity.

First, your application should get OIDC token from external identity (in this case, GitHub identity provider).
In built-in GitHub action integration with Azure (Azure/login), getIDToken() in GitHub Action’s toolkit (@actions/core package) is used for getting JWT token from GitHub OIDC provider. (See here for details about getting id token in GitHub workflow.)

As I’ve shown in my early post “How to verify token“, this token should be encoded as Json Web Token (i.e, JWT) formatted text as follows.

eyJhbGciOi...

In this case (GitHub workflow), the token will have the following claims (attributes).
As you can see below, aud, iss, and sub should match with the previous federated credential in Entra ID.

{
  "actor": "...",
  "aud": "api://AzureADTokenExchange",
  "base_ref": "",
  "event_name": "...",
  "head_ref": "",
  "iat": ...,
  "exp": ...,
  "iss": "https://token.actions.githubusercontent.com",
  "job_workflow_ref": "...",
  "jti": "...",
  "nbf": ...,
  "ref": "...",
  "ref_type": "...",
  "repository": "...",
  "repository_owner": "...",
  "run_attempt": "...",
  "run_id": "...",
  "run_number": "...",
  "sha": "...",
  "sub": "repo:tsmatz/testrepo01:environment:Production",
  "workflow": "..."
}

After getting an external token (JWT token), your app can request a new Entra ID token with this external token (GitHub token). (See below.)
You don’t need to specify Authorization header in this HTTP request.

HTTP Request

POST https://login.microsoftonline.com/{TENANT ID}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&client_id={YOUR APP'S CLIENT ID}
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion={JWT TOKEN}
&grant_type=client_credentials

Note : The external token (JWT) must follow :

  • The token must have a lifetime (lifetime between iat and exp) of less than or equal to 1 hour.
  • The token must be signed with RS256 algorithm.
  • The certificate thumbprint (x5t) or key Id (kid) must be specified in token header.

Note : Same as client credential flow, only https://graph.microsoft.com/.default is supported in scope value. See here for client credential flow.
(Same as client credential flow, you should also use tenant-aware endpoint in this flow.)

When Entra ID have received this request, Entra ID will ask external identity (issuer Url) for public key using OIDC well-known endpoint. (See below.)
Entra ID will then validate the signature in the external token.

GET https://token.actions.githubusercontent.com/.well-known/openid-configuration

When the validation has passed, Entra ID will return an access token to your app in HTTP response. (See below.)

HTTP Response

{
  "token_type": "Bearer",
  "expires_in": 3599,
  "access_token": "eyJ0eXAiOi..."
}

Congratulation !
Now your application can interact with resources protected by Entra ID, using this token.

Note : Currently, the reusable Azure ML GitHub Actions (see here) doesn’t still support this credential. (This needs a service principal with a secret.)

Categories: Uncategorized

Tagged as: ,

2 replies »

Leave a Reply