From b4f783c20aa47cb601dc81e0dad07aa92517c229 Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 16 Apr 2022 22:11:29 +0800 Subject: ... --- BackEnd/Timeline/Controllers/TokenController.cs | 4 +- .../Timeline/Controllers/V2/TokenV2Controller.cs | 132 +++++++++++++++++++++ 2 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 BackEnd/Timeline/Controllers/V2/TokenV2Controller.cs (limited to 'BackEnd/Timeline/Controllers') diff --git a/BackEnd/Timeline/Controllers/TokenController.cs b/BackEnd/Timeline/Controllers/TokenController.cs index 9ee5a09f..7fba0bc5 100644 --- a/BackEnd/Timeline/Controllers/TokenController.cs +++ b/BackEnd/Timeline/Controllers/TokenController.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; @@ -77,7 +77,7 @@ namespace Timeline.Controllers [AllowAnonymous] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public async Task> Verify([FromBody] HttpVerifyTokenRequest request) + public async Task> Verify([FromBody] HttpVerifyOrRevokeTokenRequest request) { try { diff --git a/BackEnd/Timeline/Controllers/V2/TokenV2Controller.cs b/BackEnd/Timeline/Controllers/V2/TokenV2Controller.cs new file mode 100644 index 00000000..b129758a --- /dev/null +++ b/BackEnd/Timeline/Controllers/V2/TokenV2Controller.cs @@ -0,0 +1,132 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Threading.Tasks; +using Timeline.Models.Http; +using Timeline.Services; +using Timeline.Services.Token; +using Timeline.Services.User; + +namespace Timeline.Controllers.V2 +{ + [ApiController] + [Route("v2/token")] + public class TokenV2Controller : V2ControllerBase + { + private readonly IUserService _userService; + private readonly IUserTokenService _userTokenService; + private readonly IClock _clock; + + public TokenV2Controller(IUserService userService, IUserTokenService userTokenService, IClock clock) + { + _userService = userService; + _userTokenService = userTokenService; + _clock = clock; + } + + private const string BadCredentialMessage = "Username or password is wrong."; + + /// + /// Create a new token for a user. + /// + /// Result of token creation. + [HttpPost("create")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)] + public async Task> CreateAsync([FromBody] HttpCreateTokenRequestV2 request) + { + + try + { + DateTime? expireTime = null; + if (request.ValidDays is not null) + expireTime = _clock.GetCurrentTime().AddDays(request.ValidDays.Value); + + var userId = await _userService.VerifyCredential(request.Username, request.Password); + var token = await _userTokenService.CreateTokenAsync(userId, expireTime); + var user = await _userService.GetUserAsync(userId); + + return new HttpCreateTokenResponse + { + Token = token, + User = await MapAsync(user) + }; + } + catch (EntityNotExistException) + { + return UnprocessableEntity(new ErrorResponse(ErrorResponse.InvalidRequest, BadCredentialMessage)); + } + catch (BadPasswordException) + { + return UnprocessableEntity(new ErrorResponse(ErrorResponse.InvalidRequest, BadCredentialMessage)); + } + } + + private const string TokenExpiredMessage = "The token has expired."; + private const string TokenInvalidMessage = "The token is invalid."; + + /// + /// Verify a token. + /// + /// Result of token verification. + [HttpPost("verify")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)] + public async Task> VerifyAsync([FromBody] HttpVerifyOrRevokeTokenRequest request) + { + try + { + var tokenInfo = await _userTokenService.ValidateTokenAsync(request.Token); + var user = await _userService.GetUserAsync(tokenInfo.UserId); + return new HttpVerifyTokenResponseV2 + { + User = await MapAsync(user), + ExpireAt = tokenInfo.ExpireAt + }; + } + catch (UserTokenExpiredException) + { + return UnprocessableEntity(new ErrorResponse(ErrorResponse.InvalidRequest, TokenExpiredMessage)); + } + catch (UserTokenException) + { + return UnprocessableEntity(new ErrorResponse(ErrorResponse.InvalidRequest, TokenInvalidMessage)); + } + } + + [HttpPost("revoke")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)] + [Authorize] + public async Task RevokeAsync([FromBody] HttpVerifyOrRevokeTokenRequest body) + { + UserTokenInfo userTokenInfo; + try + { + userTokenInfo = await _userTokenService.ValidateTokenAsync(body.Token, false); + } + catch (UserTokenException) + { + return UnprocessableEntity(new ErrorResponse(ErrorResponse.InvalidRequest, TokenInvalidMessage)); + } + + if (userTokenInfo.UserId != GetAuthUserId()) + return UnprocessableEntity(new ErrorResponse(ErrorResponse.InvalidRequest, TokenInvalidMessage)); + + await _userTokenService.RevokeTokenAsync(body.Token); + + return NoContent(); + } + + [HttpPost("revokeall")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [Authorize] + public async Task RevokeAllAsync() + { + await _userTokenService.RevokeAllTokenByUserIdAsync(GetAuthUserId()); + return NoContent(); + } + } +} + -- cgit v1.2.3