1

I am working on an application with the following components:

  • API (ASP.NET Core)
  • Website
  • Mobile app (Xamarin.Forms)

Users are authenticated with Azure AD, and should receive a bearer token to access the API. For the webpage, this is working as expected, but I can't seem to get it working for the mobile app.

I can authenticate using the Microsoft.identity.Client library, and I do receive an access token. However, this access token is not working, as calls to the API with this token return 401 Unauthorized, with a response header:

Www-Authenticate: Bearer error="invalid_token", error_description="The signature is invalid"
  • When I use the bearer token from the webapp hardcoded in the mobile app, it works.
  • When I compare the two tokens, I see that the token retrieved by the mobile app is a v1 token, as opposed to the v2 token that the webapp receives.

In the Azure App Registrations, I have a registration for all 3 components, and granted API permissions to the website and mobile app.

Authenticating and acquiring access token in the mobile app:

var app = PublicClientApplicationBuilder.Create(clientId)
    .WithRedirectUri("msal116d9cbe-88ef-4b73-bae2-0d21c10df305://auth")
    .WithIosKeychainSecurityGroup(this.configuration.IOSKeychainSecurityGroup)
    .WithB2CAuthority("https://testorganisation.b2clogin.com/tfp/testorganisation.onmicrosoft.com/B2C_1_SignupSignin/")
            .Build();

var scopes = new string[]
{
    "openid",
    "email",
    "https://testorganisation.onmicrosoft.com/api/access_as_user"
}

var accounts = await this.app
    .GetAccountsAsync()

await this.app
    .AcquireTokenSilent(scopes, accounts.FirstOrDefault())
    .ExecuteAsync()

API App Registration Manifest from Azure:

{
    "id": "b003da59-5a76-4083-99e6-bdb708feae16",
    "acceptMappedClaims": null,
    "accessTokenAcceptedVersion": 2,
    "addIns": [],
    "allowPublicClient": true,
    "appId": "9bf40874-747a-41d5-a3fe-2d0b1a3fcfa8",
    "appRoles": [],
    "oauth2AllowUrlPathMatching": false,
    "createdDateTime": "2021-10-27T08:32:35Z",
    "certification": null,
    "disabledByMicrosoftStatus": null,
    "groupMembershipClaims": null,
    "identifierUris": [
        "https://testorganisation.onmicrosoft.com/api"
    ],
    "informationalUrls": {
        "termsOfService": null,
        "support": null,
        "privacy": null,
        "marketing": null
    },
    "keyCredentials": [],
    "knownClientApplications": [],
    "logoUrl": null,
    "logoutUrl": null,
    "name": "testorganisation API",
    "oauth2AllowIdTokenImplicitFlow": false,
    "oauth2AllowImplicitFlow": false,
    "oauth2Permissions": [
        {
            "adminConsentDescription": "Access the testorganisation API as a user",
            "adminConsentDisplayName": "Access testorganisation API as user",
            "id": "1e8c2b96-7b60-4a32-9145-1083e1fba86b",
            "isEnabled": true,
            "lang": null,
            "origin": "Application",
            "type": "Admin",
            "userConsentDescription": null,
            "userConsentDisplayName": null,
            "value": "access_as_user"
        },
        {
            "adminConsentDescription": "Access the API as administrator",
            "adminConsentDisplayName": "Access testorganisation API as admin",
            "id": "63a54831-11a2-47f2-8e50-2872f1c25d5d",
            "isEnabled": true,
            "lang": null,
            "origin": "Application",
            "type": "Admin",
            "userConsentDescription": null,
            "userConsentDisplayName": null,
            "value": "access_as_admin"
        }
    ],
    "oauth2RequirePostResponse": false,
    "optionalClaims": null,
    "orgRestrictions": [],
    "parentalControlSettings": {
        "countriesBlockedForMinors": [],
        "legalAgeGroupRule": "Allow"
    },
    "passwordCredentials": [],
    "preAuthorizedApplications": [],
    "publisherDomain": "testorganisation.onmicrosoft.com",
    "replyUrlsWithType": [
        {
            "url": "https://localhost:5001/swagger/oauth2-redirect.html",
            "type": "Spa"
        }
    ],
    "requiredResourceAccess": [
        {
            "resourceAppId": "00000003-0000-0000-c000-000000000000",
            "resourceAccess": [
                {
                    "id": "37f7f235-527c-4136-accd-4a02d197296e",
                    "type": "Scope"
                },
                {
                    "id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182",
                    "type": "Scope"
                }
            ]
        }
    ],
    "samlMetadataUrl": null,
    "signInUrl": null,
    "signInAudience": "AzureADandPersonalMicrosoftAccount",
    "tags": [
        "notApiConsumer",
        "webApi"
    ],
    "tokenEncryptionKeyId": null
}

Mobile App Registration Manifest from Azure:

{
    "id": "1cc5a0bc-13dc-4101-9cff-ace48ad4865d",
    "acceptMappedClaims": null,
    "accessTokenAcceptedVersion": 2,
    "addIns": [],
    "allowPublicClient": true,
    "appId": "116d9cbe-88ef-4b73-bae2-0d21c10df305",
    "appRoles": [],
    "oauth2AllowUrlPathMatching": false,
    "createdDateTime": "2021-01-13T19:37:49Z",
    "certification": null,
    "disabledByMicrosoftStatus": null,
    "groupMembershipClaims": null,
    "identifierUris": [],
    "informationalUrls": {
        "termsOfService": null,
        "support": null,
        "privacy": null,
        "marketing": null
    },
    "keyCredentials": [],
    "knownClientApplications": [],
    "logoUrl": null,
    "logoutUrl": null,
    "name": "testorganisation Mobile App",
    "oauth2AllowIdTokenImplicitFlow": false,
    "oauth2AllowImplicitFlow": false,
    "oauth2Permissions": [],
    "oauth2RequirePostResponse": false,
    "optionalClaims": null,
    "orgRestrictions": [],
    "parentalControlSettings": {
        "countriesBlockedForMinors": [],
        "legalAgeGroupRule": "Allow"
    },
    "passwordCredentials": [],
    "preAuthorizedApplications": [],
    "publisherDomain": "testorganisation.onmicrosoft.com",
    "replyUrlsWithType": [
        {
            "url": "msal116d9cbe-88ef-4b73-bae2-0d21c10df305://auth",
            "type": "InstalledClient"
        }
    ],
    "requiredResourceAccess": [
        {
            "resourceAppId": "9bf40874-747a-41d5-a3fe-2d0b1a3fcfa8",
            "resourceAccess": [
                {
                    "id": "1e8c2b96-7b60-4a32-9145-1083e1fba86b",
                    "type": "Scope"
                }
            ]
        },
        {
            "resourceAppId": "00000003-0000-0000-c000-000000000000",
            "resourceAccess": [
                {
                    "id": "37f7f235-527c-4136-accd-4a02d197296e",
                    "type": "Scope"
                },
                {
                    "id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182",
                    "type": "Scope"
                }
            ]
        }
    ],
    "samlMetadataUrl": null,
    "signInUrl": null,
    "signInAudience": "AzureADandPersonalMicrosoftAccount",
    "tags": [
        "apiConsumer",
        "mobileApp"
    ],
    "tokenEncryptionKeyId": null
}
Daan
  • 197
  • 1
  • 2
  • 11
  • What is the ```redirect URL``` configured for your App registration for the platform : mobile and desktop applications? – Anand Sowmithiran Nov 14 '21 at 16:01
  • Refer this to know about token [formats](https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens#token-formats-and-ownership). And you can mention the version in your ```app manifest``` file, see the [ms doc page](https://learn.microsoft.com/en-us/azure/active-directory/develop/reference-app-manifest#manifest-reference). – Anand Sowmithiran Nov 14 '21 at 16:05
  • This link(https://stackoverflow.com/questions/63755540/azure-active-directory-bearer-error-invalid-token-error-description-the-sig?rq=1) may help you. – Wen xu Li Nov 18 '21 at 09:44

1 Answers1

0

• It is quite clear that you are receiving a v1 access token on your mobile app instead of v2 access token even after configuring the ‘accessTokenAcceptedVersion’ to 2 as seen from your app manifest code for mobile application whereas the same is not happening for web app API. Thus please use the below sample code to use the parameter ‘AcquireTokenOnBehalfOf’ so that the token that is passed to the web app as v2 token and gets decoded correctly will also be passed on to the mobile application API and get recognized correctly. So, update your msal.net code accordingly to call the mobile app api as below: -

MSAL.net code that is required

    ‘ string[] scopes = { "user.read" };
        string accesstoken = "";

        string appKey = "yor web api client secret";
        string clientId = "your web api application id";

        var app = ConfidentialClientApplicationBuilder.Create(clientId)
          .WithClientSecret(appKey)
          .Build();
        UserAssertion userAssertion = new UserAssertion(accesstoken, 
     "urn:ietf:params:oauth:grant-type:jwt-bearer");
        var result = app.AcquireTokenOnBehalfOf(scopes, 
      userAssertion).ExecuteAsync().Result;
        Console.WriteLine(result.AccessToken); ‘

This parameter of ‘AcquireTokenOnBehalfOf’ is surely missing due to which the token passed to the mobile app is considered as v1 instead of v2. Also, please check the scope of the mobile app API which should be ‘user_impersonification’.

Please find the below link for reference: -

https://github.com/Azure-Samples/ms-identity-aspnet-webapi-onbehalfof

Kartik Bhiwapurkar
  • 4,550
  • 2
  • 4
  • 9
  • The example shows a ConfidentalClientApplication, which together with the AcquireTokenOnBehalfOf method is unusable in a public client application like a mobile app – Daan Nov 17 '21 at 21:08
  • Please try appending '.default' to the app resource URI and then check again. – Kartik Bhiwapurkar Nov 18 '21 at 17:31
  • Also try providing the resource URI as the scope. – Kartik Bhiwapurkar Nov 18 '21 at 17:45
  • Thanks for your time. I tried using scopes `"openid", "email", "https://testorganisation.onmicrosoft.com/api/.default"` but this does not give me an access token at all, and I tried `"openid", "email", "https://testorganisation.onmicrosoft.com/api/access_as_user", "https://testorganisation.onmicrosoft.com/api/.default` and this gives me the same v1 token structure – Daan Nov 19 '21 at 08:54
  • Hi @Daan, you can refer to this stackover flow thread for reference and more details - https://stackoverflow.com/questions/54368394/is-it-possible-to-obtain-an-azure-ad-v1-token-using-msal – Kartik Bhiwapurkar Nov 22 '21 at 05:34