[Bug] ConfidentialClientApplication returns AADSTS700211 when using raw assertion
JJRising opened this issue · 1 comments
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.
In your first attempt, it was successful, so, the OBO works with that user assertion. In your second attempt, everything else being equal, but you were attempting to have your Confidential Client Application (CCA) itself authenticated by another token. That was the federation model, which requires some configuration upfront. You may refer to this Workload identity federation doc.