Skip to content

Commit f71874e

Browse files
authored
Support Workload Identity Federation (#2297)
Add support for Workload Identity Federation (WIF) for Azure Repos. This enables users to authenticate to Azure Repos using federated tokens from Managed Identities, GitHub Actions, or generic identity providers. We support three scenarios: 1. Generic When you have a pre-obtained client assertion token from any external identity provider. You provide the assertion directly and GCM exchanges it for an access token. 2. Entra ID Managed Identities When your workload runs on an Azure resource that has a Managed Identity assigned. GCM will first request a token from the Managed Identity for the configured audience, then exchange that token for an Azure DevOps access token. 3. GitHub Actions When your workload runs in a GitHub Actions workflow. GCM will automatically obtain an OIDC token from the GitHub Actions runtime and exchange it for an Azure DevOps access token. [Rendered documentation](https://github.com/mjcheetham/git-credential-manager/blob/ce0eff3509f2c9cb5b8e976c6c02f6ee175292ea/docs/azrepos-wif.md)
2 parents dc7fdf2 + 9c38f6a commit f71874e

9 files changed

Lines changed: 1128 additions & 2 deletions

File tree

docs/azrepos-wif.md

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
# Azure Workload Identity Federation
2+
3+
Git Credential Manager supports [Workload Identity Federation][wif] for
4+
authentication with Azure Repos. This document provides an overview of Workload
5+
Identity Federation and how to use it with GCM.
6+
7+
## Overview
8+
9+
Workload Identity Federation allows a workload (such as a CI/CD pipeline, VM, or
10+
container) to exchange a token from an external identity provider for a Microsoft
11+
Entra ID access token — without needing to manage secrets like client secrets or
12+
certificates.
13+
14+
This is especially useful in scenarios where:
15+
16+
- You want to avoid storing long-lived secrets.
17+
- Your workload already has an identity token from another provider (e.g., GitHub
18+
Actions OIDC, a Managed Identity, or a custom identity provider).
19+
- You want to follow the principle of least privilege with short-lived,
20+
automatically rotated credentials.
21+
22+
You can read more about Workload Identity Federation in the
23+
[Microsoft Entra documentation][wif].
24+
25+
## How it works
26+
27+
When configured, GCM obtains a client assertion (a token from the external
28+
identity provider) and exchanges it with Microsoft Entra ID for an access token
29+
scoped to Azure DevOps. The exact mechanism for obtaining the client assertion
30+
depends on the federation scenario you choose.
31+
32+
## Scenarios
33+
34+
GCM supports three federation scenarios:
35+
36+
### Generic
37+
38+
Use this scenario when you have a pre-obtained client assertion token from any
39+
external identity provider. You provide the assertion directly and GCM exchanges
40+
it for an access token.
41+
42+
**Required settings:**
43+
44+
Setting|Git Configuration|Environment Variable
45+
-|-|-
46+
Scenario|[`credential.azreposWorkloadFederation`][gcm-wif-config]|[`GCM_AZREPOS_WIF`][gcm-wif-env]
47+
Client ID|[`credential.azreposWorkloadFederationClientId`][gcm-wif-clientid-config]|[`GCM_AZREPOS_WIF_CLIENTID`][gcm-wif-clientid-env]
48+
Tenant ID|[`credential.azreposWorkloadFederationTenantId`][gcm-wif-tenantid-config]|[`GCM_AZREPOS_WIF_TENANTID`][gcm-wif-tenantid-env]
49+
Assertion|[`credential.azreposWorkloadFederationAssertion`][gcm-wif-assertion-config]|[`GCM_AZREPOS_WIF_ASSERTION`][gcm-wif-assertion-env]
50+
51+
**Optional settings:**
52+
53+
Setting|Git Configuration|Environment Variable
54+
-|-|-
55+
Audience|[`credential.azreposWorkloadFederationAudience`][gcm-wif-audience-config]|[`GCM_AZREPOS_WIF_AUDIENCE`][gcm-wif-audience-env]
56+
57+
#### Example
58+
59+
```shell
60+
git config --global credential.azreposWorkloadFederation generic
61+
git config --global credential.azreposWorkloadFederationClientId "11111111-1111-1111-1111-111111111111"
62+
git config --global credential.azreposWorkloadFederationTenantId "22222222-2222-2222-2222-222222222222"
63+
git config --global credential.azreposWorkloadFederationAssertion "eyJhbGci..."
64+
```
65+
66+
### Managed Identity
67+
68+
Use this scenario when your workload runs on an Azure resource that has a
69+
[Managed Identity][az-mi] assigned. GCM will first request a token from the
70+
Managed Identity for the configured audience, then exchange that token for an
71+
Azure DevOps access token.
72+
73+
This is useful for Azure VMs, App Services, or other Azure resources that have a
74+
Managed Identity but need to authenticate as a specific app registration with
75+
a federated credential trust.
76+
77+
**Required settings:**
78+
79+
Setting|Git Configuration|Environment Variable
80+
-|-|-
81+
Scenario|[`credential.azreposWorkloadFederation`][gcm-wif-config]|[`GCM_AZREPOS_WIF`][gcm-wif-env]
82+
Client ID|[`credential.azreposWorkloadFederationClientId`][gcm-wif-clientid-config]|[`GCM_AZREPOS_WIF_CLIENTID`][gcm-wif-clientid-env]
83+
Tenant ID|[`credential.azreposWorkloadFederationTenantId`][gcm-wif-tenantid-config]|[`GCM_AZREPOS_WIF_TENANTID`][gcm-wif-tenantid-env]
84+
Managed Identity|[`credential.azreposWorkloadFederationManagedIdentity`][gcm-wif-mi-config]|[`GCM_AZREPOS_WIF_MANAGEDIDENTITY`][gcm-wif-mi-env]
85+
86+
**Optional settings:**
87+
88+
Setting|Git Configuration|Environment Variable
89+
-|-|-
90+
Audience|[`credential.azreposWorkloadFederationAudience`][gcm-wif-audience-config]|[`GCM_AZREPOS_WIF_AUDIENCE`][gcm-wif-audience-env]
91+
92+
The Managed Identity value accepts the same formats as
93+
[`credential.azreposManagedIdentity`][gcm-mi-config]:
94+
95+
Value|Description
96+
-|-
97+
`system`|System-Assigned Managed Identity
98+
`[guid]`|User-Assigned Managed Identity with the specified client ID
99+
`id://[guid]`|User-Assigned Managed Identity with the specified client ID
100+
`resource://[guid]`|User-Assigned Managed Identity for the associated resource
101+
102+
#### Example
103+
104+
```shell
105+
git config --global credential.azreposWorkloadFederation managedidentity
106+
git config --global credential.azreposWorkloadFederationClientId "11111111-1111-1111-1111-111111111111"
107+
git config --global credential.azreposWorkloadFederationTenantId "22222222-2222-2222-2222-222222222222"
108+
git config --global credential.azreposWorkloadFederationManagedIdentity system
109+
```
110+
111+
### GitHub Actions
112+
113+
Use this scenario when your workload runs in a GitHub Actions workflow. GCM will
114+
automatically obtain an OIDC token from the GitHub Actions runtime and exchange
115+
it for an Azure DevOps access token.
116+
117+
This scenario uses the `ACTIONS_ID_TOKEN_REQUEST_URL` and
118+
`ACTIONS_ID_TOKEN_REQUEST_TOKEN` environment variables that GitHub Actions
119+
automatically provides when a workflow has the `id-token: write` permission.
120+
121+
**Required settings:**
122+
123+
Setting|Git Configuration|Environment Variable
124+
-|-|-
125+
Scenario|[`credential.azreposWorkloadFederation`][gcm-wif-config]|[`GCM_AZREPOS_WIF`][gcm-wif-env]
126+
Client ID|[`credential.azreposWorkloadFederationClientId`][gcm-wif-clientid-config]|[`GCM_AZREPOS_WIF_CLIENTID`][gcm-wif-clientid-env]
127+
Tenant ID|[`credential.azreposWorkloadFederationTenantId`][gcm-wif-tenantid-config]|[`GCM_AZREPOS_WIF_TENANTID`][gcm-wif-tenantid-env]
128+
129+
**Optional settings:**
130+
131+
Setting|Git Configuration|Environment Variable
132+
-|-|-
133+
Audience|[`credential.azreposWorkloadFederationAudience`][gcm-wif-audience-config]|[`GCM_AZREPOS_WIF_AUDIENCE`][gcm-wif-audience-env]
134+
135+
No additional GCM settings are required — the GitHub Actions OIDC environment
136+
variables are read automatically.
137+
138+
#### Prerequisites
139+
140+
1. An app registration in Microsoft Entra ID with a federated credential
141+
configured to trust your GitHub repository.
142+
2. The app registration must have the necessary permissions to access Azure
143+
DevOps.
144+
3. Your GitHub Actions workflow must have the `id-token: write` permission.
145+
146+
#### Example workflow
147+
148+
```yaml
149+
permissions:
150+
id-token: write
151+
contents: read
152+
153+
steps:
154+
- uses: actions/checkout@v4
155+
env:
156+
GCM_AZREPOS_WIF: githubactions
157+
GCM_AZREPOS_WIF_CLIENTID: "11111111-1111-1111-1111-111111111111"
158+
GCM_AZREPOS_WIF_TENANTID: "22222222-2222-2222-2222-222222222222"
159+
```
160+
161+
## Audience
162+
163+
All scenarios accept an optional audience setting that controls the audience
164+
claim in the federated token request. The default value is
165+
`api://AzureADTokenExchange`, which is the standard audience for Microsoft Entra
166+
ID workload identity federation.
167+
168+
You only need to change this if your federated credential trust is configured
169+
with a custom audience.
170+
171+
[az-mi]: https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview
172+
[wif]: https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation
173+
[gcm-mi-config]: https://gh.io/gcm/config#credentialazreposmanagedidentity
174+
[gcm-wif-config]: https://gh.io/gcm/config#credentialazreposworkloadfederation
175+
[gcm-wif-clientid-config]: https://gh.io/gcm/config#credentialazreposworkloadfederationclientid
176+
[gcm-wif-tenantid-config]: https://gh.io/gcm/config#credentialazreposworkloadfederationtenantid
177+
[gcm-wif-audience-config]: https://gh.io/gcm/config#credentialazreposworkloadfederationaudience
178+
[gcm-wif-assertion-config]: https://gh.io/gcm/config#credentialazreposworkloadfederationassertion
179+
[gcm-wif-mi-config]: https://gh.io/gcm/config#credentialazreposworkloadfederationmanagedidentity
180+
[gcm-wif-env]: https://gh.io/gcm/env#GCM_AZREPOS_WIF
181+
[gcm-wif-clientid-env]: https://gh.io/gcm/env#GCM_AZREPOS_WIF_CLIENTID
182+
[gcm-wif-tenantid-env]: https://gh.io/gcm/env#GCM_AZREPOS_WIF_TENANTID
183+
[gcm-wif-audience-env]: https://gh.io/gcm/env#GCM_AZREPOS_WIF_AUDIENCE
184+
[gcm-wif-assertion-env]: https://gh.io/gcm/env#GCM_AZREPOS_WIF_ASSERTION
185+
[gcm-wif-mi-env]: https://gh.io/gcm/env#GCM_AZREPOS_WIF_MANAGEDIDENTITY

docs/configuration.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,138 @@ git config --global credential.azreposManagedIdentity "id://11111111-1111-1111-1
884884

885885
---
886886

887+
### credential.azreposWorkloadFederation
888+
889+
Use [Workload Identity Federation][wif] to authenticate with Azure Repos.
890+
891+
The value specifies the federation scenario to use for obtaining a client
892+
assertion to exchange for an access token.
893+
894+
You must also set the following companion settings:
895+
896+
- [credential.azreposWorkloadFederationClientId][credential-azrepos-wif-clientid]
897+
- [credential.azreposWorkloadFederationTenantId][credential-azrepos-wif-tenantid]
898+
899+
Depending on the scenario, additional settings may be required.
900+
901+
Value|Description
902+
-|-
903+
`generic`|Use a user-supplied client assertion ([credential.azreposWorkloadFederationAssertion][credential-azrepos-wif-assertion])
904+
`managedidentity`|Use a [Managed Identity][managed-identity] to obtain the federated token ([credential.azreposWorkloadFederationManagedIdentity][credential-azrepos-wif-managedidentity])
905+
`githubactions`|Automatically obtain an OIDC token from GitHub Actions
906+
907+
For more information about workload identity federation, see the
908+
[conceptual documentation][azrepos-wif-doc] and the Azure DevOps
909+
[documentation][azrepos-sp-mid].
910+
911+
#### Example
912+
913+
```shell
914+
git config --global credential.azreposWorkloadFederation githubactions
915+
```
916+
917+
**Also see: [GCM_AZREPOS_WIF][gcm-azrepos-wif]**
918+
919+
---
920+
921+
### credential.azreposWorkloadFederationClientId
922+
923+
The client ID of the app registration / service principal to request an access
924+
token for when using [Workload Identity Federation][wif] with
925+
[credential.azreposWorkloadFederation][credential-azrepos-wif].
926+
927+
#### Example
928+
929+
```shell
930+
git config --global credential.azreposWorkloadFederationClientId "11111111-1111-1111-1111-111111111111"
931+
```
932+
933+
**Also see: [GCM_AZREPOS_WIF_CLIENTID][gcm-azrepos-wif-clientid]**
934+
935+
---
936+
937+
### credential.azreposWorkloadFederationTenantId
938+
939+
The tenant ID of the app registration / service principal to request an access
940+
token for when using [Workload Identity Federation][wif] with
941+
[credential.azreposWorkloadFederation][credential-azrepos-wif].
942+
943+
#### Example
944+
945+
```shell
946+
git config --global credential.azreposWorkloadFederationTenantId "22222222-2222-2222-2222-222222222222"
947+
```
948+
949+
**Also see: [GCM_AZREPOS_WIF_TENANTID][gcm-azrepos-wif-tenantid]**
950+
951+
---
952+
953+
### credential.azreposWorkloadFederationAudience
954+
955+
The audience to use when requesting the federated token for
956+
[Workload Identity Federation][wif] with
957+
[credential.azreposWorkloadFederation][credential-azrepos-wif].
958+
959+
Defaults to `api://AzureADTokenExchange`.
960+
961+
#### Example
962+
963+
```shell
964+
git config --global credential.azreposWorkloadFederationAudience "api://AzureADTokenExchange"
965+
```
966+
967+
**Also see: [GCM_AZREPOS_WIF_AUDIENCE][gcm-azrepos-wif-audience]**
968+
969+
---
970+
971+
### credential.azreposWorkloadFederationAssertion
972+
973+
Specifies the client assertion token to use with the `generic`
974+
[Workload Identity Federation][wif] scenario
975+
([credential.azreposWorkloadFederation][credential-azrepos-wif]).
976+
977+
This setting is required when `credential.azreposWorkloadFederation` is set to
978+
`generic`.
979+
980+
#### Example
981+
982+
```shell
983+
git config --global credential.azreposWorkloadFederationAssertion "eyJhbGci..."
984+
```
985+
986+
**Also see: [GCM_AZREPOS_WIF_ASSERTION][gcm-azrepos-wif-assertion]**
987+
988+
---
989+
990+
### credential.azreposWorkloadFederationManagedIdentity
991+
992+
Specifies the [Managed Identity][managed-identity] to use to obtain a federated
993+
token for the `managedidentity` [Workload Identity Federation][wif] scenario
994+
([credential.azreposWorkloadFederation][credential-azrepos-wif]).
995+
996+
This setting is required when `credential.azreposWorkloadFederation` is set to
997+
`managedidentity`.
998+
999+
The value accepts the same formats as
1000+
[credential.azreposManagedIdentity](#credentialazreposmanagedidentity).
1001+
1002+
Value|Description
1003+
-|-
1004+
`system`|System-Assigned Managed Identity
1005+
`[guid]`|User-Assigned Managed Identity with the specified client ID
1006+
`id://[guid]`|User-Assigned Managed Identity with the specified client ID
1007+
`resource://[guid]`|User-Assigned Managed Identity for the associated resource
1008+
1009+
#### Example
1010+
1011+
```shell
1012+
git config --global credential.azreposWorkloadFederationManagedIdentity system
1013+
```
1014+
1015+
**Also see: [GCM_AZREPOS_WIF_MANAGEDIDENTITY][gcm-azrepos-wif-managedidentity]**
1016+
1017+
---
1018+
8871019
### credential.azreposServicePrincipal
8881020

8891021
Specify the client and tenant IDs of a [service principal][service-principal]
@@ -1048,6 +1180,12 @@ Defaults to disabled.
10481180
[gcm-autodetect-timeout]: environment.md#GCM_AUTODETECT_TIMEOUT
10491181
[gcm-azrepos-credentialtype]: environment.md#GCM_AZREPOS_CREDENTIALTYPE
10501182
[gcm-azrepos-credentialmanagedidentity]: environment.md#GCM_AZREPOS_MANAGEDIDENTITY
1183+
[gcm-azrepos-wif]: environment.md#GCM_AZREPOS_WIF
1184+
[gcm-azrepos-wif-clientid]: environment.md#GCM_AZREPOS_WIF_CLIENTID
1185+
[gcm-azrepos-wif-tenantid]: environment.md#GCM_AZREPOS_WIF_TENANTID
1186+
[gcm-azrepos-wif-audience]: environment.md#GCM_AZREPOS_WIF_AUDIENCE
1187+
[gcm-azrepos-wif-assertion]: environment.md#GCM_AZREPOS_WIF_ASSERTION
1188+
[gcm-azrepos-wif-managedidentity]: environment.md#GCM_AZREPOS_WIF_MANAGEDIDENTITY
10511189
[gcm-bitbucket-always-refresh-credentials]: environment.md#GCM_BITBUCKET_ALWAYS_REFRESH_CREDENTIALS
10521190
[gcm-bitbucket-authmodes]: environment.md#GCM_BITBUCKET_AUTHMODES
10531191
[gcm-credential-cache-options]: environment.md#GCM_CREDENTIAL_CACHE_OPTIONS
@@ -1077,6 +1215,7 @@ Defaults to disabled.
10771215
[autodetect]: autodetect.md
10781216
[libsecret]: https://wiki.gnome.org/Projects/Libsecret
10791217
[managed-identity]: https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview
1218+
[wif]: https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation
10801219
[provider-migrate]: migration.md#gcm_authority
10811220
[cache-options]: https://git-scm.com/docs/git-credential-cache#_options
10821221
[pass]: https://www.passwordstore.org/
@@ -1090,6 +1229,13 @@ Defaults to disabled.
10901229
[wam]: windows-broker.md
10911230
[service-principal]: https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals
10921231
[azrepos-sp-mid]: https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/service-principal-managed-identity
1232+
[azrepos-wif-doc]: azrepos-wif.md
1233+
[credential-azrepos-wif]: #credentialazreposworkloadfederation
1234+
[credential-azrepos-wif-clientid]: #credentialazreposworkloadfederationclientid
1235+
[credential-azrepos-wif-tenantid]: #credentialazreposworkloadfederationtenantid
1236+
[credential-azrepos-wif-audience]: #credentialazreposworkloadfederationaudience
1237+
[credential-azrepos-wif-assertion]: #credentialazreposworkloadfederationassertion
1238+
[credential-azrepos-wif-managedidentity]: #credentialazreposworkloadfederationmanagedidentity
10931239
[credential-azrepos-sp]: #credentialazreposserviceprincipal
10941240
[credential-azrepos-sp-secret]: #credentialazreposserviceprincipalsecret
10951241
[credential-azrepos-sp-cert-thumbprint]: #credentialazreposserviceprincipalcertificatethumbprint

0 commit comments

Comments
 (0)