private static void AddMicrosoftIdentityWebApiImplementation( AuthenticationBuilder builder, Action <JwtBearerOptions> configureJwtBearerOptions, Action <MicrosoftIdentityOptions> configureMicrosoftIdentityOptions, string jwtBearerScheme, bool subscribeToJwtBearerMiddlewareDiagnosticsEvents) { builder.AddJwtBearer(jwtBearerScheme, configureJwtBearerOptions); builder.Services.Configure(jwtBearerScheme, configureMicrosoftIdentityOptions); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton <IValidateOptions <MicrosoftIdentityOptions>, MicrosoftIdentityOptionsValidation>()); builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpClient(); builder.Services.TryAddSingleton <MicrosoftIdentityIssuerValidatorFactory>(); builder.Services.AddOptions <AadIssuerValidatorOptions>(); if (subscribeToJwtBearerMiddlewareDiagnosticsEvents) { builder.Services.AddSingleton <IJwtBearerMiddlewareDiagnostics, JwtBearerMiddlewareDiagnostics>(); } // Change the authentication configuration to accommodate the Microsoft identity platform endpoint (v2.0). builder.Services.AddOptions <JwtBearerOptions>(jwtBearerScheme) .Configure <IServiceProvider, IOptionsMonitor <MicrosoftIdentityOptions> >((options, serviceProvider, microsoftIdentityOptionsMonitor) => { var microsoftIdentityOptions = microsoftIdentityOptionsMonitor.Get(jwtBearerScheme); if (string.IsNullOrWhiteSpace(options.Authority)) { options.Authority = AuthorityHelpers.BuildAuthority(microsoftIdentityOptions); } // This is a Microsoft identity platform web API options.Authority = AuthorityHelpers.EnsureAuthorityIsV2(options.Authority); if (options.TokenValidationParameters.AudienceValidator == null && options.TokenValidationParameters.ValidAudience == null && options.TokenValidationParameters.ValidAudiences == null) { RegisterValidAudience registerAudience = new RegisterValidAudience(); registerAudience.RegisterAudienceValidation( options.TokenValidationParameters, microsoftIdentityOptions); } // If the developer registered an IssuerValidator, do not overwrite it if (options.TokenValidationParameters.IssuerValidator == null) { // Instead of using the default validation (validating against a single tenant, as we do in line of business apps), // we inject our own multi-tenant validation logic (which even accepts both v1.0 and v2.0 tokens) MicrosoftIdentityIssuerValidatorFactory microsoftIdentityIssuerValidatorFactory = serviceProvider.GetRequiredService <MicrosoftIdentityIssuerValidatorFactory>(); options.TokenValidationParameters.IssuerValidator = microsoftIdentityIssuerValidatorFactory.GetAadIssuerValidator(options.Authority).Validate; } // If you provide a token decryption certificate, it will be used to decrypt the token if (microsoftIdentityOptions.TokenDecryptionCertificates != null) { options.TokenValidationParameters.TokenDecryptionKey = new X509SecurityKey(DefaultCertificateLoader.LoadFirstCertificate(microsoftIdentityOptions.TokenDecryptionCertificates)); } if (options.Events == null) { options.Events = new JwtBearerEvents(); } // When an access token for our own web API is validated, we add it to MSAL.NET's cache so that it can // be used from the controllers. var tokenValidatedHandler = options.Events.OnTokenValidated; options.Events.OnTokenValidated = async context => { if (!microsoftIdentityOptions.AllowWebApiToBeAuthorizedByACL) { // This check is required to ensure that the web API only accepts tokens from tenants where it has been consented and provisioned. if (!context.Principal.Claims.Any(x => x.Type == ClaimConstants.Scope) && !context.Principal.Claims.Any(y => y.Type == ClaimConstants.Scp) && !context.Principal.Claims.Any(y => y.Type == ClaimConstants.Roles) && !context.Principal.Claims.Any(y => y.Type == ClaimConstants.Role)) { throw new UnauthorizedAccessException(IDWebErrorMessage.NeitherScopeOrRolesClaimFoundInToken); } } await tokenValidatedHandler(context).ConfigureAwait(false); }; if (subscribeToJwtBearerMiddlewareDiagnosticsEvents) { var diagnostics = serviceProvider.GetRequiredService <IJwtBearerMiddlewareDiagnostics>(); diagnostics.Subscribe(options.Events); } }); }
/// <summary> /// Add authentication with Microsoft identity platform. /// This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. /// </summary> /// <param name="builder">AuthenticationBuilder to which to add this configuration</param> /// <param name="configureOpenIdConnectOptions">The IConfiguration object</param> /// <param name="configureMicrosoftIdentityOptions">The configuration section with the necessary settings to initialize authentication options</param> /// <param name="openIdConnectScheme">The OpenIdConnect scheme name to be used. By default it uses "OpenIdConnect"</param> /// <param name="cookieScheme">The Cookies scheme name to be used. By default it uses "Cookies"</param> /// <param name="subscribeToOpenIdConnectMiddlewareDiagnosticsEvents"> /// Set to true if you want to debug, or just understand the OpenIdConnect events. /// </param> /// <returns>The authentication builder for chaining</returns> public static AuthenticationBuilder AddSignIn( this AuthenticationBuilder builder, Action <OpenIdConnectOptions> configureOpenIdConnectOptions, Action <MicrosoftIdentityOptions> configureMicrosoftIdentityOptions, string openIdConnectScheme = OpenIdConnectDefaults.AuthenticationScheme, string cookieScheme = CookieAuthenticationDefaults.AuthenticationScheme, bool subscribeToOpenIdConnectMiddlewareDiagnosticsEvents = false) { builder.Services.Configure(openIdConnectScheme, configureOpenIdConnectOptions); builder.Services.Configure <MicrosoftIdentityOptions>(configureMicrosoftIdentityOptions); builder.Services.AddHttpClient(); var microsoftIdentityOptions = new MicrosoftIdentityOptions(); configureMicrosoftIdentityOptions(microsoftIdentityOptions); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton <IValidateOptions <MicrosoftIdentityOptions>, MicrosoftIdentityOptionsValidation>()); var b2cOidcHandlers = new AzureADB2COpenIDConnectEventHandlers(openIdConnectScheme, microsoftIdentityOptions); builder.Services.AddSingleton <IOpenIdConnectMiddlewareDiagnostics, OpenIdConnectMiddlewareDiagnostics>(); builder.AddCookie(cookieScheme); builder.AddOpenIdConnect(openIdConnectScheme, options => { options.SignInScheme = cookieScheme; if (string.IsNullOrWhiteSpace(options.Authority)) { options.Authority = AuthorityHelpers.BuildAuthority(microsoftIdentityOptions); } // This is a Microsoft identity platform Web app options.Authority = AuthorityHelpers.EnsureAuthorityIsV2(options.Authority); // B2C doesn't have preferred_username claims if (microsoftIdentityOptions.IsB2C) { options.TokenValidationParameters.NameClaimType = "name"; } else { options.TokenValidationParameters.NameClaimType = "preferred_username"; } // If the developer registered an IssuerValidator, do not overwrite it if (options.TokenValidationParameters.IssuerValidator == null) { // If you want to restrict the users that can sign-in to several organizations // Set the tenant value in the appsettings.json file to 'organizations', and add the // issuers you want to accept to options.TokenValidationParameters.ValidIssuers collection options.TokenValidationParameters.IssuerValidator = AadIssuerValidator.GetIssuerValidator(options.Authority).Validate; } // Avoids having users being presented the select account dialog when they are already signed-in // for instance when going through incremental consent var redirectToIdpHandler = options.Events.OnRedirectToIdentityProvider; options.Events.OnRedirectToIdentityProvider = async context => { var login = context.Properties.GetParameter <string>(OpenIdConnectParameterNames.LoginHint); if (!string.IsNullOrWhiteSpace(login)) { context.ProtocolMessage.LoginHint = login; context.ProtocolMessage.DomainHint = context.Properties.GetParameter <string>( OpenIdConnectParameterNames.DomainHint); // delete the login_hint and domainHint from the Properties when we are done otherwise // it will take up extra space in the cookie. context.Properties.Parameters.Remove(OpenIdConnectParameterNames.LoginHint); context.Properties.Parameters.Remove(OpenIdConnectParameterNames.DomainHint); } // Additional claims if (context.Properties.Items.ContainsKey(OidcConstants.AdditionalClaims)) { context.ProtocolMessage.SetParameter( OidcConstants.AdditionalClaims, context.Properties.Items[OidcConstants.AdditionalClaims]); } if (microsoftIdentityOptions.IsB2C) { context.ProtocolMessage.SetParameter("client_info", "1"); // When a new Challenge is returned using any B2C user flow different than susi, we must change // the ProtocolMessage.IssuerAddress to the desired user flow otherwise the redirect would use the susi user flow await b2cOidcHandlers.OnRedirectToIdentityProvider(context).ConfigureAwait(false); } await redirectToIdpHandler(context).ConfigureAwait(false); }; if (microsoftIdentityOptions.IsB2C) { var remoteFailureHandler = options.Events.OnRemoteFailure; options.Events.OnRemoteFailure = async context => { // Handles the error when a user cancels an action on the Azure Active Directory B2C UI. // Handle the error code that Azure Active Directory B2C throws when trying to reset a password from the login page // because password reset is not supported by a "sign-up or sign-in user flow". await b2cOidcHandlers.OnRemoteFailure(context).ConfigureAwait(false); await remoteFailureHandler(context).ConfigureAwait(false); }; } if (subscribeToOpenIdConnectMiddlewareDiagnosticsEvents) { var diags = builder.Services.BuildServiceProvider().GetRequiredService <IOpenIdConnectMiddlewareDiagnostics>(); diags.Subscribe(options.Events); } }); return(builder); }
/// <summary> /// Protects the Web API with Microsoft identity platform (formerly Azure AD v2.0) /// This method expects the configuration file will have a section, named "AzureAd" as default, with the necessary settings to initialize authentication options. /// </summary> /// <param name="builder">AuthenticationBuilder to which to add this configuration.</param> /// <param name="configureJwtBearerOptions">The action to configure <see cref="JwtBearerOptions"/>.</param> /// <param name="configureMicrosoftIdentityOptions">The action to configure the <see cref="MicrosoftIdentityOptions"/> /// configuration options.</param> /// <param name="jwtBearerScheme">The JwtBearer scheme name to be used. By default it uses "Bearer".</param> /// <param name="tokenDecryptionCertificate">Token decryption certificate.</param> /// <param name="subscribeToJwtBearerMiddlewareDiagnosticsEvents"> /// Set to true if you want to debug, or just understand the JwtBearer events. /// </param> /// <returns>The authentication builder to chain.</returns> public static AuthenticationBuilder AddProtectedWebApi( this AuthenticationBuilder builder, Action <JwtBearerOptions> configureJwtBearerOptions, Action <MicrosoftIdentityOptions> configureMicrosoftIdentityOptions, X509Certificate2 tokenDecryptionCertificate = null, string jwtBearerScheme = JwtBearerDefaults.AuthenticationScheme, bool subscribeToJwtBearerMiddlewareDiagnosticsEvents = false) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } builder.Services.Configure(jwtBearerScheme, configureJwtBearerOptions); builder.Services.Configure <MicrosoftIdentityOptions>(configureMicrosoftIdentityOptions); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton <IValidateOptions <MicrosoftIdentityOptions>, MicrosoftIdentityOptionsValidation>()); builder.Services.AddHttpContextAccessor(); builder.Services.AddSingleton <IJwtBearerMiddlewareDiagnostics, JwtBearerMiddlewareDiagnostics>(); builder.Services.AddHttpClient(); // Change the authentication configuration to accommodate the Microsoft identity platform endpoint (v2.0). builder.AddJwtBearer(jwtBearerScheme, options => { // TODO: // Suspect. Why not get the IOption<MicrosoftIdentityOptions>? var microsoftIdentityOptions = new MicrosoftIdentityOptions(); // configuration.GetSection(configSectionName).Get<MicrosoftIdentityOptions>(); configureMicrosoftIdentityOptions(microsoftIdentityOptions); if (string.IsNullOrWhiteSpace(options.Authority)) { options.Authority = AuthorityHelpers.BuildAuthority(microsoftIdentityOptions); } // This is a Microsoft identity platform Web API options.Authority = AuthorityHelpers.EnsureAuthorityIsV2(options.Authority); // The valid audience could be given as Client Id or as Uri. // If it does not start with 'api://', this variant is added to the list of valid audiences. EnsureValidAudiencesContainsApiGuidIfGuidProvided(options, microsoftIdentityOptions); // If the developer registered an IssuerValidator, do not overwrite it if (options.TokenValidationParameters.IssuerValidator == null) { // Instead of using the default validation (validating against a single tenant, as we do in line of business apps), // we inject our own multi-tenant validation logic (which even accepts both v1.0 and v2.0 tokens) options.TokenValidationParameters.IssuerValidator = AadIssuerValidator.GetIssuerValidator(options.Authority).Validate; } // If you provide a token decryption certificate, it will be used to decrypt the token if (tokenDecryptionCertificate != null) { options.TokenValidationParameters.TokenDecryptionKey = new X509SecurityKey(tokenDecryptionCertificate); } if (options.Events == null) { options.Events = new JwtBearerEvents(); } // When an access token for our own Web API is validated, we add it to MSAL.NET's cache so that it can // be used from the controllers. var tokenValidatedHandler = options.Events.OnTokenValidated; options.Events.OnTokenValidated = async context => { // This check is required to ensure that the Web API only accepts tokens from tenants where it has been consented and provisioned. if (!context.Principal.Claims.Any(x => x.Type == ClaimConstants.Scope) && !context.Principal.Claims.Any(y => y.Type == ClaimConstants.Scp) && !context.Principal.Claims.Any(y => y.Type == ClaimConstants.Roles) && !context.Principal.Claims.Any(y => y.Type == ClaimConstants.Role)) { throw new UnauthorizedAccessException("Neither scope or roles claim was found in the bearer token."); } await tokenValidatedHandler(context).ConfigureAwait(false); }; if (subscribeToJwtBearerMiddlewareDiagnosticsEvents) { var diags = builder.Services.BuildServiceProvider().GetRequiredService <IJwtBearerMiddlewareDiagnostics>(); diags.Subscribe(options.Events); } }); return(builder); }
private static void AddMicrosoftIdentityWebAppInternal( AuthenticationBuilder builder, Action <MicrosoftIdentityOptions> configureMicrosoftIdentityOptions, Action <CookieAuthenticationOptions>?configureCookieAuthenticationOptions, string openIdConnectScheme, string?cookieScheme, bool subscribeToOpenIdConnectMiddlewareDiagnosticsEvents) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (configureMicrosoftIdentityOptions == null) { throw new ArgumentNullException(nameof(configureMicrosoftIdentityOptions)); } builder.Services.Configure(configureMicrosoftIdentityOptions); builder.Services.AddHttpClient(); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton <IValidateOptions <MicrosoftIdentityOptions>, MicrosoftIdentityOptionsValidation>()); if (!string.IsNullOrEmpty(cookieScheme)) { Action <CookieAuthenticationOptions> emptyOption = option => { }; builder.AddCookie(cookieScheme, configureCookieAuthenticationOptions ?? emptyOption); } builder.Services.TryAddSingleton <MicrosoftIdentityIssuerValidatorFactory>(); builder.Services.TryAddSingleton <ILoginErrorAccessor>(ctx => { // ITempDataDictionaryFactory is not always available, so we don't require it var tempFactory = ctx.GetService <ITempDataDictionaryFactory>(); var env = ctx.GetService <IHostEnvironment>(); // ex. Azure Functions will not have an env. if (env != null) { return(TempDataLoginErrorAccessor.Create(tempFactory, env.IsDevelopment())); } else { return(TempDataLoginErrorAccessor.Create(tempFactory, false)); } }); if (subscribeToOpenIdConnectMiddlewareDiagnosticsEvents) { builder.Services.AddSingleton <IOpenIdConnectMiddlewareDiagnostics, OpenIdConnectMiddlewareDiagnostics>(); } if (AppServicesAuthenticationInformation.IsAppServicesAadAuthenticationEnabled) { builder.Services.AddAuthentication(AppServicesAuthenticationDefaults.AuthenticationScheme) .AddAppServicesAuthentication(); return; } builder.AddOpenIdConnect(openIdConnectScheme, options => { }); builder.Services.AddOptions <OpenIdConnectOptions>(openIdConnectScheme) .Configure <IServiceProvider, IOptions <MicrosoftIdentityOptions> >((options, serviceProvider, microsoftIdentityOptions) => { PopulateOpenIdOptionsFromMicrosoftIdentityOptions(options, microsoftIdentityOptions.Value); var b2cOidcHandlers = new AzureADB2COpenIDConnectEventHandlers( openIdConnectScheme, microsoftIdentityOptions.Value, serviceProvider.GetRequiredService <ILoginErrorAccessor>()); if (!string.IsNullOrEmpty(cookieScheme)) { options.SignInScheme = cookieScheme; } if (string.IsNullOrWhiteSpace(options.Authority)) { options.Authority = AuthorityHelpers.BuildAuthority(microsoftIdentityOptions.Value); } // This is a Microsoft identity platform web app options.Authority = AuthorityHelpers.EnsureAuthorityIsV2(options.Authority); // B2C doesn't have preferred_username claims if (microsoftIdentityOptions.Value.IsB2C) { options.TokenValidationParameters.NameClaimType = ClaimConstants.Name; } else { options.TokenValidationParameters.NameClaimType = ClaimConstants.PreferredUserName; } // If the developer registered an IssuerValidator, do not overwrite it if (options.TokenValidationParameters.ValidateIssuer && options.TokenValidationParameters.IssuerValidator == null) { // If you want to restrict the users that can sign-in to several organizations // Set the tenant value in the appsettings.json file to 'organizations', and add the // issuers you want to accept to options.TokenValidationParameters.ValidIssuers collection MicrosoftIdentityIssuerValidatorFactory microsoftIdentityIssuerValidatorFactory = serviceProvider.GetRequiredService <MicrosoftIdentityIssuerValidatorFactory>(); options.TokenValidationParameters.IssuerValidator = microsoftIdentityIssuerValidatorFactory.GetAadIssuerValidator(options.Authority).Validate; } // Avoids having users being presented the select account dialog when they are already signed-in // for instance when going through incremental consent var redirectToIdpHandler = options.Events.OnRedirectToIdentityProvider; options.Events.OnRedirectToIdentityProvider = async context => { var login = context.Properties.GetParameter <string>(OpenIdConnectParameterNames.LoginHint); if (!string.IsNullOrWhiteSpace(login)) { context.ProtocolMessage.LoginHint = login; // delete the login_hint from the Properties when we are done otherwise // it will take up extra space in the cookie. context.Properties.Parameters.Remove(OpenIdConnectParameterNames.LoginHint); } var domainHint = context.Properties.GetParameter <string>(OpenIdConnectParameterNames.DomainHint); if (!string.IsNullOrWhiteSpace(domainHint)) { context.ProtocolMessage.DomainHint = domainHint; // delete the domain_hint from the Properties when we are done otherwise // it will take up extra space in the cookie. context.Properties.Parameters.Remove(OpenIdConnectParameterNames.DomainHint); } context.ProtocolMessage.SetParameter(Constants.ClientInfo, Constants.One); context.ProtocolMessage.SetParameter(Constants.TelemetryHeaderKey, IdHelper.CreateTelemetryInfo()); // Additional claims if (context.Properties.Items.TryGetValue(OidcConstants.AdditionalClaims, out var additionClaims)) { context.ProtocolMessage.SetParameter( OidcConstants.AdditionalClaims, additionClaims); } if (microsoftIdentityOptions.Value.IsB2C) { // When a new Challenge is returned using any B2C user flow different than susi, we must change // the ProtocolMessage.IssuerAddress to the desired user flow otherwise the redirect would use the susi user flow await b2cOidcHandlers.OnRedirectToIdentityProvider(context).ConfigureAwait(false); } await redirectToIdpHandler(context).ConfigureAwait(false); }; if (microsoftIdentityOptions.Value.IsB2C) { var remoteFailureHandler = options.Events.OnRemoteFailure; options.Events.OnRemoteFailure = async context => { // Handles the error when a user cancels an action on the Azure Active Directory B2C UI. // Handle the error code that Azure Active Directory B2C throws when trying to reset a password from the login page // because password reset is not supported by a "sign-up or sign-in user flow". await b2cOidcHandlers.OnRemoteFailure(context).ConfigureAwait(false); await remoteFailureHandler(context).ConfigureAwait(false); }; } if (subscribeToOpenIdConnectMiddlewareDiagnosticsEvents) { var diagnostics = serviceProvider.GetRequiredService <IOpenIdConnectMiddlewareDiagnostics>(); diagnostics.Subscribe(options.Events); } }); }
private static void AddMicrosoftIdentityWebApiImplementation( AuthenticationBuilder builder, Action <JwtBearerOptions> configureJwtBearerOptions, Action <MicrosoftIdentityOptions> configureMicrosoftIdentityOptions, string jwtBearerScheme, bool subscribeToJwtBearerMiddlewareDiagnosticsEvents) { builder.AddJwtBearer(jwtBearerScheme, configureJwtBearerOptions); builder.Services.Configure(jwtBearerScheme, configureMicrosoftIdentityOptions); builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpClient(); builder.Services.TryAddSingleton <MicrosoftIdentityIssuerValidatorFactory>(); builder.Services.AddRequiredScopeAuthorization(); builder.Services.AddRequiredScopeOrAppPermissionAuthorization(); builder.Services.AddOptions <AadIssuerValidatorOptions>(); if (subscribeToJwtBearerMiddlewareDiagnosticsEvents) { builder.Services.AddTransient <IJwtBearerMiddlewareDiagnostics, JwtBearerMiddlewareDiagnostics>(); } // Change the authentication configuration to accommodate the Microsoft identity platform endpoint (v2.0). builder.Services.AddOptions <JwtBearerOptions>(jwtBearerScheme) .Configure <IServiceProvider, IOptionsMonitor <MergedOptions>, IOptionsMonitor <MicrosoftIdentityOptions>, IOptions <MicrosoftIdentityOptions> >(( options, serviceProvider, mergedOptionsMonitor, msIdOptionsMonitor, msIdOptions) => { MicrosoftIdentityBaseAuthenticationBuilder.SetIdentityModelLogger(serviceProvider); MergedOptions mergedOptions = mergedOptionsMonitor.Get(jwtBearerScheme); MergedOptions.UpdateMergedOptionsFromJwtBearerOptions(options, mergedOptions); MergedOptions.UpdateMergedOptionsFromMicrosoftIdentityOptions(msIdOptions.Value, mergedOptions); MergedOptions.UpdateMergedOptionsFromMicrosoftIdentityOptions(msIdOptionsMonitor.Get(jwtBearerScheme), mergedOptions); MergedOptionsValidation.Validate(mergedOptions); if (string.IsNullOrWhiteSpace(options.Authority)) { options.Authority = AuthorityHelpers.BuildAuthority(mergedOptions); } // This is a Microsoft identity platform web API options.Authority = AuthorityHelpers.EnsureAuthorityIsV2(options.Authority); if (options.TokenValidationParameters.AudienceValidator == null && options.TokenValidationParameters.ValidAudience == null && options.TokenValidationParameters.ValidAudiences == null) { RegisterValidAudience registerAudience = new RegisterValidAudience(); registerAudience.RegisterAudienceValidation( options.TokenValidationParameters, mergedOptions); } // If the developer registered an IssuerValidator, do not overwrite it if (options.TokenValidationParameters.ValidateIssuer && options.TokenValidationParameters.IssuerValidator == null) { // Instead of using the default validation (validating against a single tenant, as we do in line of business apps), // we inject our own multi-tenant validation logic (which even accepts both v1.0 and v2.0 tokens) MicrosoftIdentityIssuerValidatorFactory microsoftIdentityIssuerValidatorFactory = serviceProvider.GetRequiredService <MicrosoftIdentityIssuerValidatorFactory>(); options.TokenValidationParameters.IssuerValidator = microsoftIdentityIssuerValidatorFactory.GetAadIssuerValidator(options.Authority).Validate; } // If you provide a token decryption certificate, it will be used to decrypt the token if (mergedOptions.TokenDecryptionCertificates != null) { DefaultCertificateLoader.UserAssignedManagedIdentityClientId = mergedOptions.UserAssignedManagedIdentityClientId; IEnumerable <X509Certificate2?> certificates = DefaultCertificateLoader.LoadAllCertificates(mergedOptions.TokenDecryptionCertificates); IEnumerable <X509SecurityKey> keys = certificates.Select(c => new X509SecurityKey(c)); options.TokenValidationParameters.TokenDecryptionKeys = keys; } if (options.Events == null) { options.Events = new JwtBearerEvents(); } // When an access token for our own web API is validated, we add it to MSAL.NET's cache so that it can // be used from the controllers. if (!mergedOptions.AllowWebApiToBeAuthorizedByACL) { ChainOnTokenValidatedEventForClaimsValidation(options.Events, jwtBearerScheme); } if (subscribeToJwtBearerMiddlewareDiagnosticsEvents) { var diagnostics = serviceProvider.GetRequiredService <IJwtBearerMiddlewareDiagnostics>(); diagnostics.Subscribe(options.Events); } }); }