8

I am implementing an Azure Active Directory in a .NET 5 API. I currently have this API perfectly running on .NET Core 2.2.

This is the old working code:

services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
    .AddAzureADBearer(options =>
    {
         options.Instance = "https://login.microsoftonline.com/";
         options.Domain = backOfficeADDomain;
         options.TenantId = backOfficeADTenantId;
         options.ClientId = $"api://{backOfficeADAPIClientId}";
         options.ClientSecret = backOfficeADAPISecret;
    });

But since the update to .NET 5 I get this warning:

'AzureADAuthenticationBuilderExtensions.AddAzureADBearer(AuthenticationBuilder, Action)' is obsolete: 'This is obsolete and will be removed in a future version. Use AddMicrosoftWebApiAuthentication from Microsoft.Identity.Web instead. See https://aka.ms/ms-identity-web.'

So I tried updating it to this:

services.AddMicrosoftIdentityWebApiAuthentication(_configuration, "AzureAd");

It seems that an "AzureAd" section in the appsettings.json is the only way to pass the credentials. How can I manually enter the Instance, domain, ClientId, etc..? I don't use the appsettings.json, all the data is manually retrieved from AzureKeyVault.

Thank you!

J Flex
  • 312
  • 1
  • 3
  • 11
  • using KeyVault shouldn't change anything if using IConfiguration and just adding KV as a new source – Poat Jun 06 '23 at 14:04

3 Answers3

8

Assuming you have a good reason to not use a the configuration value from your settings, you can add an inmemory provider.

You can also create a configuration that is used only for this extension method :

var azureAdConfig = new ConfigurationBuilder()
    .AddInMemoryCollection(new Dictionary<string, string>
    {
        {"AzureAd:Instance", "https://login.microsoftonline.com/"},
        {"AzureAd:Domain", backOfficeADDomain}
        //...
    })
    .Build();

services.AddMicrosoftIdentityWebApiAuthentication(azureAdConfig);
saad
  • 764
  • 4
  • 18
4

Ok, I found it!

It's actually really easy:

IConfigurationSection azureAdSection = _configuration.GetSection("AzureAd");

azureAdSection.GetSection("Instance").Value = "https://login.microsoftonline.com/";
azureAdSection.GetSection("Domain").Value = backOfficeADDomain;
azureAdSection.GetSection("TenantId").Value = backOfficeADTenantId;
azureAdSection.GetSection("ClientId").Value = backOfficeADAPIClientId;
azureAdSection.GetSection("ClientSecret").Value = backOfficeADAPISecret;

services.AddMicrosoftIdentityWebApiAuthentication(_configuration, "AzureAd");

It seems that after a whole day of complex code refactoring my brain couldn't comprehend such an easy solution.

Note that I also had to remove the "api://" from the clientId. It looks like the new version adds it automatically. It tried to validate "api://api://".

J Flex
  • 312
  • 1
  • 3
  • 11
1

It is an old question but maybe it is worth adding few cents here. First of all using Azure KeyVault to store configuration values doesn't prevent you from using IConfiguration abstraction to get those values. All you need is Azure.Extensions.AspNetCore.Configuration.Secrets nuget package and then register new configuration source with builder.Configuration.AddAzureKeyVault method. Generally that's what IConfiguration and IConfigurationSource abstractions are for. To keep your application logic clean from reading configuration values no matter of their source.

With second approach there is also cleaner way to do this. You may simply take advantage of the fact that options pattern is used underneath. Therefore you may register your own IPostConfigureOptions<JwtBearerOptions> implementation. For example:

    public class AuthenticationOptionsConfiguration : IPostConfigureOptions<JwtBearerOptions>
    {
        private readonly YourDependency _dependency;
    
        public AuthenticationOptionsConfiguration(YourDependency dependency)
        {
            _dependency = dependency;
        }
    
        public void PostConfigure(string name, JwtBearerOptions options)
        {
            options.Instance = "https://login.microsoftonline.com/";
            options.Domain = _dependency.BackOfficeADDomain;
            (...)
        }
    }

and register it with builder.Services.ConfigureOptions<AuthenticationOptionsConfiguration>(); This way you may also get access to options.Events property and add some handlers if needed.

Esset
  • 916
  • 2
  • 15
  • 17