From ac769e656b122ff569c3f1534701b71e00fed586 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 27 Oct 2020 19:21:35 +0800 Subject: Split front and back end. --- BackEnd/Timeline/Auth/MyAuthenticationHandler.cs | 100 +++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 BackEnd/Timeline/Auth/MyAuthenticationHandler.cs (limited to 'BackEnd/Timeline/Auth/MyAuthenticationHandler.cs') diff --git a/BackEnd/Timeline/Auth/MyAuthenticationHandler.cs b/BackEnd/Timeline/Auth/MyAuthenticationHandler.cs new file mode 100644 index 00000000..3c97c329 --- /dev/null +++ b/BackEnd/Timeline/Auth/MyAuthenticationHandler.cs @@ -0,0 +1,100 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; +using System; +using System.Globalization; +using System.Linq; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Timeline.Services; +using static Timeline.Resources.Authentication.AuthHandler; + +namespace Timeline.Auth +{ + public static class AuthenticationConstants + { + public const string Scheme = "Bearer"; + public const string DisplayName = "My Jwt Auth Scheme"; + } + + public class MyAuthenticationOptions : 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 class MyAuthenticationHandler : AuthenticationHandler + { + private readonly ILogger _logger; + private readonly IUserTokenManager _userTokenManager; + + public MyAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IUserTokenManager userTokenManager) + : base(options, logger, encoder, clock) + { + _logger = logger.CreateLogger(); + _userTokenManager = userTokenManager; + } + + // 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.InvariantCultureIgnoreCase)) + { + var token = header.Substring("Bearer ".Length).Trim(); + _logger.LogInformation(LogTokenFoundInHeader, 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(LogTokenFoundInQuery, 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(LogTokenNotFound); + return AuthenticateResult.NoResult(); + } + + try + { + var userInfo = await _userTokenManager.VerifyToken(token); + + var identity = new ClaimsIdentity(AuthenticationConstants.Scheme); + identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userInfo.Id!.Value.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64)); + identity.AddClaim(new Claim(identity.NameClaimType, userInfo.Username, ClaimValueTypes.String)); + identity.AddClaims(UserRoleConvert.ToArray(userInfo.Administrator!.Value).Select(role => new Claim(identity.RoleClaimType, role, ClaimValueTypes.String))); + + var principal = new ClaimsPrincipal(); + principal.AddIdentity(identity); + + return AuthenticateResult.Success(new AuthenticationTicket(principal, AuthenticationConstants.Scheme)); + } + catch (Exception e) when (!(e is ArgumentException)) + { + _logger.LogInformation(e, LogTokenValidationFail); + return AuthenticateResult.Fail(e); + } + } + } +} -- cgit v1.2.3