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