diff options
author | 杨宇千 <crupest@outlook.com> | 2019-07-27 21:47:14 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-27 21:47:14 +0800 |
commit | 590a8c576f17817539505ef2ca50f52e840a61d2 (patch) | |
tree | 572a2ae5c65c484718b3bfda68fd8babc56fe6f2 /Timeline/Authenticate/AuthHandler.cs | |
parent | 3de4179449a209646e0e5a967d270f7fa0878c03 (diff) | |
parent | 58985e8f2a6931029974067b2c1e78963e4508f0 (diff) | |
download | timeline-590a8c576f17817539505ef2ca50f52e840a61d2.tar.gz timeline-590a8c576f17817539505ef2ca50f52e840a61d2.tar.bz2 timeline-590a8c576f17817539505ef2ca50f52e840a61d2.zip |
Merge pull request #25 from crupest/auth
Refactor a lot, especially authentication.
Diffstat (limited to 'Timeline/Authenticate/AuthHandler.cs')
-rw-r--r-- | Timeline/Authenticate/AuthHandler.cs | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/Timeline/Authenticate/AuthHandler.cs b/Timeline/Authenticate/AuthHandler.cs new file mode 100644 index 00000000..80860edf --- /dev/null +++ b/Timeline/Authenticate/AuthHandler.cs @@ -0,0 +1,97 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.Net.Http.Headers; +using System; +using System.Linq; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Timeline.Services; + +namespace Timeline.Authenticate +{ + static class AuthConstants + { + public const string Scheme = "Bearer"; + public const string DisplayName = "My Jwt Auth Scheme"; + } + + class AuthOptions : AuthenticationSchemeOptions + { + /// <summary> + /// The query param key to search for token. If null then query params are not searched for token. Default to <c>"token"</c>. + /// </summary> + public string TokenQueryParamKey { get; set; } = "token"; + } + + class AuthHandler : AuthenticationHandler<AuthOptions> + { + private readonly ILogger<AuthHandler> _logger; + private readonly IUserService _userService; + + public AuthHandler(IOptionsMonitor<AuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IUserService userService) + : base(options, logger, encoder, clock) + { + _logger = logger.CreateLogger<AuthHandler>(); + _userService = userService; + } + + // 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<AuthenticateResult> HandleAuthenticateAsync() + { + var token = ExtractToken(); + if (string.IsNullOrEmpty(token)) + { + _logger.LogInformation("No jwt token is found."); + return AuthenticateResult.NoResult(); + } + + try + { + var userInfo = await _userService.VerifyToken(token); + + var identity = new ClaimsIdentity(AuthConstants.Scheme); + identity.AddClaim(new Claim(identity.NameClaimType, userInfo.Username, ClaimValueTypes.String)); + identity.AddClaims(Entities.UserUtility.IsAdminToRoleArray(userInfo.IsAdmin).Select(role => new Claim(identity.RoleClaimType, role, ClaimValueTypes.String))); + + var principal = new ClaimsPrincipal(); + principal.AddIdentity(identity); + + return AuthenticateResult.Success(new AuthenticationTicket(principal, AuthConstants.Scheme)); + } + catch (Exception e) + { + _logger.LogInformation(e, "A jwt token validation failed."); + return AuthenticateResult.Fail(e); + } + } + } +} |