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(); } } }