MS App Proxy: works in browser, fails in app with token.

Vahan Avakyan 0 Reputation points
2023-03-08T13:06:59.5466667+00:00

I have a test on-premises Exchange server in internal network, which is externalized with Azure Application Proxy. When accessing the external url with EWS endpoint (/ews/exchange.asmx) from a browser, user is redirected to Microsoft login page, as expected. After authentication user is being navigated to EWS endpoint, also as expected:
Screenshot 1

Enterprise Applications setup:

  1. User is added to "Users and groups".
  2. "Single sign-on" is set to "IWA".
  3. Everything else is set to default. App Registrations setup:
  4. "iOS/macOS" platform is added in "Authenticaion" with correct bundleId.
  5. Everything else is set to default. I have an iOS application with MSAL library integrated, an access token is acquired with this parameters:
  6. clientId: "Application (client) ID" copied from "App registrations" -> "Overview"
  7. authority: "https://login.microsoftonline.com/{Directory (tenant) ID}" copied from "App registrations" -> "Overview"
  8. redirectUri: "msauth.{bundle_id}://auth" same as in "App registrations" -> "Authentication"
  9. scopes: "https://{external_url}.msappproxy.net//user_impersonation"

Access token is acquired without any issues. Here are the decoded contents:

{
    "aud": "https://{external_url}.msappproxy.net/",
    "iss": "https://sts.windows.net/{Directory (tenant) ID}/",
    "iat": 1678210180,
    "nbf": 1678210180,
    "exp": 1678214664,
    "acr": "1",
    "aio": "{Don't know what this is, can provide if necessary}",
    "amr": [
        "pwd",
        "rsa"
    ],
    "appid": "{Application (client) ID}",
    "appidacr": "0",
    "deviceid": "{device_id}",
    "family_name": "{family_name}",
    "given_name": "{given_name}",
    "ipaddr": "{ip_address}",
    "name": "{name}",
    "oid": "{uuid_1}",
    "onprem_sid": "{sid_1}",
    "rh": "{Don't know what this is, can provide if necessary}",
    "scp": "user_impersonation",
    "sub": "{uuid_2}",
    "tid": "{uuid_3}",
    "unique_name": "{same_as_upn}",
    "upn": "{upn}",
    "uti": "{Don't know what this is, can provide if necessary}",
    "ver": "1.0",
    "onpremisessamaccountname": "{upn_without_domain}"
}

When I use this token to access the webpage with external url, I receive a redirect (302) to Microsoft login page:

https://login.microsoftonline.com/{tenant_id}/oauth2/authorize?response_type=code&client_id={client_id}&scope=openid&nonce={uuid_1}&redirect_uri=https://{external_url}.msappproxy.net/&state=AppProxyState:{"InvalidTokenRetry":true,"IsMsofba":false,"OriginalRawUrl":"https://{external_url}.msappproxy.net/EWS/Exchange.asmx","RequestProfileId":"{uuid_2}","SessionId":"{uuid_3}"}#EndOfStateParam#&client-request-id={uuid_4}

I have had this setup numerous times, and never had this kind of issue. I have no clue what to look for anymore, any kind of help is deeply appreciated!

This issue was also reported on MSAL iOS github page:
https://github.com/AzureAD/microsoft-authentication-library-for-objc/issues/1665

Exchange Exchange Server Development
Microsoft Security Microsoft Entra Microsoft Entra ID
{count} votes

1 answer

Sort by: Most helpful
  1. Vahan Avakyan 0 Reputation points
    2023-03-10T11:41:36.48+00:00

    This might be the silliest issue I have ever encountered.

    I spent weeks troubleshooting this issue to find out that "aud" field in the access token, which is generated based on scopes, is case sensitive.

    And it's not case sensitive in a way that it has to be the same as in Azure app exposed api, but it has to be fully lowercased.

    This is clearly a bug on Microsoft side, because if you ask for a token for non-existing audience, it will return an error. But if you ask a token for a scope that contains an uppercase letter in audience part, it will return an invalid token, which can't be used to access the actual resource.

    Please, either fix this or add it to the documentation, it will save people from getting mental disorders.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.