AzureAD / microsoft-identity-web

Helps creating protected web apps and web APIs with Microsoft identity platform and Azure AD B2C
MIT License
679 stars 209 forks source link

Update MicrosoftIdentityOptions(ClientId/ClientSecret) in runtime #1299

Open ndebata opened 3 years ago

ndebata commented 3 years ago

We are using Microsoft.Identity.Web for a web app which calls Microsoft Graph API where MicrosoftIdentityOptions are configured in startup from configuration. When secrets expire or regular key rotation we update the secret. How do we update the configuration without application restart?

services.Configure<AzureAdConfiguration>(Configuration.GetSection(
                                        AzureAdConfiguration.SectionName));
            services.Configure<MSGraphConfiguration>(Configuration.GetSection(
                                        MSGraphConfiguration.SectionName));

            var mSGraphAPIConfiguration = Configuration.GetSection(MSGraphConfiguration.SectionName).Get<MSGraphConfiguration>();
            var initialScopes = mSGraphAPIConfiguration.Scopes?.Split(' ');
            services.AddSingleton<OpenIdConnectPostConfigureOptions>();
            services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
                .AddMicrosoftIdentityWebApp(options => Configuration.Bind(AzureAdConfiguration.SectionName, options))
                .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
                .AddMicrosoftGraph(graphOptions => Configuration.Bind(MSGraphConfiguration.SectionName, graphOptions))
                .AddInMemoryTokenCaches();
jmprieur commented 3 years ago

@ndebata. I'm assuming that this is for certificate credentials (not decrypt certificates): If you use the certificate description configuration properties, Microsoft.Identity.Web will retry automatically fetching a new certificate version from KeyVault if a certificate expired. See https://github.com/AzureAD/microsoft-identity-web/wiki/Certificates#describing-client-certificates-to-use-by-configuration

Does this work for you? or do you need more fine grain control?

ndebata commented 3 years ago

@jmprieur Thank you for your answer. We are using client secret instead of certificates and configurations are loaded from configuration service using custom configuration provider which reads it from key vault. IOptionsMonitor.CurrentValue has the latest value updated from Azure Ad Configuration configuration. Can you please describe the fine grain control for using the updated client secret.

jmprieur commented 3 years ago

@ndebata: you can configure the options like this from Startup.cs.

services.Configure<ConfidentialClientApplicationOptions>(OpenIdConnectDefault.AuthenticationScheme, options =>
{
 options.ClientSecret = "";
});

Where do you want to do that? from a controller/page?

ndebata commented 3 years ago

This way secret will be initialized once during application start, looking for an option when application is running. Once configuration changes secrets are refreshed and I can see the updated value using IOptionsMonitor but application is not using the value. Is there any way we can refresh the secrets used by Microsoft.Identity.Web without restarting the application. I can invoke the same from HostedService in background worker.

jmprieur commented 3 years ago

@ndebata. I think that we'd need to add listeners to the IOptionMonitor of MicrosoftIdentityOptions and ConfidentialClientApplicationOptions so that we update the (internal) MergeOptions when those change.

Do you have the possibility of tyring out if this branch solves your issue? https://github.com/AzureAD/microsoft-identity-web/tree/jmprieur/investigate1299

cc: @jennyf19.

agyss commented 1 year ago

Is there any news on this?

agyss commented 1 year ago

In my usecase, the user may decide during the runtime of the app to which tenant he wants to authenticate (he may be guest in multiple tenants and my app is fetching data from whichever tenant the user decides - this, while the "initial" authentication was to his/her home tenant and also data from the fetching operation is stored there). Long story, short, I came up with this:

            services.AddAuthentication()
                .AddMicrosoftIdentityWebApp(options =>
                {
                    Configuration.Bind("AzureADapp", options);
                    options.Events.OnRedirectToIdentityProvider = (context) =>
                    {
                        var tenantId = context.HttpContext.RequestServices.GetService(ITenantService).GetTenant().Id;
                        // Updating the IssuerUrl to the tenant Endpoint instead of common makes sure the user is authenticated to the desired tenant instead of the users home tenant.
                        var updatedIssuerUrl = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize";
                        context.ProtocolMessage.IssuerAddress = updatedIssuerUrl;

                        return Task.FromResult(0);
                    };
                }

While in the appsettings.json, seciton "AzureADapp" all details of the used App are stored, the https://login.microsoftonline.com/common endpoint is configured there. By overwriting this, I'm able to specify the tenant to which the user is authenticated.

jamescarterbellMSFT commented 8 months ago

Having this middleware subscribe to the reload token similar to how kestrel does would be a very helpful change.