3

Trying to Authenticate against Azure Management API using their SDK. I can get the user authenticated with the MSAL.NET SDK. But when I try to pass the Bearer token for ClientCrendentials I can and AuthorizationFailed Message.

I've enabled user_impersination and delegate permissions in my Active Directory instance and register my application through the application portal.

The Tenant is set to common

   class Program
   {

       static readonly string TenantID = ConfigurationManager.AppSettings.Get("tenant_id");
       static readonly string ClientID = ConfigurationManager.AppSettings.Get("client_id");
       static readonly string Scopes = ConfigurationManager.AppSettings.Get("scopes");

       static AuthenticationResult Authentication { get; set; }
       static AzureEnvironment AzureEnvironment => AzureEnvironment.AzureGlobalCloud;

       static void Main(string[] args)
       {
           // useful links
           // Micorosft.Identity.Client https://github.com/AzureAD/microsoft-authentication-library-for-dotnet
           DoLoginAsync().Wait();
           CallAzure().Wait();
           //CallMsGraphAPI().Wait();

           Console.Read();
       }

       static async Task DoLoginAsync()
       {
           try
           {
               IPublicClientApplication client = PublicClientApplicationBuilder.Create(ClientID)
                   .WithAuthority(AzureCloudInstance.AzurePublic, TenantID)
                   .Build();

               Authentication = await client.AcquireTokenInteractive(Scopes.Split(','))
                   .ExecuteAsync();
           }
           catch (Exception ex)
           {
               Console.WriteLine(ex);
           }
       }

       static async Task CallAzure()
       {
           try
           {
               var client = RestClient.Configure()
                   .WithEnvironment(AzureEnvironment)
                   .WithCredentials(GetCredentials())
                   .WithLogLevel(HttpLoggingDelegatingHandler.Level.BodyAndHeaders)
                   .Build();

               var subscriptionClient = new SubscriptionClient(client);

               var subscriptions = await subscriptionClient.Subscriptions.ListAsync();

               Console.WriteLine(subscriptions); // fails
           }
           catch(Exception ex)
           {
               Console.WriteLine(ex);
           }
       }

       static AzureCredentials GetCredentials()
       {
           var provider = new StringTokenProvider(Authentication.AccessToken, "Bearer");
           var tokenCredentials = new TokenCredentials(provider, TenantID, Authentication.Account.Username);

           return new AzureCredentials(tokenCredentials, tokenCredentials, TenantID, AzureEnvironment);
       }
   }

I would think the user could be authorized using the Bearer Token given back in the GetCredentials method I have.

1 Answers1

2

I managed to work through the issue and had two things worth pointing out

  1. The Audience is the Account TenantId. If you're unsure of how this works you can learn more about it on the Official Microsoft Page.
  2. The scopes parameter which appears to look like it supports multiple scopes, it indeed does not. Passing more than one scope causes an error to occur

Useful resources

  • single to multi tenant applications
  • application audiences
  • choosing an authentication provider
  • Best practices for ConfigureAwait

    class Program
    {
        static AuthenticationResult AuthenticationResult { get; set; }
        static readonly string ClientId = ConfigurationManager.AppSettings.Get("ClientId") ?? throw new ApplicationException("No ClientID configured in <appsettings /> App.Config");
        static readonly IEnumerable<string> Scopes = new[] { "https://management.azure.com/user_impersonation" };
    
        static IPublicClientApplication App { get; set; }
    
        static void Main(string[] args)
        {
            App = PublicClientApplicationBuilder.Create(ClientId)
                    .WithLogging((level, message, containsPii) =>
                    {
                        Console.WriteLine("Error when using Public Client");
                        Console.WriteLine($"{level}: {message}");
                    }, LogLevel.Verbose, true, true)
                    .WithAuthority(AzureCloudInstance.AzurePublic, AadAuthorityAudience.AzureAdMultipleOrgs, true)
                    .Build();
    
            DoLoginAsync().Wait();
            CallAzureMangementRestApiAsync().Wait();
        }
    
        static async Task DoLoginAsync()
        {
            try
            {
                var accounts = await App.GetAccountsAsync().ConfigureAwait(false);
    
                try
                {
                    AuthenticationResult = await App.AcquireTokenSilent(Scopes, accounts.FirstOrDefault())
                        .ExecuteAsync()
                        .ConfigureAwait(false);
                }
                catch (MsalUiRequiredException)
                {
                    AuthenticationResult = await App.AcquireTokenInteractive(Scopes)
                        .ExecuteAsync()
                        .ConfigureAwait(false);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }
    
        static async Task CallAzureMangementRestApiAsync()
        {
            try
            {
                try
                {
                    var accounts = await App.GetAccountsAsync().ConfigureAwait(false);
    
                    AuthenticationResult = await App.AcquireTokenSilent(Scopes, accounts.FirstOrDefault())
                        .WithAuthority(AzureCloudInstance.AzurePublic, AuthenticationResult.TenantId)
                        .ExecuteAsync()
                        .ConfigureAwait(false);
                }
                catch (MsalUiRequiredException)
                {
                    // UI needs to have the user call in
                    AuthenticationResult = await App.AcquireTokenInteractive(Scopes)
                        .WithAuthority(AzureCloudInstance.AzurePublic, AuthenticationResult.TenantId)
                        .ExecuteAsync()
                        .ConfigureAwait(false);
                }
    
                var client = RestClient.Configure()
                    .WithEnvironment(AzureEnvironment.FromName(AuthenticationResult?.Account?.Environment) ?? AzureEnvironment.AzureGlobalCloud)
                    .WithCredentials(GetAzureCredentials())
                    .WithLogLevel(HttpLoggingDelegatingHandler.Level.BodyAndHeaders)
                    .Build();
    
                using (var subscriptionClient = new SubscriptionClient(client))
                {
                    var subscriptions = await subscriptionClient.Subscriptions
                        .ListAsync()
                        .ConfigureAwait(false);
    
                    foreach (var s in subscriptions)
                    {
                        Console.WriteLine($"Id={s.Id};subscriptionId={s.SubscriptionId};displayName={s.DisplayName}");
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }
    
        static AzureCredentials GetAzureCredentials()
        {
            var provider = new StringTokenProvider(AuthenticationResult.AccessToken, "Bearer");
            var token = new TokenCredentials(provider, AuthenticationResult.TenantId, AuthenticationResult.IdToken != null ? AuthenticationResult.UniqueId : AuthenticationResult.IdToken);
    
            return new AzureCredentials(token, token, AuthenticationResult.TenantId, AzureEnvironment.AzureGlobalCloud);
        }
    }