Add project files.
This commit is contained in:
		
							parent
							
								
									09e99a3f96
								
							
						
					
					
						commit
						d06df142e8
					
				
							
								
								
									
										25
									
								
								PoweredSoft.DynamicJwtBearer.sln
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								PoweredSoft.DynamicJwtBearer.sln
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  |  | ||||||
|  | Microsoft Visual Studio Solution File, Format Version 12.00 | ||||||
|  | # Visual Studio Version 16 | ||||||
|  | VisualStudioVersion = 16.0.29503.13 | ||||||
|  | MinimumVisualStudioVersion = 10.0.40219.1 | ||||||
|  | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.DynamicJwtBearer", "PoweredSoft.DynamicJwtBearer\PoweredSoft.DynamicJwtBearer.csproj", "{0A15F002-66C4-44D1-8162-563F860C49E4}" | ||||||
|  | EndProject | ||||||
|  | Global | ||||||
|  | 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||||
|  | 		Debug|Any CPU = Debug|Any CPU | ||||||
|  | 		Release|Any CPU = Release|Any CPU | ||||||
|  | 	EndGlobalSection | ||||||
|  | 	GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||||||
|  | 		{0A15F002-66C4-44D1-8162-563F860C49E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||||
|  | 		{0A15F002-66C4-44D1-8162-563F860C49E4}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||||
|  | 		{0A15F002-66C4-44D1-8162-563F860C49E4}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||||
|  | 		{0A15F002-66C4-44D1-8162-563F860C49E4}.Release|Any CPU.Build.0 = Release|Any CPU | ||||||
|  | 	EndGlobalSection | ||||||
|  | 	GlobalSection(SolutionProperties) = preSolution | ||||||
|  | 		HideSolutionNode = FALSE | ||||||
|  | 	EndGlobalSection | ||||||
|  | 	GlobalSection(ExtensibilityGlobals) = postSolution | ||||||
|  | 		SolutionGuid = {9D366086-7F19-4F1E-A5B5-DAEF1EA56D90} | ||||||
|  | 	EndGlobalSection | ||||||
|  | EndGlobal | ||||||
							
								
								
									
										22
									
								
								PoweredSoft.DynamicJwtBearer/DynamicJwtBearerExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								PoweredSoft.DynamicJwtBearer/DynamicJwtBearerExtensions.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | using Microsoft.AspNetCore.Authentication; | ||||||
|  | using Microsoft.AspNetCore.Authentication.JwtBearer; | ||||||
|  | using Microsoft.Extensions.DependencyInjection; | ||||||
|  | using Microsoft.Extensions.DependencyInjection.Extensions; | ||||||
|  | using Microsoft.Extensions.Options; | ||||||
|  | using System; | ||||||
|  | 
 | ||||||
|  | namespace PoweredSoft.DynamicJwtBearer | ||||||
|  | {  | ||||||
|  |     public static class DynamicJwtBearerExtensions | ||||||
|  |     { | ||||||
|  |         public static AuthenticationBuilder AddDynamicJwtBearer(this AuthenticationBuilder builder, string authenticationScheme, Action<JwtBearerOptions> action = null) | ||||||
|  |         { | ||||||
|  |             builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>, JwtBearerPostConfigureOptions>()); | ||||||
|  | 
 | ||||||
|  |             if (action != null) | ||||||
|  |                 return builder.AddScheme<JwtBearerOptions, DynamicJwtBearerHandler>(authenticationScheme, null, action); | ||||||
|  | 
 | ||||||
|  |             return builder.AddScheme<JwtBearerOptions, DynamicJwtBearerHandler>(authenticationScheme, null, _ => { }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										174
									
								
								PoweredSoft.DynamicJwtBearer/DynamicJwtBearerHandler.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								PoweredSoft.DynamicJwtBearer/DynamicJwtBearerHandler.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,174 @@ | |||||||
|  | using Microsoft.AspNetCore.Authentication; | ||||||
|  | using Microsoft.AspNetCore.Authentication.JwtBearer; | ||||||
|  | using Microsoft.Extensions.Logging; | ||||||
|  | using Microsoft.Extensions.Options; | ||||||
|  | using Microsoft.IdentityModel.Tokens; | ||||||
|  | using Microsoft.Net.Http.Headers; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Security.Claims; | ||||||
|  | using System.Text.Encodings.Web; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | 
 | ||||||
|  | namespace PoweredSoft.DynamicJwtBearer | ||||||
|  | { | ||||||
|  |     public class DynamicJwtBearerHandler : JwtBearerHandler | ||||||
|  |     { | ||||||
|  |         private readonly IDynamicJwtBearerHanderConfigurationResolver dynamicJwtBearerHanderConfigurationResolver; | ||||||
|  | 
 | ||||||
|  |         public DynamicJwtBearerHandler(IOptionsMonitor<JwtBearerOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IDynamicJwtBearerHanderConfigurationResolver dynamicJwtBearerHanderConfigurationResolver) : base(options, logger, encoder, clock) | ||||||
|  |         { | ||||||
|  |             this.dynamicJwtBearerHanderConfigurationResolver = dynamicJwtBearerHanderConfigurationResolver; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Searches the 'Authorization' header for a 'Bearer' token. If the 'Bearer' token is found, it is validated using <see cref="TokenValidationParameters"/> set in the options. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         protected override async Task<AuthenticateResult> HandleAuthenticateAsync() | ||||||
|  |         { | ||||||
|  |             string token = null; | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 // Give application opportunity to find from a different location, adjust, or reject token | ||||||
|  |                 var messageReceivedContext = new MessageReceivedContext(Context, Scheme, Options); | ||||||
|  | 
 | ||||||
|  |                 // event can set the token | ||||||
|  |                 await Events.MessageReceived(messageReceivedContext); | ||||||
|  |                 if (messageReceivedContext.Result != null) | ||||||
|  |                 { | ||||||
|  |                     return messageReceivedContext.Result; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // If application retrieved token from somewhere else, use that. | ||||||
|  |                 token = messageReceivedContext.Token; | ||||||
|  | 
 | ||||||
|  |                 if (string.IsNullOrEmpty(token)) | ||||||
|  |                 { | ||||||
|  |                     string authorization = Request.Headers[HeaderNames.Authorization]; | ||||||
|  | 
 | ||||||
|  |                     // If no authorization header found, nothing to process further | ||||||
|  |                     if (string.IsNullOrEmpty(authorization)) | ||||||
|  |                     { | ||||||
|  |                         return AuthenticateResult.NoResult(); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) | ||||||
|  |                     { | ||||||
|  |                         token = authorization.Substring("Bearer ".Length).Trim(); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     // If no token found, no further work possible | ||||||
|  |                     if (string.IsNullOrEmpty(token)) | ||||||
|  |                     { | ||||||
|  |                         return AuthenticateResult.NoResult(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 var currentConfiguration = await this.dynamicJwtBearerHanderConfigurationResolver.ResolveCurrentOpenIdConfiguration(Context); | ||||||
|  |                 var validationParameters = Options.TokenValidationParameters.Clone(); | ||||||
|  |                 if (currentConfiguration != null) | ||||||
|  |                 { | ||||||
|  |                     var issuers = new[] { currentConfiguration.Issuer }; | ||||||
|  |                     validationParameters.ValidIssuers = validationParameters.ValidIssuers?.Concat(issuers) ?? issuers; | ||||||
|  | 
 | ||||||
|  |                     validationParameters.IssuerSigningKeys = validationParameters.IssuerSigningKeys?.Concat(currentConfiguration.SigningKeys) | ||||||
|  |                         ?? currentConfiguration.SigningKeys; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 List<Exception> validationFailures = null; | ||||||
|  |                 SecurityToken validatedToken; | ||||||
|  |                 foreach (var validator in Options.SecurityTokenValidators) | ||||||
|  |                 { | ||||||
|  |                     if (validator.CanReadToken(token)) | ||||||
|  |                     { | ||||||
|  |                         ClaimsPrincipal principal; | ||||||
|  |                         try | ||||||
|  |                         { | ||||||
|  |                             principal = validator.ValidateToken(token, validationParameters, out validatedToken); | ||||||
|  |                         } | ||||||
|  |                         catch (Exception ex) | ||||||
|  |                         { | ||||||
|  |                             Logger.TokenValidationFailed(ex); | ||||||
|  | 
 | ||||||
|  |                             // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event. | ||||||
|  |                             if (Options.RefreshOnIssuerKeyNotFound && Options.ConfigurationManager != null | ||||||
|  |                                 && ex is SecurityTokenSignatureKeyNotFoundException) | ||||||
|  |                             { | ||||||
|  |                                 Options.ConfigurationManager.RequestRefresh(); | ||||||
|  |                             } | ||||||
|  | 
 | ||||||
|  |                             if (validationFailures == null) | ||||||
|  |                             { | ||||||
|  |                                 validationFailures = new List<Exception>(1); | ||||||
|  |                             } | ||||||
|  |                             validationFailures.Add(ex); | ||||||
|  |                             continue; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         Logger.TokenValidationSucceeded(); | ||||||
|  | 
 | ||||||
|  |                         var tokenValidatedContext = new TokenValidatedContext(Context, Scheme, Options) | ||||||
|  |                         { | ||||||
|  |                             Principal = principal, | ||||||
|  |                             SecurityToken = validatedToken | ||||||
|  |                         }; | ||||||
|  | 
 | ||||||
|  |                         await Events.TokenValidated(tokenValidatedContext); | ||||||
|  |                         if (tokenValidatedContext.Result != null) | ||||||
|  |                         { | ||||||
|  |                             return tokenValidatedContext.Result; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         if (Options.SaveToken) | ||||||
|  |                         { | ||||||
|  |                             tokenValidatedContext.Properties.StoreTokens(new[] | ||||||
|  |                             { | ||||||
|  |                                 new AuthenticationToken { Name = "access_token", Value = token } | ||||||
|  |                             }); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         tokenValidatedContext.Success(); | ||||||
|  |                         return tokenValidatedContext.Result; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (validationFailures != null) | ||||||
|  |                 { | ||||||
|  |                     var authenticationFailedContext = new AuthenticationFailedContext(Context, Scheme, Options) | ||||||
|  |                     { | ||||||
|  |                         Exception = (validationFailures.Count == 1) ? validationFailures[0] : new AggregateException(validationFailures) | ||||||
|  |                     }; | ||||||
|  | 
 | ||||||
|  |                     await Events.AuthenticationFailed(authenticationFailedContext); | ||||||
|  |                     if (authenticationFailedContext.Result != null) | ||||||
|  |                     { | ||||||
|  |                         return authenticationFailedContext.Result; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     return AuthenticateResult.Fail(authenticationFailedContext.Exception); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return AuthenticateResult.Fail("No SecurityTokenValidator available for token: " + token ?? "[null]"); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 Logger.ErrorProcessingMessage(ex); | ||||||
|  | 
 | ||||||
|  |                 var authenticationFailedContext = new AuthenticationFailedContext(Context, Scheme, Options) | ||||||
|  |                 { | ||||||
|  |                     Exception = ex | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |                 await Events.AuthenticationFailed(authenticationFailedContext); | ||||||
|  |                 if (authenticationFailedContext.Result != null) | ||||||
|  |                 { | ||||||
|  |                     return authenticationFailedContext.Result; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 throw; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,11 @@ | |||||||
|  | using Microsoft.AspNetCore.Http; | ||||||
|  | using Microsoft.IdentityModel.Protocols.OpenIdConnect; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | 
 | ||||||
|  | namespace PoweredSoft.DynamicJwtBearer | ||||||
|  | { | ||||||
|  |     public interface IDynamicJwtBearerHanderConfigurationResolver | ||||||
|  |     { | ||||||
|  |         Task<OpenIdConnectConfiguration> ResolveCurrentOpenIdConfiguration(HttpContext context); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								PoweredSoft.DynamicJwtBearer/LoggingExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								PoweredSoft.DynamicJwtBearer/LoggingExtensions.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | using Microsoft.Extensions.Logging; | ||||||
|  | using System; | ||||||
|  | 
 | ||||||
|  | namespace PoweredSoft.DynamicJwtBearer | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     internal static class LoggingExtensions | ||||||
|  |     { | ||||||
|  |         private static Action<ILogger, Exception> _tokenValidationFailed; | ||||||
|  |         private static Action<ILogger, Exception> _tokenValidationSucceeded; | ||||||
|  |         private static Action<ILogger, Exception> _errorProcessingMessage; | ||||||
|  | 
 | ||||||
|  |         static LoggingExtensions() | ||||||
|  |         { | ||||||
|  |             _tokenValidationFailed = LoggerMessage.Define( | ||||||
|  |                 eventId: new EventId(1, "TokenValidationFailed"), | ||||||
|  |                 logLevel: LogLevel.Information, | ||||||
|  |                 formatString: "Failed to validate the token."); | ||||||
|  |             _tokenValidationSucceeded = LoggerMessage.Define( | ||||||
|  |                 eventId: new EventId(2, "TokenValidationSucceeded"), | ||||||
|  |                 logLevel: LogLevel.Information, | ||||||
|  |                 formatString: "Successfully validated the token."); | ||||||
|  |             _errorProcessingMessage = LoggerMessage.Define( | ||||||
|  |                 eventId: new EventId(3, "ProcessingMessageFailed"), | ||||||
|  |                 logLevel: LogLevel.Error, | ||||||
|  |                 formatString: "Exception occurred while processing message."); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void TokenValidationFailed(this ILogger logger, Exception ex) | ||||||
|  |             => _tokenValidationFailed(logger, ex); | ||||||
|  | 
 | ||||||
|  |         public static void TokenValidationSucceeded(this ILogger logger) | ||||||
|  |             => _tokenValidationSucceeded(logger, null); | ||||||
|  | 
 | ||||||
|  |         public static void ErrorProcessingMessage(this ILogger logger, Exception ex) | ||||||
|  |             => _errorProcessingMessage(logger, ex); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,12 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk"> | ||||||
|  | 
 | ||||||
|  |   <PropertyGroup> | ||||||
|  |     <TargetFramework>netcoreapp3.0</TargetFramework> | ||||||
|  |   </PropertyGroup> | ||||||
|  | 
 | ||||||
|  |   <ItemGroup> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.0.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.0.0" /> | ||||||
|  |   </ItemGroup> | ||||||
|  | 
 | ||||||
|  | </Project> | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user