Exemple #1
0
        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)));
                }
            }
        }
Exemple #2
0
        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;
        }
Exemple #7
0
        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;
        }
Exemple #8
0
        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);
                }
            });
        }