예제 #1
0
        /// <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" with the necessary settings to initialize authentication options.
        /// </summary>
        /// <param name="services">Service collection to which to add this authentication scheme</param>
        /// <param name="configuration">The Configuration object</param>
        /// <param name="subscribeToJwtBearerMiddlewareDiagnosticsEvents">
        /// Set to true if you want to debug, or just understand the JwtBearer events.
        /// </param>
        /// <returns></returns>
        public static IServiceCollection AddProtectedWebApi(
            this IServiceCollection services,
            IConfiguration configuration,
            X509Certificate2 tokenDecryptionCertificate = null,
            string configSectionName = "AzureAd",
            bool subscribeToJwtBearerMiddlewareDiagnosticsEvents = false)
        {
            services.AddAuthentication(AzureADDefaults.JwtBearerAuthenticationScheme)
            .AddAzureADBearer(options => configuration.Bind(configSectionName, options));
            services.Configure <AzureADOptions>(options => configuration.Bind(configSectionName, options));

            services.AddHttpContextAccessor();

            // Change the authentication configuration to accommodate the Microsoft identity platform endpoint (v2.0).
            services.Configure <JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
            {
                // This is an Microsoft identity platform Web API
                options.Authority += "/v2.0";

                // The valid audiences are both the Client ID (options.Audience) and api://{ClientID}
                options.TokenValidationParameters.ValidAudiences = new string[]
                {
                    options.Audience, $"api://{options.Audience}"
                };

                // 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);
                }

                // 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.
                options.Events = new JwtBearerEvents();

                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))
                    {
                        throw new UnauthorizedAccessException("Neither scope or roles claim was found in the bearer token.");
                    }

                    await Task.FromResult(0);
                };

                if (subscribeToJwtBearerMiddlewareDiagnosticsEvents)
                {
                    options.Events = JwtBearerMiddlewareDiagnostics.Subscribe(options.Events);
                }
            });

            return(services);
        }
예제 #2
0
        /// <summary>
        /// Protects the Web API with Microsoft Identity Platform v2.0 (AAD v2.0)
        /// This supposes that the configuration files have a section named "AzureAD"
        /// </summary>
        /// <param name="services">Service collection to which to add authentication</param>
        /// <param name="configuration">Configuration</param>
        /// <returns></returns>
        public static IServiceCollection AddProtectWebApiWithMicrosoftIdentityPlatformV2(this IServiceCollection services, IConfiguration configuration)
        {
            var azureAd = new AzureAd();

            configuration.GetSection(nameof(AzureAd)).Bind(azureAd);


            services.AddAuthentication(AzureADDefaults.JwtBearerAuthenticationScheme)
            .AddAzureADBearer(options => configuration.Bind("AzureAd", options));

            services.AddSession();

            // Added
            services.Configure <JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
            {
                // This is an Azure AD v2.0 Web API
                options.Authority += "/v2.0";

                // The valid audiences are both the Client ID (options.Audience) and api://{ClientID}
                options.TokenValidationParameters.ValidAudiences = new string[] { azureAd.Audience, $"api://{azureAd.ClientId}/access_as_user" };

                // Instead of using the default validation (validating against a single tenant, as we do in line of business apps),
                // we inject our own multitenant validation logic (which even accepts both V1 and V2 tokens)
                options.TokenValidationParameters.IssuerValidator = AadIssuerValidator.ForAadInstance(options.Authority).ValidateAadIssuer;

                // 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.
                options.Events = new JwtBearerEvents();

                // If you want to debug, or just understand the JwtBearer events, uncomment the following line of code
                options.Events = JwtBearerMiddlewareDiagnostics.Subscribe(options.Events);
            });

            return(services);
        }
        private void SetupAdAuthenticationV2(IServiceCollection services)
        {
            services.AddAuthentication(AzureADDefaults.JwtBearerAuthenticationScheme)
            .AddAzureADBearer(options =>
            {
                this.Configuration.Bind("AzureAd", options);
                Console.WriteLine($"the AddAzureADBearer options have been configured for ClientId = {options.ClientId}");
            });

            services.AddSession();
            services.AddTokenAcquisition();
            // Added
            services.Configure <JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
            {
                var scopes = new[] { PathwaysConstants.Graph.ScopeUserRead };
                this.Configuration.Bind("AzureAd", options);
                // This is an Azure AD v2.0 Web API
                options.Authority += "/v2.0";

                // The valid audiences are both the Client ID (options.Audience) and api://{ClientID}
                options.TokenValidationParameters.ValidAudiences = new[] { options.Audience, $"api://{options.Audience}" };

                // Instead of using the default validation (validating against a single tenant, as we do in line of business apps),
                // we inject our own multitenant validation logic (which even accepts both V1 and V2 tokens)
                options.TokenValidationParameters.IssuerValidator = AadIssuerValidator.ForAadInstance(options.Authority).ValidateAadIssuer;

                // 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.
                // options.Events = new JwtBearerEvents();

                // If you want to debug, or just understand the JwtBearer events, uncomment the following line of code
                // Events e = new Events();
                options.Events = JwtBearerMiddlewareDiagnostics.Subscribe(options.Events);

                options.Events.OnTokenValidated = async context =>
                {
                    if (scopes != null && scopes.Any())
                    {
                        var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService <ITokenAcquisition>();
                        context.Success();
                        tokenAcquisition.AddAccountToCacheFromJwt(context, scopes);
                    }
                    else
                    {
                        context.Success();

                        // Todo : rather use options.SaveToken?
                        (context.Principal.Identity as ClaimsIdentity).AddClaim(new Claim("jwt", (context.SecurityToken as JwtSecurityToken).RawData));
                    }

                    // Adds the token to the cache, and also handles the incremental consent and claim challenges
                    await Task.FromResult(0);
                };
            });

            services.AddMsal(new[] { PathwaysConstants.Graph.ScopeUserRead });
            services.AddInMemoryTokenCaches();
        }
예제 #4
0
        public void ConfigureDevelopmentServices(IServiceCollection services)
        {
            //add diagnostics support for the JwtBearer middleware
            services.PostConfigure <JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
            {
                options.Events = JwtBearerMiddlewareDiagnostics.Subscribe(options.Events);
            });

            ConfigureServices(services);
        }
 public JwtBearerMiddlewareDiagnosticsTests()
 {
     _customEventWasRaised = false;
     _httpContext          = HttpContextUtilities.CreateHttpContext();
     _logger         = Substitute.For <ILogger <JwtBearerMiddlewareDiagnostics> >();
     _jwtDiagnostics = new JwtBearerMiddlewareDiagnostics(new LoggerMock <JwtBearerMiddlewareDiagnostics>(_logger));
     _jwtOptions     = new JwtBearerOptions();
     _jwtEvents      = new JwtBearerEvents();
     _authScheme     = new AuthenticationScheme(JwtBearerDefaults.AuthenticationScheme, JwtBearerDefaults.AuthenticationScheme, typeof(JwtBearerHandler));
     _eventHandler   = (context) => {
         _customEventWasRaised = true;
         return(Task.CompletedTask);
     };
 }
        /// <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="configSectionName">The configuration section with the necessary settings to initialize authentication options</param>
        /// <param name="configuration">The Configuration object</param>
        /// <param name="jwtBearerScheme">The JwtBearer scheme name to be used. By default it uses "Bearer"</param>
        /// <param name="configureOptions">An action to configure JwtBearerOptions</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></returns>
        public static AuthenticationBuilder AddProtectedWebApi(
            this AuthenticationBuilder builder,
            string configSectionName,
            IConfiguration configuration,
            string jwtBearerScheme,
            Action <JwtBearerOptions> configureOptions,
            X509Certificate2 tokenDecryptionCertificate          = null,
            bool subscribeToJwtBearerMiddlewareDiagnosticsEvents = false)
        {
            builder.Services.Configure(jwtBearerScheme, configureOptions);
            builder.Services.Configure <MicrosoftIdentityOptions>(options => configuration.Bind(configSectionName, options));

            builder.Services.AddHttpContextAccessor();

            // Change the authentication configuration to accommodate the Microsoft identity platform endpoint (v2.0).
            builder.AddJwtBearer(jwtBearerScheme, options =>
            {
                var microsoftIdentityOptions = configuration.GetSection(configSectionName).Get <MicrosoftIdentityOptions>();

                if (string.IsNullOrWhiteSpace(options.Authority))
                {
                    options.Authority = AuthorityHelpers.BuildAuthority(microsoftIdentityOptions);
                }

                // This is an Microsoft identity platform Web API
                EnsureAuthorityIsV2_0(options);

                // 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)
                {
                    options.Events = JwtBearerMiddlewareDiagnostics.Subscribe(options.Events);
                }
            });

            return(builder);
        }