OAuth : App development working with Microsoft Entra ID (Azure AD)
- Build Standard Web or Native Application
- Build JavaScript Application
- Build Backend Application (Daemon app, Service app)
- How to Verify Token
- Build Your Custom API Application
- 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 Azureaz loginaz account set -s {AZURE SUBSCRIPTION ID}# Add a federated credentialaz 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 withiss
in an external token. In this GitHub scenario,issuer
is a fixed value “https://token.actions.githubusercontent.com/
“.subject
:
This value must match withsub
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 forsubject
.
Please see here (GitHub docs) for more information aboutsubject
used in GitHub workflow.audiences
:
Theaud
in an external token must be included in thisaudiences
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/tokenContent-Type: application/x-www-form-urlencodedscope=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
andexp
) 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 inscope
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
2 replies»