using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Microsoft.Net.Http.Headers; using System; using System.IdentityModel.Tokens.Jwt; using System.Text.Encodings.Web; using System.Threading.Tasks; namespace Timeline.Authenticate { static class AuthConstants { public const string Scheme = "Bearer"; public const string DisplayName = "My Jwt Auth Scheme"; } class AuthOptions : AuthenticationSchemeOptions { /// /// The query param key to search for token. If null then query params are not searched for token. Default to "token". /// public string TokenQueryParamKey { get; set; } = "token"; public TokenValidationParameters TokenValidationParameters { get; set; } = new TokenValidationParameters(); } class AuthHandler : AuthenticationHandler { private readonly ILogger _logger; public AuthHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { _logger = logger.CreateLogger(); } // return null if no token is found private string ExtractToken() { // check the authorization header string header = Request.Headers[HeaderNames.Authorization]; if (!string.IsNullOrEmpty(header) && header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { var token = header.Substring("Bearer ".Length).Trim(); _logger.LogInformation("Token is found in authorization header. Token is {} .", token); return token; } // check the query params var paramQueryKey = Options.TokenQueryParamKey; if (!string.IsNullOrEmpty(paramQueryKey)) { string token = Request.Query[paramQueryKey]; if (!string.IsNullOrEmpty(token)) { _logger.LogInformation("Token is found in query param with key \"{}\". Token is {} .", paramQueryKey, token); return token; } } // not found anywhere then return null return null; } protected override async Task HandleAuthenticateAsync() { var token = ExtractToken(); if (string.IsNullOrEmpty(token)) { _logger.LogInformation("No jwt token is found."); return AuthenticateResult.NoResult(); } var handler = new JwtSecurityTokenHandler(); try { var principal = handler.ValidateToken(token, Options.TokenValidationParameters, out var validatedToken); return AuthenticateResult.Success(new AuthenticationTicket(principal, AuthConstants.Scheme)); } catch (SecurityTokenException e) { _logger.LogInformation(e, "A jwt token validation failed."); return AuthenticateResult.Fail(e); } catch (Exception e) { _logger.LogError(e, "Arguments passed to the JwtSecurityTokenHandler.ValidateToken are bad."); throw e; } } } }