public static void Validate(MergedOptions options) { if (string.IsNullOrEmpty(options.ClientId)) { throw new ArgumentNullException(options.ClientId, string.Format(CultureInfo.InvariantCulture, IDWebErrorMessage.ConfigurationOptionRequired, nameof(options.ClientId))); } if (string.IsNullOrEmpty(options.Instance)) { throw new ArgumentNullException(options.Instance, string.Format(CultureInfo.InvariantCulture, IDWebErrorMessage.ConfigurationOptionRequired, nameof(options.Instance))); } if (options.IsB2C) { if (string.IsNullOrEmpty(options.Domain)) { throw new ArgumentNullException(options.Domain, string.Format(CultureInfo.InvariantCulture, IDWebErrorMessage.ConfigurationOptionRequired, nameof(options.Domain))); } } else { if (string.IsNullOrEmpty(options.TenantId)) { throw new ArgumentNullException(options.TenantId, string.Format(CultureInfo.InvariantCulture, IDWebErrorMessage.ConfigurationOptionRequired, nameof(options.TenantId))); } } }
internal static void WebAppCallsWebApiImplementation( IServiceCollection services, IEnumerable <string>?initialScopes, Action <MicrosoftIdentityOptions> configureMicrosoftIdentityOptions, string openIdConnectScheme, Action <ConfidentialClientApplicationOptions>?configureConfidentialClientApplicationOptions) { // Ensure that configuration options for MSAL.NET, HttpContext accessor and the Token acquisition service // (encapsulating MSAL.NET) are available through dependency injection services.Configure(openIdConnectScheme, configureMicrosoftIdentityOptions); if (configureConfidentialClientApplicationOptions != null) { services.Configure(openIdConnectScheme, configureConfidentialClientApplicationOptions); } services.AddHttpContextAccessor(); if (AppServicesAuthenticationInformation.IsAppServicesAadAuthenticationEnabled) { services.AddScoped <ITokenAcquisition, AppServicesAuthenticationTokenAcquisition>(); } else { services.AddTokenAcquisition(); services.AddOptions <OpenIdConnectOptions>(openIdConnectScheme) .Configure <IServiceProvider, IOptionsMonitor <MergedOptions>, IOptionsMonitor <ConfidentialClientApplicationOptions>, IOptions <ConfidentialClientApplicationOptions> >(( options, serviceProvider, mergedOptionsMonitor, ccaOptionsMonitor, ccaOptions) => { MergedOptions mergedOptions = mergedOptionsMonitor.Get(openIdConnectScheme); MergedOptions.UpdateMergedOptionsFromConfidentialClientApplicationOptions(ccaOptions.Value, mergedOptions); // legacy scenario w/out auth scheme MergedOptions.UpdateMergedOptionsFromConfidentialClientApplicationOptions(ccaOptionsMonitor.Get(openIdConnectScheme), mergedOptions); // w/auth scheme options.ResponseType = OpenIdConnectResponseType.Code; // This scope is needed to get a refresh token when users sign-in with their Microsoft personal accounts // It's required by MSAL.NET and automatically provided when users sign-in with work or school accounts options.Scope.Add(OidcConstants.ScopeOfflineAccess); if (initialScopes != null) { foreach (string scope in initialScopes) { if (!options.Scope.Contains(scope)) { options.Scope.Add(scope); } } } // Handling the auth redemption by MSAL.NET so that a token is available in the token cache // where it will be usable from Controllers later (through the TokenAcquisition service) var codeReceivedHandler = options.Events.OnAuthorizationCodeReceived; options.Events.OnAuthorizationCodeReceived = async context => { var tokenAcquisition = context !.HttpContext.RequestServices.GetRequiredService <ITokenAcquisitionInternal>(); await tokenAcquisition.AddAccountToCacheFromAuthorizationCodeAsync(context, options.Scope, openIdConnectScheme).ConfigureAwait(false); await codeReceivedHandler(context).ConfigureAwait(false); }; // Handling the token validated to get the client_info for cases where tenantId is not present (example: B2C) var onTokenValidatedHandler = options.Events.OnTokenValidated; options.Events.OnTokenValidated = async context => { string?clientInfo = context !.ProtocolMessage?.GetParameter(ClaimConstants.ClientInfo); if (!string.IsNullOrEmpty(clientInfo)) { ClientInfo?clientInfoFromServer = ClientInfo.CreateFromJson(clientInfo); if (clientInfoFromServer != null && clientInfoFromServer.UniqueTenantIdentifier != null && clientInfoFromServer.UniqueObjectIdentifier != null) { context !.Principal !.Identities.FirstOrDefault()?.AddClaim(new Claim(ClaimConstants.UniqueTenantIdentifier, clientInfoFromServer.UniqueTenantIdentifier)); context !.Principal !.Identities.FirstOrDefault()?.AddClaim(new Claim(ClaimConstants.UniqueObjectIdentifier, clientInfoFromServer.UniqueObjectIdentifier)); } } await onTokenValidatedHandler(context).ConfigureAwait(false); }; // Handling the sign-out: removing the account from MSAL.NET cache var signOutHandler = options.Events.OnRedirectToIdentityProviderForSignOut; options.Events.OnRedirectToIdentityProviderForSignOut = async context => { // Remove the account from MSAL.NET token cache var tokenAcquisition = context !.HttpContext.RequestServices.GetRequiredService <ITokenAcquisitionInternal>(); await tokenAcquisition.RemoveAccountAsync(context, openIdConnectScheme).ConfigureAwait(false); await signOutHandler(context).ConfigureAwait(false); }; }); } }
internal static void UpdateMergedOptionsFromMicrosoftIdentityOptions(MicrosoftIdentityOptions microsoftIdentityOptions, MergedOptions mergedOptions) { mergedOptions.ResetPasswordPath = microsoftIdentityOptions.ResetPasswordPath; mergedOptions.ErrorPath = microsoftIdentityOptions.ErrorPath; mergedOptions.AccessDeniedPath = microsoftIdentityOptions.AccessDeniedPath; mergedOptions.AllowWebApiToBeAuthorizedByACL = microsoftIdentityOptions.AllowWebApiToBeAuthorizedByACL; mergedOptions.AuthenticationMethod = microsoftIdentityOptions.AuthenticationMethod; if (string.IsNullOrEmpty(mergedOptions.Authority) && !string.IsNullOrEmpty(microsoftIdentityOptions.Authority)) { mergedOptions.Authority = microsoftIdentityOptions.Authority; } #if DOTNET_50_AND_ABOVE mergedOptions.AutomaticRefreshInterval = microsoftIdentityOptions.AutomaticRefreshInterval; #endif mergedOptions.Backchannel ??= microsoftIdentityOptions.Backchannel; mergedOptions.BackchannelHttpHandler ??= microsoftIdentityOptions.BackchannelHttpHandler; mergedOptions.BackchannelTimeout = microsoftIdentityOptions.BackchannelTimeout; mergedOptions.CallbackPath = microsoftIdentityOptions.CallbackPath; if (string.IsNullOrEmpty(mergedOptions.ClaimsIssuer) && !string.IsNullOrEmpty(microsoftIdentityOptions.ClaimsIssuer)) { mergedOptions.ClaimsIssuer = microsoftIdentityOptions.ClaimsIssuer; } mergedOptions.ClientCertificates ??= microsoftIdentityOptions.ClientCertificates; if (string.IsNullOrEmpty(mergedOptions.ClientId) && !string.IsNullOrEmpty(microsoftIdentityOptions.ClientId)) { mergedOptions.ClientId = microsoftIdentityOptions.ClientId; } if (string.IsNullOrEmpty(mergedOptions.ClientSecret) && !string.IsNullOrEmpty(microsoftIdentityOptions.ClientSecret)) { mergedOptions.ClientSecret = microsoftIdentityOptions.ClientSecret; } mergedOptions.Configuration ??= microsoftIdentityOptions.Configuration; mergedOptions.ConfigurationManager ??= microsoftIdentityOptions.ConfigurationManager; mergedOptions.CorrelationCookie = microsoftIdentityOptions.CorrelationCookie; mergedOptions.DataProtectionProvider ??= microsoftIdentityOptions.DataProtectionProvider; mergedOptions.DisableTelemetry = microsoftIdentityOptions.DisableTelemetry; if (string.IsNullOrEmpty(mergedOptions.Domain) && !string.IsNullOrEmpty(microsoftIdentityOptions.Domain)) { mergedOptions.Domain = microsoftIdentityOptions.Domain; } if (string.IsNullOrEmpty(mergedOptions.EditProfilePolicyId) && !string.IsNullOrEmpty(microsoftIdentityOptions.EditProfilePolicyId)) { mergedOptions.EditProfilePolicyId = microsoftIdentityOptions.EditProfilePolicyId; } mergedOptions.Events.OnAccessDenied += microsoftIdentityOptions.Events.OnAccessDenied; mergedOptions.Events.OnAuthenticationFailed += microsoftIdentityOptions.Events.OnAuthenticationFailed; mergedOptions.Events.OnAuthorizationCodeReceived += microsoftIdentityOptions.Events.OnAuthorizationCodeReceived; mergedOptions.Events.OnMessageReceived += microsoftIdentityOptions.Events.OnMessageReceived; mergedOptions.Events.OnRedirectToIdentityProvider += microsoftIdentityOptions.Events.OnRedirectToIdentityProvider; mergedOptions.Events.OnRedirectToIdentityProviderForSignOut += microsoftIdentityOptions.Events.OnRedirectToIdentityProviderForSignOut; mergedOptions.Events.OnRemoteFailure += microsoftIdentityOptions.Events.OnRemoteFailure; mergedOptions.Events.OnRemoteSignOut += microsoftIdentityOptions.Events.OnRemoteSignOut; mergedOptions.Events.OnSignedOutCallbackRedirect += microsoftIdentityOptions.Events.OnSignedOutCallbackRedirect; mergedOptions.Events.OnTicketReceived += microsoftIdentityOptions.Events.OnTicketReceived; mergedOptions.Events.OnTokenResponseReceived += microsoftIdentityOptions.Events.OnTokenResponseReceived; mergedOptions.Events.OnTokenValidated += microsoftIdentityOptions.Events.OnTokenValidated; mergedOptions.Events.OnUserInformationReceived += microsoftIdentityOptions.Events.OnUserInformationReceived; mergedOptions.EventsType ??= microsoftIdentityOptions.EventsType; if (string.IsNullOrEmpty(mergedOptions.ForwardAuthenticate) && !string.IsNullOrEmpty(microsoftIdentityOptions.ForwardAuthenticate)) { mergedOptions.ForwardAuthenticate = microsoftIdentityOptions.ForwardAuthenticate; } if (string.IsNullOrEmpty(mergedOptions.ForwardChallenge) && !string.IsNullOrEmpty(microsoftIdentityOptions.ForwardChallenge)) { mergedOptions.ForwardChallenge = microsoftIdentityOptions.ForwardChallenge; } if (string.IsNullOrEmpty(mergedOptions.ForwardDefault) && !string.IsNullOrEmpty(microsoftIdentityOptions.ForwardDefault)) { mergedOptions.ForwardDefault = microsoftIdentityOptions.ForwardDefault; } mergedOptions.ForwardDefaultSelector ??= microsoftIdentityOptions.ForwardDefaultSelector; if (string.IsNullOrEmpty(mergedOptions.ForwardForbid) && !string.IsNullOrEmpty(microsoftIdentityOptions.ForwardForbid)) { mergedOptions.ForwardForbid = microsoftIdentityOptions.ForwardForbid; } if (string.IsNullOrEmpty(mergedOptions.ForwardSignIn) && !string.IsNullOrEmpty(microsoftIdentityOptions.ForwardSignIn)) { mergedOptions.ForwardSignIn = microsoftIdentityOptions.ForwardSignIn; } if (string.IsNullOrEmpty(mergedOptions.ForwardSignOut) && !string.IsNullOrEmpty(microsoftIdentityOptions.ForwardSignOut)) { mergedOptions.ForwardSignOut = microsoftIdentityOptions.ForwardSignOut; } mergedOptions.GetClaimsFromUserInfoEndpoint = microsoftIdentityOptions.GetClaimsFromUserInfoEndpoint; if (string.IsNullOrEmpty(mergedOptions.Instance) && !string.IsNullOrEmpty(microsoftIdentityOptions.Instance)) { mergedOptions.Instance = microsoftIdentityOptions.Instance; } mergedOptions.LegacyCacheCompatibilityEnabled = microsoftIdentityOptions.LegacyCacheCompatibilityEnabled; #if DOTNET_50_AND_ABOVE mergedOptions.MapInboundClaims = microsoftIdentityOptions.MapInboundClaims; #endif mergedOptions.MaxAge = microsoftIdentityOptions.MaxAge; if (string.IsNullOrEmpty(mergedOptions.MetadataAddress) && !string.IsNullOrEmpty(microsoftIdentityOptions.MetadataAddress)) { mergedOptions.MetadataAddress = microsoftIdentityOptions.MetadataAddress; } mergedOptions.NonceCookie = microsoftIdentityOptions.NonceCookie; if (string.IsNullOrEmpty(mergedOptions.Prompt) && !string.IsNullOrEmpty(microsoftIdentityOptions.Prompt)) { mergedOptions.Prompt = microsoftIdentityOptions.Prompt; } mergedOptions.ProtocolValidator ??= microsoftIdentityOptions.ProtocolValidator; #if DOTNET_50_AND_ABOVE mergedOptions.RefreshInterval = microsoftIdentityOptions.RefreshInterval; #endif mergedOptions.RefreshOnIssuerKeyNotFound = microsoftIdentityOptions.RefreshOnIssuerKeyNotFound; mergedOptions.RemoteAuthenticationTimeout = microsoftIdentityOptions.RemoteAuthenticationTimeout; mergedOptions.RemoteSignOutPath = microsoftIdentityOptions.RemoteSignOutPath; mergedOptions.RequireHttpsMetadata = microsoftIdentityOptions.RequireHttpsMetadata; if (string.IsNullOrEmpty(mergedOptions.ResetPasswordPolicyId) && !string.IsNullOrEmpty(microsoftIdentityOptions.ResetPasswordPolicyId)) { mergedOptions.ResetPasswordPolicyId = microsoftIdentityOptions.ResetPasswordPolicyId; } if (string.IsNullOrEmpty(mergedOptions.Resource) && !string.IsNullOrEmpty(microsoftIdentityOptions.Resource)) { mergedOptions.Resource = microsoftIdentityOptions.Resource; } if (string.IsNullOrEmpty(mergedOptions.ResponseMode) && !string.IsNullOrEmpty(microsoftIdentityOptions.ResponseMode)) { mergedOptions.ResponseMode = microsoftIdentityOptions.ResponseMode; } mergedOptions.ResponseType = microsoftIdentityOptions.ResponseType; if (string.IsNullOrEmpty(mergedOptions.ReturnUrlParameter) && !string.IsNullOrEmpty(microsoftIdentityOptions.ReturnUrlParameter)) { mergedOptions.ReturnUrlParameter = microsoftIdentityOptions.ReturnUrlParameter; } mergedOptions.SaveTokens = microsoftIdentityOptions.SaveTokens; mergedOptions.SecurityTokenValidator ??= microsoftIdentityOptions.SecurityTokenValidator; mergedOptions.SendX5C = microsoftIdentityOptions.SendX5C; mergedOptions.WithSpaAuthCode = microsoftIdentityOptions.WithSpaAuthCode; mergedOptions.SignedOutCallbackPath = microsoftIdentityOptions.SignedOutCallbackPath; if (string.IsNullOrEmpty(mergedOptions.SignedOutRedirectUri) && !string.IsNullOrEmpty(microsoftIdentityOptions.SignedOutRedirectUri)) { mergedOptions.SignedOutRedirectUri = microsoftIdentityOptions.SignedOutRedirectUri; } if (string.IsNullOrEmpty(mergedOptions.SignInScheme) && !string.IsNullOrEmpty(microsoftIdentityOptions.SignInScheme)) { mergedOptions.SignInScheme = microsoftIdentityOptions.SignInScheme; } if (string.IsNullOrEmpty(mergedOptions.SignOutScheme) && !string.IsNullOrEmpty(microsoftIdentityOptions.SignOutScheme)) { mergedOptions.SignOutScheme = microsoftIdentityOptions.SignOutScheme; } if (string.IsNullOrEmpty(mergedOptions.SignUpSignInPolicyId) && !string.IsNullOrEmpty(microsoftIdentityOptions.SignUpSignInPolicyId)) { mergedOptions.SignUpSignInPolicyId = microsoftIdentityOptions.SignUpSignInPolicyId; } mergedOptions.SkipUnrecognizedRequests = microsoftIdentityOptions.SkipUnrecognizedRequests; mergedOptions.StateDataFormat ??= microsoftIdentityOptions.StateDataFormat; mergedOptions.StringDataFormat ??= microsoftIdentityOptions.StringDataFormat; if (string.IsNullOrEmpty(mergedOptions.TenantId) && !string.IsNullOrEmpty(microsoftIdentityOptions.TenantId)) { mergedOptions.TenantId = microsoftIdentityOptions.TenantId; } mergedOptions.TokenDecryptionCertificates ??= microsoftIdentityOptions.TokenDecryptionCertificates; mergedOptions.TokenValidationParameters = microsoftIdentityOptions.TokenValidationParameters.Clone(); mergedOptions.UsePkce = microsoftIdentityOptions.UsePkce; if (string.IsNullOrEmpty(mergedOptions.UserAssignedManagedIdentityClientId) && !string.IsNullOrEmpty(microsoftIdentityOptions.UserAssignedManagedIdentityClientId)) { mergedOptions.UserAssignedManagedIdentityClientId = microsoftIdentityOptions.UserAssignedManagedIdentityClientId; } mergedOptions.ClientCredentialsUsingManagedIdentity ??= microsoftIdentityOptions.ClientCredentialsUsingManagedIdentity; mergedOptions.UseTokenLifetime = microsoftIdentityOptions.UseTokenLifetime; mergedOptions._confidentialClientApplicationOptions = null; mergedOptions.Scope.Clear(); foreach (var scope in microsoftIdentityOptions.Scope) { if (!string.IsNullOrWhiteSpace(scope) && !mergedOptions.Scope.Any(s => string.Equals(s, scope, StringComparison.OrdinalIgnoreCase))) { mergedOptions.Scope.Add(scope); } } }
internal static void UpdateMergedOptionsFromJwtBearerOptions(JwtBearerOptions jwtBearerOptions, MergedOptions mergedOptions) { if (string.IsNullOrEmpty(mergedOptions.Authority) && !string.IsNullOrEmpty(jwtBearerOptions.Authority)) { mergedOptions.Authority = jwtBearerOptions.Authority; } }
internal static void UpdateConfidentialClientApplicationOptionsFromMergedOptions(MergedOptions mergedOptions, ConfidentialClientApplicationOptions confidentialClientApplicationOptions) { confidentialClientApplicationOptions.AadAuthorityAudience = mergedOptions.AadAuthorityAudience; confidentialClientApplicationOptions.AzureCloudInstance = mergedOptions.AzureCloudInstance; if (string.IsNullOrEmpty(confidentialClientApplicationOptions.AzureRegion) && !string.IsNullOrEmpty(mergedOptions.AzureRegion)) { confidentialClientApplicationOptions.AzureRegion = mergedOptions.AzureRegion; } confidentialClientApplicationOptions.ClientCapabilities ??= mergedOptions.ClientCapabilities; if (string.IsNullOrEmpty(confidentialClientApplicationOptions.ClientId) && !string.IsNullOrEmpty(mergedOptions.ClientId)) { confidentialClientApplicationOptions.ClientId = mergedOptions.ClientId; } if (string.IsNullOrEmpty(confidentialClientApplicationOptions.ClientName) && !string.IsNullOrEmpty(mergedOptions.ClientName)) { confidentialClientApplicationOptions.ClientName = mergedOptions.ClientName; } if (string.IsNullOrEmpty(confidentialClientApplicationOptions.ClientSecret) && !string.IsNullOrEmpty(mergedOptions.ClientSecret)) { confidentialClientApplicationOptions.ClientSecret = mergedOptions.ClientSecret; } if (string.IsNullOrEmpty(confidentialClientApplicationOptions.ClientVersion) && !string.IsNullOrEmpty(mergedOptions.ClientVersion)) { confidentialClientApplicationOptions.ClientVersion = mergedOptions.ClientVersion; } confidentialClientApplicationOptions.EnablePiiLogging = mergedOptions.EnablePiiLogging; if (string.IsNullOrEmpty(confidentialClientApplicationOptions.Instance) && !string.IsNullOrEmpty(mergedOptions.Instance)) { confidentialClientApplicationOptions.Instance = mergedOptions.Instance; } confidentialClientApplicationOptions.IsDefaultPlatformLoggingEnabled = mergedOptions.IsDefaultPlatformLoggingEnabled; confidentialClientApplicationOptions.LegacyCacheCompatibilityEnabled = mergedOptions.LegacyCacheCompatibilityEnabled; confidentialClientApplicationOptions.EnableCacheSynchronization = mergedOptions.EnableCacheSynchronization; confidentialClientApplicationOptions.LogLevel = mergedOptions.LogLevel; if (string.IsNullOrEmpty(confidentialClientApplicationOptions.RedirectUri) && !string.IsNullOrEmpty(mergedOptions.RedirectUri)) { confidentialClientApplicationOptions.RedirectUri = mergedOptions.RedirectUri; } if (string.IsNullOrEmpty(confidentialClientApplicationOptions.TenantId) && !string.IsNullOrEmpty(mergedOptions.TenantId)) { confidentialClientApplicationOptions.TenantId = mergedOptions.TenantId; } }
internal static void UpdateMergedOptionsFromConfidentialClientApplicationOptions(ConfidentialClientApplicationOptions confidentialClientApplicationOptions, MergedOptions mergedOptions) { mergedOptions.MergedWithCca = true; mergedOptions.AadAuthorityAudience = confidentialClientApplicationOptions.AadAuthorityAudience; mergedOptions.AzureCloudInstance = confidentialClientApplicationOptions.AzureCloudInstance; if (string.IsNullOrEmpty(mergedOptions.AzureRegion) && !string.IsNullOrEmpty(confidentialClientApplicationOptions.AzureRegion)) { mergedOptions.AzureRegion = confidentialClientApplicationOptions.AzureRegion; } mergedOptions.ClientCapabilities ??= confidentialClientApplicationOptions.ClientCapabilities; if (string.IsNullOrEmpty(mergedOptions.ClientId) && !string.IsNullOrEmpty(confidentialClientApplicationOptions.ClientId)) { mergedOptions.ClientId = confidentialClientApplicationOptions.ClientId; } if (string.IsNullOrEmpty(mergedOptions.ClientName) && !string.IsNullOrEmpty(confidentialClientApplicationOptions.ClientName)) { mergedOptions.ClientName = confidentialClientApplicationOptions.ClientName; } if (string.IsNullOrEmpty(mergedOptions.ClientSecret) && !string.IsNullOrEmpty(confidentialClientApplicationOptions.ClientSecret)) { mergedOptions.ClientSecret = confidentialClientApplicationOptions.ClientSecret; } if (string.IsNullOrEmpty(mergedOptions.ClientVersion) && !string.IsNullOrEmpty(confidentialClientApplicationOptions.ClientVersion)) { mergedOptions.ClientVersion = confidentialClientApplicationOptions.ClientVersion; } mergedOptions.EnablePiiLogging = confidentialClientApplicationOptions.EnablePiiLogging; if (string.IsNullOrEmpty(mergedOptions.Instance) && !string.IsNullOrEmpty(confidentialClientApplicationOptions.Instance)) { mergedOptions.Instance = confidentialClientApplicationOptions.Instance; } mergedOptions.IsDefaultPlatformLoggingEnabled = confidentialClientApplicationOptions.IsDefaultPlatformLoggingEnabled; // mergedOptions.LegacyCacheCompatibilityEnabled = confidentialClientApplicationOptions.LegacyCacheCompatibilityEnabled; // must be set through id web options mergedOptions.EnableCacheSynchronization = confidentialClientApplicationOptions.EnableCacheSynchronization; mergedOptions.LogLevel = confidentialClientApplicationOptions.LogLevel; if (string.IsNullOrEmpty(mergedOptions.RedirectUri) && !string.IsNullOrEmpty(confidentialClientApplicationOptions.RedirectUri)) { mergedOptions.RedirectUri = confidentialClientApplicationOptions.RedirectUri; } if (string.IsNullOrEmpty(mergedOptions.TenantId) && !string.IsNullOrEmpty(confidentialClientApplicationOptions.TenantId)) { mergedOptions.TenantId = confidentialClientApplicationOptions.TenantId; } mergedOptions._confidentialClientApplicationOptions = null; }
private static void PopulateOpenIdOptionsFromMergedOptions( OpenIdConnectOptions options, MergedOptions mergedOptions) { options.Authority = mergedOptions.Authority; options.ClientId = mergedOptions.ClientId; options.ClientSecret = mergedOptions.ClientSecret; options.Configuration = mergedOptions.Configuration; options.ConfigurationManager = mergedOptions.ConfigurationManager; options.GetClaimsFromUserInfoEndpoint = mergedOptions.GetClaimsFromUserInfoEndpoint; foreach (ClaimAction c in mergedOptions.ClaimActions) { options.ClaimActions.Add(c); } options.RequireHttpsMetadata = mergedOptions.RequireHttpsMetadata; options.MetadataAddress = mergedOptions.MetadataAddress; options.MaxAge = mergedOptions.MaxAge; options.ProtocolValidator = mergedOptions.ProtocolValidator; options.SignedOutCallbackPath = mergedOptions.SignedOutCallbackPath; options.SignedOutRedirectUri = mergedOptions.SignedOutRedirectUri; options.RefreshOnIssuerKeyNotFound = mergedOptions.RefreshOnIssuerKeyNotFound; options.AuthenticationMethod = mergedOptions.AuthenticationMethod; options.Resource = mergedOptions.Resource; options.ResponseMode = mergedOptions.ResponseMode; options.ResponseType = mergedOptions.ResponseType; options.Prompt = mergedOptions.Prompt; foreach (string scope in mergedOptions.Scope) { options.Scope.Add(scope); } options.RemoteSignOutPath = mergedOptions.RemoteSignOutPath; options.SignOutScheme = mergedOptions.SignOutScheme; options.StateDataFormat = mergedOptions.StateDataFormat; options.StringDataFormat = mergedOptions.StringDataFormat; options.SecurityTokenValidator = mergedOptions.SecurityTokenValidator; options.TokenValidationParameters = mergedOptions.TokenValidationParameters; options.UseTokenLifetime = mergedOptions.UseTokenLifetime; options.SkipUnrecognizedRequests = mergedOptions.SkipUnrecognizedRequests; options.DisableTelemetry = mergedOptions.DisableTelemetry; options.NonceCookie = mergedOptions.NonceCookie; options.UsePkce = mergedOptions.UsePkce; #if DOTNET_50_AND_ABOVE options.AutomaticRefreshInterval = mergedOptions.AutomaticRefreshInterval; options.RefreshInterval = mergedOptions.RefreshInterval; options.MapInboundClaims = mergedOptions.MapInboundClaims; #endif options.BackchannelTimeout = mergedOptions.BackchannelTimeout; options.BackchannelHttpHandler = mergedOptions.BackchannelHttpHandler; options.Backchannel = mergedOptions.Backchannel; options.DataProtectionProvider = mergedOptions.DataProtectionProvider; options.CallbackPath = mergedOptions.CallbackPath; options.AccessDeniedPath = mergedOptions.AccessDeniedPath; options.ReturnUrlParameter = mergedOptions.ReturnUrlParameter; options.SignInScheme = mergedOptions.SignInScheme; options.RemoteAuthenticationTimeout = mergedOptions.RemoteAuthenticationTimeout; options.SaveTokens = mergedOptions.SaveTokens; options.CorrelationCookie = mergedOptions.CorrelationCookie; options.ClaimsIssuer = mergedOptions.ClaimsIssuer; options.Events = mergedOptions.Events; options.EventsType = mergedOptions.EventsType; options.ForwardDefault = mergedOptions.ForwardDefault; options.ForwardAuthenticate = mergedOptions.ForwardAuthenticate; options.ForwardChallenge = mergedOptions.ForwardChallenge; options.ForwardForbid = mergedOptions.ForwardForbid; options.ForwardSignIn = mergedOptions.ForwardSignIn; options.ForwardSignOut = mergedOptions.ForwardSignOut; options.ForwardDefaultSelector = mergedOptions.ForwardDefaultSelector; }
private static void AddMicrosoftIdentityWebAppInternal( AuthenticationBuilder builder, Action <MicrosoftIdentityOptions> configureMicrosoftIdentityOptions, Action <CookieAuthenticationOptions>?configureCookieAuthenticationOptions, string openIdConnectScheme, string?cookieScheme, bool subscribeToOpenIdConnectMiddlewareDiagnosticsEvents, string?displayName) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (configureMicrosoftIdentityOptions == null) { throw new ArgumentNullException(nameof(configureMicrosoftIdentityOptions)); } builder.Services.Configure(openIdConnectScheme, configureMicrosoftIdentityOptions); builder.Services.AddHttpClient(); 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; } if (!string.IsNullOrEmpty(displayName)) { builder.AddOpenIdConnect(openIdConnectScheme, displayName: displayName, options => { }); } else { builder.AddOpenIdConnect(openIdConnectScheme, options => { }); } builder.Services.AddOptions <OpenIdConnectOptions>(openIdConnectScheme) .Configure <IServiceProvider, IOptionsMonitor <MergedOptions>, IOptionsMonitor <MicrosoftIdentityOptions>, IOptions <MicrosoftIdentityOptions> >(( options, serviceProvider, mergedOptionsMonitor, msIdOptionsMonitor, msIdOptions) => { MicrosoftIdentityBaseAuthenticationBuilder.SetIdentityModelLogger(serviceProvider); MergedOptions mergedOptions = mergedOptionsMonitor.Get(openIdConnectScheme); MergedOptions.UpdateMergedOptionsFromMicrosoftIdentityOptions(msIdOptions.Value, mergedOptions); MergedOptions.UpdateMergedOptionsFromMicrosoftIdentityOptions(msIdOptionsMonitor.Get(openIdConnectScheme), mergedOptions); MergedOptionsValidation.Validate(mergedOptions); PopulateOpenIdOptionsFromMergedOptions(options, mergedOptions); var b2cOidcHandlers = new AzureADB2COpenIDConnectEventHandlers( openIdConnectScheme, mergedOptions, serviceProvider.GetRequiredService <ILoginErrorAccessor>()); if (!string.IsNullOrEmpty(cookieScheme)) { options.SignInScheme = cookieScheme; } if (string.IsNullOrWhiteSpace(options.Authority)) { options.Authority = AuthorityHelpers.BuildAuthority(mergedOptions); } // This is a Microsoft identity platform web app options.Authority = AuthorityHelpers.EnsureAuthorityIsV2(options.Authority); // B2C doesn't have preferred_username claims if (mergedOptions.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 loginHint = context.Properties.GetParameter <string>(OpenIdConnectParameterNames.LoginHint); if (!string.IsNullOrWhiteSpace(loginHint)) { context.ProtocolMessage.LoginHint = loginHint; context.ProtocolMessage.SetParameter(Constants.XAnchorMailbox, $"{Constants.Upn}:{loginHint}"); // 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 (mergedOptions.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 (mergedOptions.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); } }); }