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>(); 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); 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.ContainsKey(OidcConstants.AdditionalClaims)) { context.ProtocolMessage.SetParameter( OidcConstants.AdditionalClaims, context.Properties.Items[OidcConstants.AdditionalClaims]); } 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); } }); }