aboutsummaryrefslogtreecommitdiff
path: root/BackEnd/Timeline/Controllers/V2/TokenV2Controller.cs
blob: b129758a328264a5ffad0ddeb154912ac0423bd5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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.";

        /// <summary>
        /// Create a new token for a user.
        /// </summary>
        /// <returns>Result of token creation.</returns>
        [HttpPost("create")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
        public async Task<ActionResult<HttpCreateTokenResponse>> 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<HttpUser>(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.";

        /// <summary>
        /// Verify a token.
        /// </summary>
        /// <returns>Result of token verification.</returns>
        [HttpPost("verify")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
        public async Task<ActionResult<HttpVerifyTokenResponseV2>> 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<HttpUser>(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<ActionResult> 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<ActionResult> RevokeAllAsync()
        {
            await _userTokenService.RevokeAllTokenByUserIdAsync(GetAuthUserId());
            return NoContent();
        }
    }
}