From 4f64663dde8ebc6e875ca29c31775c01e27727ca Mon Sep 17 00:00:00 2001 From: Tugberk Ugurlu Date: Wed, 2 Dec 2015 08:08:28 +0000 Subject: [PATCH] added SyncMetadataImmediately option to IdentityServerBearerTokenAuthenticationOptions to make it possible to evaluate lazily --- ...yServerBearerTokenAuthenticationOptions.cs | 7 + ...arerTokenValidationAppBuilderExtensions.cs | 155 ++++++++++-------- ...tyServerBearerTokenValidationMiddleware.cs | 36 ++-- ...veryDocumentIssuerSecurityTokenProvider.cs | 6 +- ...yServerOAuthBearerAuthenticationOptions.cs | 5 +- 5 files changed, 126 insertions(+), 83 deletions(-) diff --git a/source/AccessTokenValidation/IdentityServerBearerTokenAuthenticationOptions.cs b/source/AccessTokenValidation/IdentityServerBearerTokenAuthenticationOptions.cs index fb5febb..47f0505 100644 --- a/source/AccessTokenValidation/IdentityServerBearerTokenAuthenticationOptions.cs +++ b/source/AccessTokenValidation/IdentityServerBearerTokenAuthenticationOptions.cs @@ -41,6 +41,7 @@ public IdentityServerBearerTokenAuthenticationOptions() : base("Bearer") RequiredScopes = Enumerable.Empty(); ValidationResultCacheDuration = TimeSpan.FromMinutes(5); PreserveAccessToken = false; + DelayLoadMetadata = false; } /// @@ -178,5 +179,11 @@ public IdentityServerBearerTokenAuthenticationOptions() : base("Bearer") /// The introspection HTTP handler. /// public WebRequestHandler IntrospectionHttpHandler { get; set; } + + /// + /// Indicates whether the discovery metadata sync to be delayed during the construction of + /// the pipeline. false by default. + /// + public bool DelayLoadMetadata { get; set; } } } \ No newline at end of file diff --git a/source/AccessTokenValidation/IdentityServerBearerTokenValidationAppBuilderExtensions.cs b/source/AccessTokenValidation/IdentityServerBearerTokenValidationAppBuilderExtensions.cs index 78df0c0..ee66f2c 100644 --- a/source/AccessTokenValidation/IdentityServerBearerTokenValidationAppBuilderExtensions.cs +++ b/source/AccessTokenValidation/IdentityServerBearerTokenValidationAppBuilderExtensions.cs @@ -59,6 +59,21 @@ public static IAppBuilder UseIdentityServerBearerTokenAuthentication(this IAppBu throw new Exception("ValidationMode has invalid value"); } + if (!options.DelayLoadMetadata) + { + // evaluate the lazy members so that they can do their job + + if (middlewareOptions.LocalValidationOptions != null) + { + var ignore = middlewareOptions.LocalValidationOptions.Value; + } + + if (middlewareOptions.EndpointValidationOptions != null) + { + var ignore = middlewareOptions.EndpointValidationOptions.Value; + } + } + if (options.TokenProvider != null) { middlewareOptions.TokenProvider = options.TokenProvider; @@ -79,94 +94,102 @@ public static IAppBuilder UseIdentityServerBearerTokenAuthentication(this IAppBu return app; } - private static OAuthBearerAuthenticationOptions ConfigureEndpointValidation(IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory) + private static Lazy ConfigureEndpointValidation(IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory) { - if (options.EnableValidationResultCache) + return new Lazy(() => { - if (options.ValidationResultCache == null) + if (options.EnableValidationResultCache) { - options.ValidationResultCache = new InMemoryValidationResultCache(options); + if (options.ValidationResultCache == null) + { + options.ValidationResultCache = new InMemoryValidationResultCache(options); + } } - } - var bearerOptions = new OAuthBearerAuthenticationOptions - { - AuthenticationMode = options.AuthenticationMode, - AuthenticationType = options.AuthenticationType, - Provider = new ContextTokenProvider(options.TokenProvider), - }; + var bearerOptions = new OAuthBearerAuthenticationOptions + { + AuthenticationMode = options.AuthenticationMode, + AuthenticationType = options.AuthenticationType, + Provider = new ContextTokenProvider(options.TokenProvider), + }; - if (!string.IsNullOrEmpty(options.ClientId) || options.IntrospectionHttpHandler != null) - { - bearerOptions.AccessTokenProvider = new IntrospectionEndpointTokenProvider(options, loggerFactory); - } - else - { - bearerOptions.AccessTokenProvider = new ValidationEndpointTokenProvider(options, loggerFactory); - } + if (!string.IsNullOrEmpty(options.ClientId) || options.IntrospectionHttpHandler != null) + { + bearerOptions.AccessTokenProvider = new IntrospectionEndpointTokenProvider(options, loggerFactory); + } + else + { + bearerOptions.AccessTokenProvider = new ValidationEndpointTokenProvider(options, loggerFactory); + } - return bearerOptions; + return bearerOptions; + + }, true); } - internal static OAuthBearerAuthenticationOptions ConfigureLocalValidation(IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory) + internal static Lazy ConfigureLocalValidation(IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory) { - JwtFormat tokenFormat = null; - - // use static configuration - if (!string.IsNullOrWhiteSpace(options.IssuerName) && - options.SigningCertificate != null) + return new Lazy(() => { - var audience = options.IssuerName.EnsureTrailingSlash(); - audience += "resources"; + JwtFormat tokenFormat = null; - var valParams = new TokenValidationParameters - { - ValidIssuer = options.IssuerName, - ValidAudience = audience, - IssuerSigningToken = new X509SecurityToken(options.SigningCertificate), + // use static configuration + if (!string.IsNullOrWhiteSpace(options.IssuerName) && + options.SigningCertificate != null) + { + var audience = options.IssuerName.EnsureTrailingSlash(); + audience += "resources"; - NameClaimType = options.NameClaimType, - RoleClaimType = options.RoleClaimType, - }; + var valParams = new TokenValidationParameters + { + ValidIssuer = options.IssuerName, + ValidAudience = audience, + IssuerSigningToken = new X509SecurityToken(options.SigningCertificate), - tokenFormat = new JwtFormat(valParams); - } - else - { - // use discovery endpoint - if (string.IsNullOrWhiteSpace(options.Authority)) + NameClaimType = options.NameClaimType, + RoleClaimType = options.RoleClaimType, + }; + + tokenFormat = new JwtFormat(valParams); + } + else { - throw new Exception("Either set IssuerName and SigningCertificate - or Authority"); + // use discovery endpoint + if (string.IsNullOrWhiteSpace(options.Authority)) + { + throw new Exception("Either set IssuerName and SigningCertificate - or Authority"); + } + + var discoveryEndpoint = options.Authority.EnsureTrailingSlash(); + discoveryEndpoint += ".well-known/openid-configuration"; + + var issuerProvider = new DiscoveryDocumentIssuerSecurityTokenProvider( + discoveryEndpoint, + options, + loggerFactory); + + var valParams = new TokenValidationParameters + { + ValidAudience = issuerProvider.Audience, + NameClaimType = options.NameClaimType, + RoleClaimType = options.RoleClaimType + }; + + tokenFormat = new JwtFormat(valParams, issuerProvider); } - var discoveryEndpoint = options.Authority.EnsureTrailingSlash(); - discoveryEndpoint += ".well-known/openid-configuration"; - var issuerProvider = new DiscoveryDocumentIssuerSecurityTokenProvider( - discoveryEndpoint, - options, - loggerFactory); - - var valParams = new TokenValidationParameters + var bearerOptions = new OAuthBearerAuthenticationOptions { - ValidAudience = issuerProvider.Audience, - NameClaimType = options.NameClaimType, - RoleClaimType = options.RoleClaimType + AccessTokenFormat = tokenFormat, + AuthenticationMode = options.AuthenticationMode, + AuthenticationType = options.AuthenticationType, + Provider = new ContextTokenProvider(options.TokenProvider) }; - tokenFormat = new JwtFormat(valParams, issuerProvider); - } - - - var bearerOptions = new OAuthBearerAuthenticationOptions - { - AccessTokenFormat = tokenFormat, - AuthenticationMode = options.AuthenticationMode, - AuthenticationType = options.AuthenticationType, - Provider = new ContextTokenProvider(options.TokenProvider) - }; + return bearerOptions; - return bearerOptions; + }, true); } } } \ No newline at end of file diff --git a/source/AccessTokenValidation/IdentityServerBearerTokenValidationMiddleware.cs b/source/AccessTokenValidation/IdentityServerBearerTokenValidationMiddleware.cs index f272e1c..74e5b1c 100644 --- a/source/AccessTokenValidation/IdentityServerBearerTokenValidationMiddleware.cs +++ b/source/AccessTokenValidation/IdentityServerBearerTokenValidationMiddleware.cs @@ -33,8 +33,8 @@ namespace IdentityServer3.AccessTokenValidation public class IdentityServerBearerTokenValidationMiddleware { private readonly AppFunc _next; - private readonly AppFunc _localValidationFunc; - private readonly AppFunc _endpointValidationFunc; + private readonly Lazy _localValidationFunc; + private readonly Lazy _endpointValidationFunc; private readonly IdentityServerOAuthBearerAuthenticationOptions _options; private readonly ILogger _logger; @@ -53,20 +53,28 @@ public IdentityServerBearerTokenValidationMiddleware(AppFunc next, IAppBuilder a if (options.LocalValidationOptions != null) { - var localBuilder = app.New(); - localBuilder.UseOAuthBearerAuthentication(options.LocalValidationOptions); - localBuilder.Run(ctx => next(ctx.Environment)); - _localValidationFunc = localBuilder.Build(); + _localValidationFunc = new Lazy(() => + { + var localBuilder = app.New(); + localBuilder.UseOAuthBearerAuthentication(options.LocalValidationOptions.Value); + localBuilder.Run(ctx => next(ctx.Environment)); + return localBuilder.Build(); + + }, true); } if (options.EndpointValidationOptions != null) { - var endpointBuilder = app.New(); - endpointBuilder.Properties["host.AppName"] = "foobar"; + _endpointValidationFunc = new Lazy(() => + { + var endpointBuilder = app.New(); + endpointBuilder.Properties["host.AppName"] = "foobar"; + + endpointBuilder.UseOAuthBearerAuthentication(options.EndpointValidationOptions.Value); + endpointBuilder.Run(ctx => next(ctx.Environment)); + return endpointBuilder.Build(); - endpointBuilder.UseOAuthBearerAuthentication(options.EndpointValidationOptions); - endpointBuilder.Run(ctx => next(ctx.Environment)); - _endpointValidationFunc = endpointBuilder.Build(); + }, true); } } @@ -95,13 +103,13 @@ public async Task Invoke(IDictionary environment) // see if local validation is setup if (_localValidationFunc != null) { - await _localValidationFunc(environment); + await _localValidationFunc.Value(environment); return; } // otherwise use validation endpoint if (_endpointValidationFunc != null) { - await _endpointValidationFunc(environment); + await _endpointValidationFunc.Value(environment); return; } @@ -112,7 +120,7 @@ public async Task Invoke(IDictionary environment) // use validation endpoint if (_endpointValidationFunc != null) { - await _endpointValidationFunc(environment); + await _endpointValidationFunc.Value(environment); return; } diff --git a/source/AccessTokenValidation/Plumbing/DiscoveryDocumentIssuerSecurityTokenProvider.cs b/source/AccessTokenValidation/Plumbing/DiscoveryDocumentIssuerSecurityTokenProvider.cs index 7ce8a98..f2243f9 100644 --- a/source/AccessTokenValidation/Plumbing/DiscoveryDocumentIssuerSecurityTokenProvider.cs +++ b/source/AccessTokenValidation/Plumbing/DiscoveryDocumentIssuerSecurityTokenProvider.cs @@ -56,7 +56,11 @@ public DiscoveryDocumentIssuerSecurityTokenProvider(string discoveryEndpoint, Id } _configurationManager = new ConfigurationManager(discoveryEndpoint, new HttpClient(handler)); - RetrieveMetadata(); + + if (!options.DelayLoadMetadata) + { + RetrieveMetadata(); + } } /// diff --git a/source/AccessTokenValidation/Plumbing/IdentityServerOAuthBearerAuthenticationOptions.cs b/source/AccessTokenValidation/Plumbing/IdentityServerOAuthBearerAuthenticationOptions.cs index 3d3c0f3..6a7df68 100644 --- a/source/AccessTokenValidation/Plumbing/IdentityServerOAuthBearerAuthenticationOptions.cs +++ b/source/AccessTokenValidation/Plumbing/IdentityServerOAuthBearerAuthenticationOptions.cs @@ -15,6 +15,7 @@ */ using Microsoft.Owin.Security.OAuth; +using System; namespace IdentityServer3.AccessTokenValidation { @@ -37,7 +38,7 @@ public class IdentityServerOAuthBearerAuthenticationOptions /// /// The local validation options. /// - public OAuthBearerAuthenticationOptions LocalValidationOptions { get; set; } + public Lazy LocalValidationOptions { get; set; } /// /// Gets or sets the endpoint validation options. @@ -45,6 +46,6 @@ public class IdentityServerOAuthBearerAuthenticationOptions /// /// The endpoint validation options. /// - public OAuthBearerAuthenticationOptions EndpointValidationOptions { get; set; } + public Lazy EndpointValidationOptions { get; set; } } } \ No newline at end of file