-
Notifications
You must be signed in to change notification settings - Fork 208
Description
Describe the bug
I have two Entra application registrations established, within the same tenant, with one acting as the middleware client for the other. Running an OnBehalfOf flow using a secret directly as the client_credential in the ConfidentialClientApplication works just fine, but passing in the JWT obtained from the DefaultAzureCredential gets an invalid_client error when calling acquire_token_on_behalf_of()
To Reproduce
Steps to reproduce the behavior:
Using the .env.sample.entra-id (adjust the environment variable names to be compatible with DefaultAzureCredential for later) configuration.
AZURE_IDENTITY_AUTHORITY="https://login.microsoftonline.com"
AZURE_CLIENT_ID="middleware client service id"
AZURE_CLIENT_SECRET="secret created on the middleware service"
AZURE_TENANT_ID="my tenant id"
USER_ASSERTION="a valid JWT to my middleware service"
SCOPE="scope defined on the backend service" # Note that this scope needs to be exposed by authorizing the middleware service to access it as a client.
The following works:
app = msal.ConfidentialClientApplication(
client_id=os.getenv("AZURE_CLIENT_ID"),
authority=f"{os.getenv("AZURE_IDENTITY_AUTHORITY")}/{os.getenv("AZURE_TENANT_ID")",
client_credential=os.getenv("AZURE_CLIENT_SECRET"),
instance_discovery=False
)
result = app.acquire_token_on_behalf_of(user_assertion=os.getenv("USER_ASSERTION"), scopes=[os.getenv("SCOPE")])
However, the app will be running in AKS, so I would like to take advantage of the service identity to avoid having to manage the secrets. So I believe the following should work (even locally by re-using the secret as an environment variable):
_azure_client_credential = DefaultAzureCredential(
authority=str(os.getenv("AZURE_IDENTITY_AUTHORITY")),
instance_discovery=False
)
token = _azure_client_credential.get_token("https://graph.microsoft.com/.default")
app = msal.ConfidentialClientApplication(
client_id=os.getenv("AZURE_CLIENT_ID"),
authority=f"{os.getenv("AZURE_IDENTITY_AUTHORITY")}/{os.getenv("AZURE_TENANT_ID")",
client_credential={"client_assertion": token.token},
instance_discovery=False
)
result = app.acquire_token_on_behalf_of(user_assertion=os.getenv("USER_ASSERTION"), scopes=[os.getenv("SCOPE")])
Expected behavior
A valid OBO token should be returned.
What you see instead
I successfully get a valid JWT token for the service, but when calling app.acquire_token_on_behalf_of(...) I get an error with the following message:
AADSTS700211: No matching federated identity record found for presented assertion issuer 'https://sts.windows.net/0a...9b/'. Please note that the matching is done using a case-sensitive comparison. Please check your federated identity credential Subject, Audience and Issuer against the presented assertion. https://learn.microsoft.com/entra/workload-id/workload-identity-federation
The MSAL Python version you are using
1.30.0
Additional context
I'm very confused why its complaining about federated identity record, when the idea of a federated identity should already be resolved. Obviously I will be using a federated identity in AKS for the service account, but this is about trying to run the code locally.
In principle, I need to provide service level security credentials. From the documentation for ClientApplication, a supported type for client_credential is a service identity JWT.
.. admonition:: Supporting raw assertion obtained from elsewhere
*Added in version 1.13.0*:
It can also be a completely pre-signed assertion that you've assembled yourself.
Simply pass a container containing only the key "client_assertion", like this::
{
"client_assertion": "...a JWT with claims aud, exp, iss, jti, nbf, and sub..."
}
The documentation here is unclear to the details of the assertion. Who is the targeted audience for this assertion? Could it be anyone? I have also tried other services as the passed in scope for _azure_client_credential.get_token(...), including my backend service. They will all result in valid JWTs being created, but all throw the same error when attempting to obtain the OBO token.
Am I misunderstanding the documentation? Is there another strategy that can be applied to obtain OBO tokens from a middleware service using federated credentials through a service account on AKS? Thanks in advance.