aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author杨宇千 <crupest@outlook.com>2019-08-01 21:22:55 +0800
committer杨宇千 <crupest@outlook.com>2019-08-01 21:22:55 +0800
commitfded706989548a6f80aa7605ce70b7d20e49edb7 (patch)
tree234ec46171bb5d86d8ff569e69cdd41807df7ef7
parentc07297373df08bd605f5bec96020192bb6dec151 (diff)
downloadtimeline-fded706989548a6f80aa7605ce70b7d20e49edb7.tar.gz
timeline-fded706989548a6f80aa7605ce70b7d20e49edb7.tar.bz2
timeline-fded706989548a6f80aa7605ce70b7d20e49edb7.zip
Expired token now has a unique code.
-rw-r--r--Timeline.Tests/Helpers/TestUsers.cs6
-rw-r--r--Timeline/Authenticate/AuthHandler.cs4
-rw-r--r--Timeline/Controllers/TokenController.cs16
-rw-r--r--Timeline/Services/JwtService.cs60
-rw-r--r--Timeline/Services/UserService.cs3
5 files changed, 76 insertions, 13 deletions
diff --git a/Timeline.Tests/Helpers/TestUsers.cs b/Timeline.Tests/Helpers/TestUsers.cs
index dd00e38d..ceecc7c0 100644
--- a/Timeline.Tests/Helpers/TestUsers.cs
+++ b/Timeline.Tests/Helpers/TestUsers.cs
@@ -17,13 +17,15 @@ namespace Timeline.Tests.Helpers
{
Name = "user",
EncryptedPassword = passwordService.HashPassword("user"),
- RoleString = "user"
+ RoleString = "user",
+ Version = 0,
});
mockUsers.Add(new User
{
Name = "admin",
EncryptedPassword = passwordService.HashPassword("admin"),
- RoleString = "user,admin"
+ RoleString = "user,admin",
+ Version = 0,
});
MockUsers = mockUsers;
diff --git a/Timeline/Authenticate/AuthHandler.cs b/Timeline/Authenticate/AuthHandler.cs
index 75d3b49f..41cb11c6 100644
--- a/Timeline/Authenticate/AuthHandler.cs
+++ b/Timeline/Authenticate/AuthHandler.cs
@@ -87,6 +87,10 @@ namespace Timeline.Authenticate
return AuthenticateResult.Success(new AuthenticationTicket(principal, AuthConstants.Scheme));
}
+ catch (ArgumentException)
+ {
+ throw; // this exception usually means server error.
+ }
catch (Exception e)
{
_logger.LogInformation(e, "A jwt token validation failed.");
diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs
index 023bd53f..66c97b59 100644
--- a/Timeline/Controllers/TokenController.cs
+++ b/Timeline/Controllers/TokenController.cs
@@ -27,6 +27,7 @@ namespace Timeline.Controllers
public const int Verify_BadToken = -2001;
public const int Verify_UserNotExist = -2002;
public const int Verify_BadVersion = -2003;
+ public const int Verify_Expired = -2004;
}
private readonly IUserService _userService;
@@ -81,9 +82,18 @@ namespace Timeline.Controllers
}
catch (JwtTokenVerifyException e)
{
- var code = ErrorCodes.Verify_BadToken;
- _logger.LogInformation(LoggingEventIds.VerifyFailed, e, "Attemp to verify a bad token because of bad format. Code: {} Token: {}.", code, request.Token);
- return BadRequest(new CommonResponse(code, "A token of bad format."));
+ if (e.ErrorCode == JwtTokenVerifyException.ErrorCodes.Expired)
+ {
+ var code = ErrorCodes.Verify_Expired;
+ _logger.LogInformation(LoggingEventIds.VerifyFailed, e, "Attemp to verify a expired token. Code: {} Token: {}.", code, request.Token);
+ return BadRequest(new CommonResponse(code, "A expired token."));
+ }
+ else
+ {
+ var code = ErrorCodes.Verify_BadToken;
+ _logger.LogInformation(LoggingEventIds.VerifyFailed, e, "Attemp to verify a bad token because of bad format. Code: {} Token: {}.", code, request.Token);
+ return BadRequest(new CommonResponse(code, "A token of bad format."));
+ }
}
catch (UserNotExistException e)
{
diff --git a/Timeline/Services/JwtService.cs b/Timeline/Services/JwtService.cs
index e970bbd4..f3416cce 100644
--- a/Timeline/Services/JwtService.cs
+++ b/Timeline/Services/JwtService.cs
@@ -17,12 +17,52 @@ namespace Timeline.Services
[Serializable]
public class JwtTokenVerifyException : Exception
{
- public JwtTokenVerifyException() { }
- public JwtTokenVerifyException(string message) : base(message) { }
- public JwtTokenVerifyException(string message, Exception inner) : base(message, inner) { }
+ public static class ErrorCodes
+ {
+ // Codes in -1000 ~ -1999 usually means the user provides a token that is not created by this server.
+
+ public const int Others = -1001;
+ public const int NoIdClaim = -1002;
+ public const int IdClaimBadFormat = -1003;
+ public const int NoVersionClaim = -1004;
+ public const int VersionClaimBadFormat = -1005;
+
+ /// <summary>
+ /// Corresponds to <see cref="SecurityTokenExpiredException"/>.
+ /// </summary>
+ public const int Expired = -2001;
+ }
+
+ public JwtTokenVerifyException(int code) : base(GetErrorMessage(code)) { ErrorCode = code; }
+ public JwtTokenVerifyException(string message, int code) : base(message) { ErrorCode = code; }
+ public JwtTokenVerifyException(Exception inner, int code) : base(GetErrorMessage(code), inner) { ErrorCode = code; }
+ public JwtTokenVerifyException(string message, Exception inner, int code) : base(message, inner) { ErrorCode = code; }
protected JwtTokenVerifyException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
+
+ public int ErrorCode { get; private set; }
+
+ private static string GetErrorMessage(int errorCode)
+ {
+ switch (errorCode)
+ {
+ case ErrorCodes.Others:
+ return "Uncommon error, see inner exception for more information.";
+ case ErrorCodes.NoIdClaim:
+ return "Id claim does not exist.";
+ case ErrorCodes.IdClaimBadFormat:
+ return "Id claim is not a number.";
+ case ErrorCodes.NoVersionClaim:
+ return "Version claim does not exist.";
+ case ErrorCodes.VersionClaimBadFormat:
+ return "Version claim is not a number";
+ case ErrorCodes.Expired:
+ return "Token is expired.";
+ default:
+ return "Unknown error code.";
+ }
+ }
}
public interface IJwtService
@@ -110,15 +150,15 @@ namespace Timeline.Services
var idClaim = principal.FindFirstValue(ClaimTypes.NameIdentifier);
if (idClaim == null)
- throw new JwtTokenVerifyException("Id claim does not exist.");
+ throw new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.NoIdClaim);
if (!long.TryParse(idClaim, out var id))
- throw new JwtTokenVerifyException("Can't convert id claim into a integer number.");
+ throw new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.IdClaimBadFormat);
var versionClaim = principal.FindFirstValue(VersionClaimType);
if (versionClaim == null)
- throw new JwtTokenVerifyException("Version claim does not exist.");
+ throw new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.NoVersionClaim);
if (!long.TryParse(versionClaim, out var version))
- throw new JwtTokenVerifyException("Can't convert version claim into a integer number.");
+ throw new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.VersionClaimBadFormat);
return new TokenInfo
{
@@ -126,9 +166,13 @@ namespace Timeline.Services
Version = version
};
}
+ catch (SecurityTokenExpiredException e)
+ {
+ throw new JwtTokenVerifyException(e, JwtTokenVerifyException.ErrorCodes.Expired);
+ }
catch (Exception e)
{
- throw new JwtTokenVerifyException("Validate token failed caused by a SecurityTokenException. See inner exception.", e);
+ throw new JwtTokenVerifyException(e, JwtTokenVerifyException.ErrorCodes.Others);
}
}
}
diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs
index c63ded1e..3164a645 100644
--- a/Timeline/Services/UserService.cs
+++ b/Timeline/Services/UserService.cs
@@ -208,6 +208,9 @@ namespace Timeline.Services
public async Task<UserInfo> VerifyToken(string token)
{
+ if (token == null)
+ throw new ArgumentNullException(nameof(token));
+
TokenInfo tokenInfo;
try
{