You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(tokenexchange): add EC key support and Entra ID federated auth style (#1147)
Add ECDSA P-256/P-384 key support for JWT client assertions, enabling
SPIRE X.509-SVIDs in the OBO flow. Add new "federated" auth style that
reads external IdP JWTs from a file for Entra ID workload identity federation.
Signed-off-by: Nader Ziada <nziada@redhat.com>
Copy file name to clipboardExpand all lines: docs/ENTRA_ID_SETUP.md
+36-1Lines changed: 36 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -34,7 +34,7 @@ From the app's **Overview** page, copy:
34
34
35
35
### Configure Client Credentials
36
36
37
-
You need **one** of the following — a client secretor a certificate. If you only need MCP server authentication (no other systems sharing this app registration), certificate-based auth is recommended.
37
+
You need **one** of the following — a client secret, a certificate, or a federated identity credential. If you only need MCP server authentication (no other systems sharing this app registration), certificate-based auth is recommended. If your workload runs in an environment with an external identity provider (e.g., SPIRE), use federated credentials.
38
38
39
39
#### Option A: Client Secret
40
40
@@ -234,6 +234,39 @@ For OBO to work, you need to configure API permissions in Azure:
234
234
3. Select the downstream API app registration
235
235
4. Add the required delegated permissions
236
236
237
+
### With Workload Identity Federation (Federated Credential)
238
+
239
+
If your MCP server runs in an environment with an external identity provider (e.g., SPIRE, GitHub Actions, or another Kubernetes cluster), you can use [workload identity federation](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation) instead of managing certificates or secrets. The external IdP issues a JWT that is passed directly to Entra ID as a federated credential.
240
+
241
+
#### Prerequisites
242
+
243
+
1. Configure a **federated identity credential** on your app registration:
244
+
- Go to **Certificates & secrets** → **Federated credentials** → **Add credential**
245
+
- Select the scenario (e.g., "Other issuer")
246
+
- Set the **Issuer** to your external IdP's OIDC issuer URL (e.g., `https://spire-server.example.com`)
247
+
- Set the **Subject identifier** to match the `sub` claim in the external JWT (e.g., `spiffe://example.com/mcp-server`)
248
+
- Set the **Audience** to match the `aud` claim (typically `api://AzureADTokenExchange`)
249
+
2. Ensure the external IdP writes a JWT to a file accessible by the MCP server (e.g., via SPIRE agent, Kubernetes projected volumes, or a sidecar)
The MCP server reads the JWT from `sts_federated_token_file` on each token request, so token rotation by the external IdP is handled automatically.
269
+
237
270
## Step 3: Run the MCP Server
238
271
239
272
```bash
@@ -500,5 +533,7 @@ This way, the cluster's existing OIDC configuration is untouched, and the MCP se
500
533
501
534
-[Entra ID OAuth 2.0 Documentation](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow)
502
535
-[Entra ID On-Behalf-Of Flow](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-on-behalf-of-flow)
536
+
-[Entra ID Workload Identity Federation](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation)
537
+
-[Entra ID Client Credentials with Federated Credential](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow#third-case-access-token-request-with-a-federated-credential)
| `sts_federated_token_file` | string | `""` | Path to a JWT file from an external identity provider, e.g., SPIRE JWT-SVID (for `federated` auth style). |
497
498
| `cluster_auth_mode` | string | `""` | Cluster auth mode: `passthrough`(forward Authorization header when present, fall back to kubeconfig when absent) or `kubeconfig` (always use kubeconfig credentials). Defaults to `passthrough`. |
498
499
| `certificate_authority` | string | `""` | Path to CA certificate for validating authorization server connections. |
499
500
| `server_url` | string | `""` | Public URL of the MCP server (used for OAuth metadata). |
returnfmt.Errorf("sts_federated_token_file must be a valid file path: %w", err)
628
+
}
612
629
default:
613
-
returnfmt.Errorf("invalid sts_auth_style %q: must be %q, %q, or %q", c.StsAuthStyle, tokenexchange.AuthStyleParams, tokenexchange.AuthStyleHeader, tokenexchange.AuthStyleAssertion)
630
+
returnfmt.Errorf("invalid sts_auth_style %q: must be %q, %q, %q, or %q", c.StsAuthStyle, tokenexchange.AuthStyleParams, tokenexchange.AuthStyleHeader, tokenexchange.AuthStyleAssertion, tokenexchange.AuthStyleFederated)
returnnil, nil, fmt.Errorf("failed to parse private key from %q (tried PKCS#8 and PKCS#1 formats)", keyFile)
80
+
returnnil, nil, fmt.Errorf("failed to parse private key from %q (tried PKCS#8 and PKCS#1 formats; EC keys must be in PKCS#8 format, convert with: openssl pkcs8 -topk8 -nocrypt)", keyFile)
79
81
}
80
82
81
-
if_, ok:=privateKey.(*rsa.PrivateKey); !ok {
82
-
returnnil, nil, fmt.Errorf("unsupported key type %T from %q: only RSA keys are currently supported for JWT client assertions", privateKey, keyFile)
0 commit comments