Skip to content

[Feature Request] Ability to use the Application URI as the client_id in PublicClientApplication #806

Description

MSAL client type

Public

Problem Statement

We have written a python package (wtwco-igloo on PyPi) that allows our users to use Python to connect to their Igloo Cloud environment that is secured with Entra Id. One form of authentication that we support uses the device code flow to allow the user to interactively supply their user credentials to log in to the Enterprise Application as themselves.
However in order for this to work they need to configure wtwco-igloo to use the correct client_id for their Enterprise Application which causes lots of support issues for us.
It would be much nicer if instead of the user having to find the correct client_id, the wtwco-igloo package could instead use the Application URI of the Enterprise Application which, in our case, is simply the domain name of the URL of the Igloo Cloud API.
This is, for instance, how the Excel OData feature authenticates to our Igloo Cloud environment without requiring the user to know what the client_id is.

The frustrating thing is that this msal package is really close to allowing this feature to work, however there is a check in decode_id_token() that checks that the audience claim in the token retrieved from Entra Id is equal to the client_id passed in to the PublicClientApplication. However this won't be the case when the Application URI is used as the Client Id of the Enterprise Application is returned in the token.

See below for a simple example that I would like to work but which raises the error:
IdTokenAudienceError(
msal.oauth2cli.oidc.IdTokenAudienceError: 3. The aud (audience) claim must contain this client's client_id "https://devcon1.cloud.igloo.wtwsaas.dev", case-sensitively. Was your client_id in wrong casing? Current epoch = 2025-04-11 15:41:46. The id_token was approximately: {
"aud": "f5f74c0d-fdd2-473d-b46d-0066c0ee23a9",
"iss":
...

It might be that you consider this issue to be more of a bug than a feature request, I wasn't too sure.
Many thanks,
Bertie

Proposed solution

I would like the following code to work (you will obviously need to use a different application_uri to match an Enterprise Application that you have access to)

import requests
from msal import PublicClientApplication

application_uri = "https://devcon1.cloud.igloo.wtwsaas.dev"
scope = f"{application_uri}/user_impersonation"
authority = "https://login.microsoftonline.com/organizations"

http_client = requests.Session()
msal_client = PublicClientApplication(client_id=application_uri, authority=authority, http_client=http_client)

flow = msal_client.initiate_device_flow(scopes=[scope])
print(flow["message"])
result = msal_client.acquire_token_by_device_flow(flow)

Activity

  1. rayluo commented on Apr 11, 2025

    @rayluo
    Contributor

    By design, an app's client_id is its canonical identifier. The Application URI, on the contrary, might not even be established, or likely has a shape as api://{your_client_id} which isn't more readable than client_id.

    Not sure how your downstream developer got their application uri https://devcon1.cloud.igloo.wtwsaas.dev/. But if they know how to set a new application uri, they shall be able to also find the client_id, perhaps with some FAQ with screenshot from your side?

    While we may evaluate this feature request in the future, the suggestion for you is to stick with client_id whenever possible.

  2. BertieWTW commented on Apr 14, 2025

    @BertieWTW
    Author

    Hi, thanks for the quick response. In our case the Application URI is set up when we deploy the Igloo Cloud environment and is deliberately set to the domain name of the URL hosting the environment. This is very useful as it allows our Clients to connect to the Igloo Cloud OData feed using Excel and Power BI without the need for any additional information except for the OData feed URL itself.
    We would like to support a similar ease of use for our Python connector, where all the Python developer needs to know is the URL to the Igloo Cloud API service.
    As it stands they need both the URL and the Client Id of the Enterprise Application. Obviously this is not a show stopper and we have been supporting this set up for a while now, however it does cause our users some friction. I'd love to see the msal library support this use case if it is simple to achieve.

    As an aside, when I removed the lines:

        if client_id:
            valid_aud = client_id in decoded["aud"] if isinstance(
                decoded["aud"], list) else client_id == decoded["aud"]
            if not valid_aud:
                raise IdTokenAudienceError(
                    "3. The aud (audience) claim must contain this client's client_id "
                    '"%s", case-sensitively. Was your client_id in wrong casing?'
                    # Some IdP accepts wrong casing request but issues right casing IDT
                    % client_id,
                    _now,
                    decoded)
    

    from oidc.py the device code flow completed successfully and I can connect to the Igloo Cloud API.

  3. rayluo commented on May 16, 2025

    @rayluo
    Contributor

    I gave it more thought, but still not quite sure that this client-side SDK (MSAL) shall remove that check broadly. Quoting from the OIDC specs, section 3.1.3.7. ID Token Validation:

    Clients MUST validate the ID Token in the Token Response in the following manner:

    ...
    3. The Client MUST validate that the aud (audience) Claim contains its client_id value registered at the Issuer identified by the iss (issuer) Claim as an audience. The aud (audience) Claim MAY contain an array with more than one element. The ID Token MUST be rejected if the ID Token does not list the Client as a valid audience, or if it contains additional audiences not trusted by the Client.

    I can understand your scenario, Adrian Peck (@BertieWTW) . Perhaps you may consider the following two alternatives.

    1. Given that the Entra ID accepts Application URI as if a client id, yet issues an ID token whose aud claim has currently only the real client id, you may explore getting help from Entra ID to see if Entra ID shall issue ID tokens with aud claims containing both client id and Application URI. That feels like the right solution.
    2. Meanwhile, you may fork this client-side SDK and remove that check by yourself.
  4. BertieWTW commented on May 28, 2025

    @BertieWTW
    Author
  5. rayluo commented on May 29, 2025

    @rayluo
    Contributor

    ... there was no particular need for this part of the msal library to validate the claims as the purpose of this call is simply to retrieve the bearer token which the python application will pass on the relevant web service

    That part is debatable.

    There are two layers here.

    1. The underlying OAuth2 protocol is used to obtain an access token (colloquially known as a "bearer token" although that is not a precise term).
    2. The OpenID Connect protocol which is defined on top of OAuth2 to also obtain an ID token alongside with the access token.

    Your scenario may need #1 only, but MSAL library is implemented at #2 level, therefore the ID token validation is conceptually relevant.

    For the sake of completeness, this MSAL Python library does contain an OAuth2-only component, which can be used to obtain an access token without any ID token validation, but it is NOT part of MSAL Python's official API so it might be changed without prior notice. Besides, using such a low-level building block would also mean that you won't have MSAL's token cache benefit.

    So, realistically speaking, I still think your best bet is the two options that I mentioned in my previous message.

  6. BertieWTW commented on May 29, 2025

    @BertieWTW
    Author
  7. locked and limited conversation to collaborators on Jun 6, 2025
  8. converted this issue into a discussion #828 on Jun 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions