.net-coreazure-active-directoryasp.net-identityazure-ad-msalazure-management-api

Login to Azure Management API from .NET Console app


I am creating a console application that logs in into my Line of Business application and also call some Azure Management APIs. My Client Application has permissions to do both and it can do it from my Blazor WASM web app from the client side here are the permission in Azure: Azure Permissions for Azure App I have this Authentication code for the console application (which works for login to my web application)

public static async Task<AuthenticationResult> GetTokenAsync(string userName)
        {
            //ref https://github.com/AzureAD/microsoft-authentication-extensions-for-dotnet/blob/master/sample/ManualTestApp/Program.cs
            IPublicClientApplication app = PublicClientApplicationBuilder
            .Create(AuthCacheConfig.ClientId)
            .WithAuthority(AuthCacheConfig.Authority)
            .WithRedirectUri(AuthCacheConfig.RedirectURI)
            .Build();
            MsalCacheHelper cacheHelper = await CreateCacheHelperAsync().ConfigureAwait(false);
            cacheHelper.RegisterCache(app.UserTokenCache);
            IEnumerable<IAccount> accounts = await app.GetAccountsAsync();
            AuthenticationResult result;
            if (accounts.Any())
            {
                IAccount selectedAccount = GetSelectedAccount(accounts.ToList(), userName);
                try
                {
                    result = await app.AcquireTokenSilent(AuthCacheConfig.Scopes, selectedAccount)
                                .ExecuteAsync();
                }
                catch (MsalUiRequiredException)
                {
                    result = await app.AcquireTokenInteractive(AuthCacheConfig.Scopes)
                                .ExecuteAsync();
                }
            }
            else
            {
                result = await app.AcquireTokenInteractive(AuthCacheConfig.Scopes)
                                .ExecuteAsync();
            }
            return result;
        }

Where AuthCacheConfig.ClientId = MYAPPCLIENTID AuthCacheConfig.Authority = "https://login.microsoftonline.com/common" and AuthCacheConfig.Scopes is my app scope when trying to get a token for the App and https://management.azure.com/user_impersonation when trying to get a token for ARM. I looked at the token that the web application can successfully generate in JWT.ms and have this result:

{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "",
  "kid": ""
}.{
  "aud": "https://management.azure.com",
  "iss": "https://sts.windows.net/TENANTID/",
  "iat": 1605579273,
  "nbf": 1605579273,
  "exp": 1605583173,
  "acr": "1",
  "aio": "REMOVED FOR PRIVACY",
  "amr": [
    "rsa",
    "mfa"
  ],
  "appid": "MYCLIENTAPPID",
  "appidacr": "0",
  "family_name": "MYLASTNAME",
  "given_name": "MYFIRSTNAME",
  "groups": [
    "AADGROUP0",
    "AADGROUP1"
  ],
  "ipaddr": "MYIPADDRES",
  "name": "MYNAME",
  "oid": "MYOID",
  "puid": "REMOVED FOR PRIVACY",
  "rh": "REMOVED FOR PRIVACY",
  "scp": "user_impersonation",
  "sub": "REMOVED FOR PRIVACY",
  "tid": "MYTENANTID",
  "unique_name": "MYUPN",
  "upn": "MYUPN",
  "uti": "REMOVED FOR PRIVACY",
  "ver": "1.0",
  "wids": [
    "wid0",
    "wid1",
    "wid2"
  ],

and here is the token for when I call from the console app:

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "REMOVED FOR PRIVACY"
}.{
  "aud": "MYAPPID",
  "iss": "https://login.microsoftonline.com/TENANTID/v2.0",
  "iat": 1605579071,
  "nbf": 1605579071,
  "exp": 1605582971,
  "aio": "REMOVED FOR PRIVACY",
  "name": "MYNAME",
  "oid": "MYOID",
  "preferred_username": "mMYUPN",
  "rh": "REMOVED FOR PRIVACY",
  "roles": [
    "Administrator"
  ],
  "sub": "REMOVED FOR PRIVACY",
  "tid": "MYTENANTID",
  "uti": "REMOVED FOR PRIVACY",
  "ver": "2.0"
}

I cant find a way to specify the audience in the MSAL Library, is there a way to do this? Since from what I can see in the token, Microsoft STS is not seeing that the request is for the Azure Service Management Scope?


Solution

  • the error was caused because the the V1 endpoint expect a slash in the audience claim and the other to separate the API Name from the scope.

    TL;DR change scopes from var scopes = new[] {"https://management.core.windows.net/user_impersonation"}; to this var scopes = new[] {"https://management.core.windows.net//user_impersonation"}; and it will work.

    to save someone else the two hours of googling here is the doc on calling endpoints that still don't support v2 tokens: https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-v1-app-scopes