aboutsummaryrefslogtreecommitdiff
path: root/BackEnd/Timeline/Services
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2022-03-23 21:30:14 +0800
committercrupest <crupest@outlook.com>2022-03-23 21:30:31 +0800
commitda9139b7bab95f6e5ba5f4bb2d99011c2d6db03a (patch)
tree051fd4ca4bc511db7e04b019a33fddaab2d0cc6b /BackEnd/Timeline/Services
parent3d6c9fd916e18c99b3a5497b8313672680571b5e (diff)
downloadtimeline-da9139b7bab95f6e5ba5f4bb2d99011c2d6db03a.tar.gz
timeline-da9139b7bab95f6e5ba5f4bb2d99011c2d6db03a.tar.bz2
timeline-da9139b7bab95f6e5ba5f4bb2d99011c2d6db03a.zip
Diffstat (limited to 'BackEnd/Timeline/Services')
-rw-r--r--BackEnd/Timeline/Services/Token/DatabaseUserTokenHandler.cs0
-rw-r--r--BackEnd/Timeline/Services/Token/IUserTokenHandler.cs29
-rw-r--r--BackEnd/Timeline/Services/Token/IUserTokenManager.cs35
-rw-r--r--BackEnd/Timeline/Services/Token/IUserTokenService.cs45
-rw-r--r--BackEnd/Timeline/Services/Token/JwtUserTokenBadFormatException.cs47
-rw-r--r--BackEnd/Timeline/Services/Token/Resource.Designer.cs276
-rw-r--r--BackEnd/Timeline/Services/Token/Resource.resx165
-rw-r--r--BackEnd/Timeline/Services/Token/SecureRandomUserTokenService.cs125
-rw-r--r--BackEnd/Timeline/Services/Token/TokenServicesServiceColletionExtensions.cs4
-rw-r--r--BackEnd/Timeline/Services/Token/UserTokenBadFormatException.cs17
-rw-r--r--BackEnd/Timeline/Services/Token/UserTokenExpiredException.cs21
-rw-r--r--BackEnd/Timeline/Services/Token/UserTokenHandler.cs117
-rw-r--r--BackEnd/Timeline/Services/Token/UserTokenInfo.cs6
-rw-r--r--BackEnd/Timeline/Services/Token/UserTokenManager.cs102
-rw-r--r--BackEnd/Timeline/Services/Token/UserTokenTimeExpiredException.cs21
-rw-r--r--BackEnd/Timeline/Services/Token/UserTokenUserNotExistException.cs16
-rw-r--r--BackEnd/Timeline/Services/Token/UserTokenVersionExpiredException.cs21
-rw-r--r--BackEnd/Timeline/Services/User/CreateTokenResult.cs12
-rw-r--r--BackEnd/Timeline/Services/User/UserService.cs15
19 files changed, 361 insertions, 713 deletions
diff --git a/BackEnd/Timeline/Services/Token/DatabaseUserTokenHandler.cs b/BackEnd/Timeline/Services/Token/DatabaseUserTokenHandler.cs
deleted file mode 100644
index e69de29b..00000000
--- a/BackEnd/Timeline/Services/Token/DatabaseUserTokenHandler.cs
+++ /dev/null
diff --git a/BackEnd/Timeline/Services/Token/IUserTokenHandler.cs b/BackEnd/Timeline/Services/Token/IUserTokenHandler.cs
deleted file mode 100644
index 62e01de5..00000000
--- a/BackEnd/Timeline/Services/Token/IUserTokenHandler.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Threading.Tasks;
-
-namespace Timeline.Services.Token
-{
- public interface IUserTokenHandler
- {
- /// <summary>
- /// Create a token for a given token info.
- /// </summary>
- /// <param name="tokenInfo">The info to generate token.</param>
- /// <returns>Return the generated token.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="tokenInfo"/> is null.</exception>
- Task<string> GenerateTokenAsync(UserTokenInfo tokenInfo);
-
- /// <summary>
- /// Verify a token and get the saved info. Do not validate lifetime!!!
- /// </summary>
- /// <param name="token">The token to verify.</param>
- /// <returns>The saved info in token.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="token"/> is null.</exception>
- /// <exception cref="UserTokenBadFormatException">Thrown when the token is of bad format.</exception>
- /// <remarks>
- /// If this method throw <see cref="UserTokenBadFormatException"/>, it usually means the token is not created by this service.
- /// Do not check expire time in this method, only check whether it is present.
- /// </remarks>
- Task<UserTokenInfo> ValidateTokenAsync(string token);
- }
-}
diff --git a/BackEnd/Timeline/Services/Token/IUserTokenManager.cs b/BackEnd/Timeline/Services/Token/IUserTokenManager.cs
deleted file mode 100644
index 39009d69..00000000
--- a/BackEnd/Timeline/Services/Token/IUserTokenManager.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Timeline.Entities;
-using Timeline.Services.User;
-
-namespace Timeline.Services.Token
-{
- public interface IUserTokenManager
- {
- /// <summary>
- /// Try to create a token for given username and password.
- /// </summary>
- /// <param name="username">The username.</param>
- /// <param name="password">The password.</param>
- /// <param name="expireAt">The expire time of the token.</param>
- /// <returns>The created token and the user info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> or <paramref name="password"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
- /// <exception cref="EntityNotExistException">Thrown when the user with <paramref name="username"/> does not exist.</exception>
- /// <exception cref="BadPasswordException">Thrown when <paramref name="password"/> is wrong.</exception>
- public Task<UserTokenCreateResult> CreateTokenAsync(string username, string password, DateTime? expireAt = null);
-
- /// <summary>
- /// Verify a token and get the saved user info. This also check the database for existence of the user.
- /// </summary>
- /// <param name="token">The token.</param>
- /// <returns>The user stored in token.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="token"/> is null.</exception>
- /// <exception cref="UserTokenTimeExpiredException">Thrown when the token is expired.</exception>
- /// <exception cref="UserTokenVersionExpiredException">Thrown when the token is of bad version.</exception>
- /// <exception cref="UserTokenBadFormatException">Thrown when the token is of bad format.</exception>
- /// <exception cref="UserTokenUserNotExistException">Thrown when the user specified by the token does not exist. Usually the user had been deleted after the token was issued.</exception>
- public Task<UserEntity> VerifyTokenAsync(string token);
- }
-}
diff --git a/BackEnd/Timeline/Services/Token/IUserTokenService.cs b/BackEnd/Timeline/Services/Token/IUserTokenService.cs
new file mode 100644
index 00000000..22fb0fb4
--- /dev/null
+++ b/BackEnd/Timeline/Services/Token/IUserTokenService.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Timeline.Services.Token
+{
+ public interface IUserTokenService
+ {
+ /// <summary>
+ /// Create a token for a user. Please ensure the user id exists!
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <param name="expireTime">The expire time of the token.</param>
+ /// <returns>Return the generated token.</returns>
+ Task<string> CreateTokenAsync(long userId, DateTime? expireTime);
+
+ /// <summary>
+ /// Verify a token and get the info of the token.
+ /// </summary>
+ /// <param name="token">The token to verify.</param>
+ /// <returns>The info of the token.</returns>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="token"/> is null.</exception>
+ /// <exception cref="UserTokenException">Thrown when the token is not valid for reasons other than expired.</exception>
+ /// <exception cref="UserTokenExpiredException">Thrown when the token is expired.</exception>
+ Task<UserTokenInfo> ValidateTokenAsync(string token);
+
+ /// <summary>
+ /// Revoke a token to make it no longer valid.
+ /// </summary>
+ /// <param name="token">The token to revoke.</param>
+ /// <returns>Return true if a token is revoked.</returns>
+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="token"/> is null.</exception>
+ /// <remarks>
+ /// This method returns true if a real token is revoked and returns false if the token is not valid.
+ /// If the token is expired, false is return.
+ /// </remarks>
+ Task<bool> RevokeTokenAsync(string token);
+
+ /// <summary>
+ /// Revoke all tokens of a user.
+ /// </summary>
+ /// <param name="userId">User id of tokens.</param>
+ /// <returns>Return the task.</returns>
+ Task RevokeAllTokenByUserIdAsync(long userId);
+ }
+}
diff --git a/BackEnd/Timeline/Services/Token/JwtUserTokenBadFormatException.cs b/BackEnd/Timeline/Services/Token/JwtUserTokenBadFormatException.cs
deleted file mode 100644
index 7d272170..00000000
--- a/BackEnd/Timeline/Services/Token/JwtUserTokenBadFormatException.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System;
-using System.Globalization;
-
-namespace Timeline.Services.Token
-{
- [Serializable]
- public class JwtUserTokenBadFormatException : UserTokenBadFormatException
- {
- public enum ErrorKind
- {
- NoIdClaim,
- IdClaimBadFormat,
- NoVersionClaim,
- VersionClaimBadFormat,
- NoExp,
- Other
- }
-
- public JwtUserTokenBadFormatException() : this("", ErrorKind.Other) { }
- public JwtUserTokenBadFormatException(string message) : base(message) { }
- public JwtUserTokenBadFormatException(string message, Exception inner) : base(message, inner) { }
-
- public JwtUserTokenBadFormatException(string token, ErrorKind type) : base(token, GetErrorMessage(type)) { ErrorType = type; }
- public JwtUserTokenBadFormatException(string token, ErrorKind type, Exception inner) : base(token, GetErrorMessage(type), inner) { ErrorType = type; }
- public JwtUserTokenBadFormatException(string token, ErrorKind type, string message, Exception inner) : base(token, message, inner) { ErrorType = type; }
- protected JwtUserTokenBadFormatException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public ErrorKind ErrorType { get; set; }
-
- private static string GetErrorMessage(ErrorKind type)
- {
- var reason = type switch
- {
- ErrorKind.NoIdClaim => Resource.ExceptionJwtUserTokenBadFormatReasonIdMissing,
- ErrorKind.IdClaimBadFormat => Resource.ExceptionJwtUserTokenBadFormatReasonIdBadFormat,
- ErrorKind.NoVersionClaim => Resource.ExceptionJwtUserTokenBadFormatReasonVersionMissing,
- ErrorKind.VersionClaimBadFormat => Resource.ExceptionJwtUserTokenBadFormatReasonVersionBadFormat,
- ErrorKind.Other => Resource.ExceptionJwtUserTokenBadFormatReasonOthers,
- _ => Resource.ExceptionJwtUserTokenBadFormatReasonUnknown
- };
-
- return string.Format(CultureInfo.CurrentCulture, Resource.ExceptionJwtUserTokenBadFormat, reason);
- }
- }
-}
diff --git a/BackEnd/Timeline/Services/Token/Resource.Designer.cs b/BackEnd/Timeline/Services/Token/Resource.Designer.cs
index ac6f3707..c1bd30ef 100644
--- a/BackEnd/Timeline/Services/Token/Resource.Designer.cs
+++ b/BackEnd/Timeline/Services/Token/Resource.Designer.cs
@@ -1,198 +1,78 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Services.Token {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resource {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resource() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Services.Token.Resource", typeof(Resource).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Jwt key is not found. Maybe you forget to do the migration..
- /// </summary>
- internal static string ExceptionJwtKeyNotExist {
- get {
- return ResourceManager.GetString("ExceptionJwtKeyNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token didn&apos;t pass verification because {0}..
- /// </summary>
- internal static string ExceptionJwtUserTokenBadFormat {
- get {
- return ResourceManager.GetString("ExceptionJwtUserTokenBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to id claim is not a number.
- /// </summary>
- internal static string ExceptionJwtUserTokenBadFormatReasonIdBadFormat {
- get {
- return ResourceManager.GetString("ExceptionJwtUserTokenBadFormatReasonIdBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to id claim does not exist.
- /// </summary>
- internal static string ExceptionJwtUserTokenBadFormatReasonIdMissing {
- get {
- return ResourceManager.GetString("ExceptionJwtUserTokenBadFormatReasonIdMissing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to other error, see inner exception for information.
- /// </summary>
- internal static string ExceptionJwtUserTokenBadFormatReasonOthers {
- get {
- return ResourceManager.GetString("ExceptionJwtUserTokenBadFormatReasonOthers", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to unknown error.
- /// </summary>
- internal static string ExceptionJwtUserTokenBadFormatReasonUnknown {
- get {
- return ResourceManager.GetString("ExceptionJwtUserTokenBadFormatReasonUnknown", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to version claim is not a number..
- /// </summary>
- internal static string ExceptionJwtUserTokenBadFormatReasonVersionBadFormat {
- get {
- return ResourceManager.GetString("ExceptionJwtUserTokenBadFormatReasonVersionBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to version claim does not exist..
- /// </summary>
- internal static string ExceptionJwtUserTokenBadFormatReasonVersionMissing {
- get {
- return ResourceManager.GetString("ExceptionJwtUserTokenBadFormatReasonVersionMissing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is of bad format, which means it may not be created by the server..
- /// </summary>
- internal static string ExceptionUserTokenBadFormat {
- get {
- return ResourceManager.GetString("ExceptionUserTokenBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is expired because its expiration time has passed..
- /// </summary>
- internal static string ExceptionUserTokenTimeExpired {
- get {
- return ResourceManager.GetString("ExceptionUserTokenTimeExpired", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The owner user of the token does not exist..
- /// </summary>
- internal static string ExceptionUserTokenUserNotExist {
- get {
- return ResourceManager.GetString("ExceptionUserTokenUserNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is of bad version..
- /// </summary>
- internal static string ExceptionUserTokenVersionExpired {
- get {
- return ResourceManager.GetString("ExceptionUserTokenVersionExpired", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A token is created for user with username={0}, id={1}..
- /// </summary>
- internal static string LogTokenCreate {
- get {
- return ResourceManager.GetString("LogTokenCreate", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A token of user with username = {0}, id = {1} is verified successfully..
- /// </summary>
- internal static string LogTokenVerified {
- get {
- return ResourceManager.GetString("LogTokenVerified", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A token fails to be verified..
- /// </summary>
- internal static string LogTokenVerifiedFail {
- get {
- return ResourceManager.GetString("LogTokenVerifiedFail", resourceCulture);
- }
- }
- }
-}
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Timeline.Services.Token {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// This class was generated by MSBuild using the GenerateResource task.
+ /// To add or remove a member, edit your .resx file then rerun MSBuild.
+ /// </summary>
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Build.Tasks.StronglyTypedResourceBuilder", "15.1.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resource {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resource() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Services.Token.Resource", typeof(Resource).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The token is expired because its expiration time has passed..
+ /// </summary>
+ internal static string ExceptionUserTokenExpired {
+ get {
+ return ResourceManager.GetString("ExceptionUserTokenExpired", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The token is invalid..
+ /// </summary>
+ internal static string ExceptionUserTokenInvalid {
+ get {
+ return ResourceManager.GetString("ExceptionUserTokenInvalid", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Services/Token/Resource.resx b/BackEnd/Timeline/Services/Token/Resource.resx
index 06bf03f6..9ea2e63a 100644
--- a/BackEnd/Timeline/Services/Token/Resource.resx
+++ b/BackEnd/Timeline/Services/Token/Resource.resx
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
- <!--
+ <!--
Microsoft ResX Schema
Version 2.0
@@ -59,107 +59,68 @@
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
+ <xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string"/>
+ <xsd:attribute name="type" type="xsd:string"/>
+ <xsd:attribute name="mimetype" type="xsd:string"/>
+ <xsd:attribute ref="xml:space"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string"/>
+ <xsd:attribute name="name" type="xsd:string"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
+ <xsd:attribute ref="xml:space"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
</xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionJwtKeyNotExist" xml:space="preserve">
- <value>Jwt key is not found. Maybe you forget to do the migration.</value>
- </data>
- <data name="ExceptionJwtUserTokenBadFormat" xml:space="preserve">
- <value>The token didn't pass verification because {0}.</value>
- </data>
- <data name="ExceptionJwtUserTokenBadFormatReasonIdBadFormat" xml:space="preserve">
- <value>id claim is not a number</value>
- </data>
- <data name="ExceptionJwtUserTokenBadFormatReasonIdMissing" xml:space="preserve">
- <value>id claim does not exist</value>
- </data>
- <data name="ExceptionJwtUserTokenBadFormatReasonOthers" xml:space="preserve">
- <value>other error, see inner exception for information</value>
- </data>
- <data name="ExceptionJwtUserTokenBadFormatReasonUnknown" xml:space="preserve">
- <value>unknown error</value>
- </data>
- <data name="ExceptionJwtUserTokenBadFormatReasonVersionBadFormat" xml:space="preserve">
- <value>version claim is not a number.</value>
- </data>
- <data name="ExceptionJwtUserTokenBadFormatReasonVersionMissing" xml:space="preserve">
- <value>version claim does not exist.</value>
- </data>
- <data name="ExceptionUserTokenBadFormat" xml:space="preserve">
- <value>The token is of bad format, which means it may not be created by the server.</value>
- </data>
- <data name="ExceptionUserTokenTimeExpired" xml:space="preserve">
- <value>The token is expired because its expiration time has passed.</value>
- </data>
- <data name="ExceptionUserTokenUserNotExist" xml:space="preserve">
- <value>The owner user of the token does not exist.</value>
- </data>
- <data name="ExceptionUserTokenVersionExpired" xml:space="preserve">
- <value>The token is of bad version.</value>
- </data>
- <data name="LogTokenCreate" xml:space="preserve">
- <value>A token is created for user with username={0}, id={1}.</value>
- </data>
- <data name="LogTokenVerified" xml:space="preserve">
- <value>A token of user with username = {0}, id = {1} is verified successfully.</value>
- </data>
- <data name="LogTokenVerifiedFail" xml:space="preserve">
- <value>A token fails to be verified.</value>
- </data>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="ExceptionUserTokenInvalid" xml:space="preserve">
+ <value>The token is invalid.</value>
+ </data>
+ <data name="ExceptionUserTokenExpired" xml:space="preserve">
+ <value>The token is expired because its expiration time has passed.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/BackEnd/Timeline/Services/Token/SecureRandomUserTokenService.cs b/BackEnd/Timeline/Services/Token/SecureRandomUserTokenService.cs
new file mode 100644
index 00000000..404862d4
--- /dev/null
+++ b/BackEnd/Timeline/Services/Token/SecureRandomUserTokenService.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Timeline.Configs;
+using Timeline.Entities;
+
+namespace Timeline.Services.Token
+{
+ public class SecureRandomUserTokenService : IUserTokenService, IDisposable
+ {
+ private DatabaseContext _databaseContext;
+ private ILogger<SecureRandomUserTokenService> _logger;
+ private RandomNumberGenerator _secureRandom;
+ private IOptionsMonitor<TokenOptions> _optionMonitor;
+ private IClock _clock;
+
+ public SecureRandomUserTokenService(DatabaseContext databaseContext, ILogger<SecureRandomUserTokenService> logger, IOptionsMonitor<TokenOptions> optionMonitor, IClock clock)
+ {
+ _databaseContext = databaseContext;
+ _logger = logger;
+ _secureRandom = RandomNumberGenerator.Create();
+ _optionMonitor = optionMonitor;
+ _clock = clock;
+ }
+
+ public void Dispose()
+ {
+ _secureRandom.Dispose();
+ }
+
+ private string GenerateSecureRandomTokenString()
+ {
+ var option = _optionMonitor.CurrentValue;
+ var tokenLength = option.TokenLength ?? 32;
+ var buffer = new byte[tokenLength];
+ _secureRandom.GetBytes(buffer);
+ return Convert.ToHexString(buffer);
+ }
+
+ /// <inheritdoc/>
+ public async Task<string> CreateTokenAsync(long userId, DateTime? expireTime)
+ {
+ var currentTime = _clock.GetCurrentTime();
+
+ if (expireTime is not null && expireTime > currentTime)
+ {
+ _logger.LogWarning("The expire time of the token has already passed.");
+ }
+
+ UserTokenEntity entity = new UserTokenEntity
+ {
+ UserId = userId,
+ Token = GenerateSecureRandomTokenString(),
+ ExpireAt = expireTime,
+ CreateAt = currentTime,
+ Deleted = false
+ };
+
+ _databaseContext.UserTokens.Add(entity);
+ await _databaseContext.SaveChangesAsync();
+
+ _logger.LogInformation("A user token is created with user id {}.", userId);
+
+ return entity.Token;
+ }
+
+ /// <inheritdoc/>
+ public async Task<UserTokenInfo> ValidateTokenAsync(string token)
+ {
+ var entity = await _databaseContext.UserTokens.Where(t => t.Token == token && !t.Deleted).SingleOrDefaultAsync();
+
+ if (entity is null)
+ {
+ throw new UserTokenException(token, Resource.ExceptionUserTokenInvalid);
+ }
+
+ var currentTime = _clock.GetCurrentTime();
+
+ if (entity.ExpireAt.HasValue && entity.ExpireAt > currentTime)
+ {
+ throw new UserTokenExpiredException(token, entity.ExpireAt.Value, currentTime);
+ }
+
+ return new UserTokenInfo()
+ {
+ UserId = entity.UserId,
+ ExpireAt = entity.ExpireAt,
+ CreateAt = entity.CreateAt
+ };
+ }
+
+ /// <inheritdoc/>
+ public async Task<bool> RevokeTokenAsync(string token)
+ {
+ var entity = await _databaseContext.UserTokens.Where(t => t.Token == token && t.Deleted == false).SingleOrDefaultAsync();
+ if (entity is not null)
+ {
+ entity.Deleted = true;
+ await _databaseContext.SaveChangesAsync();
+
+ _logger.LogInformation("A token is revoked with user id {}.", entity.UserId);
+
+ return entity.ExpireAt <= _clock.GetCurrentTime();
+ }
+ return false;
+ }
+
+ /// <inheritdoc/>
+ public async Task RevokeAllTokenByUserIdAsync(long userId)
+ {
+ List<UserTokenEntity> entities = await _databaseContext.UserTokens.Where(t => t.UserId == userId && t.Deleted == false).ToListAsync();
+ foreach (var entity in entities)
+ {
+ entity.Deleted = true;
+ }
+ await _databaseContext.SaveChangesAsync();
+ _logger.LogInformation("All tokens of user with id {} are revoked.", userId);
+ }
+ }
+} \ No newline at end of file
diff --git a/BackEnd/Timeline/Services/Token/TokenServicesServiceColletionExtensions.cs b/BackEnd/Timeline/Services/Token/TokenServicesServiceColletionExtensions.cs
index 1ad84311..cf4eeb11 100644
--- a/BackEnd/Timeline/Services/Token/TokenServicesServiceColletionExtensions.cs
+++ b/BackEnd/Timeline/Services/Token/TokenServicesServiceColletionExtensions.cs
@@ -9,9 +9,7 @@ namespace Timeline.Services.Token
public static IServiceCollection AddTokenServices(this IServiceCollection services, IConfiguration configuration)
{
services.Configure<TokenOptions>(configuration.GetSection("Token"));
- services.Configure<JwtOptions>(configuration.GetSection("Jwt"));
- services.AddScoped<IUserTokenHandler, JwtUserTokenHandler>();
- services.AddScoped<IUserTokenManager, UserTokenManager>();
+ services.AddScoped<IUserTokenService, SecureRandomUserTokenService>();
return services;
}
}
diff --git a/BackEnd/Timeline/Services/Token/UserTokenBadFormatException.cs b/BackEnd/Timeline/Services/Token/UserTokenBadFormatException.cs
deleted file mode 100644
index 39ed1be4..00000000
--- a/BackEnd/Timeline/Services/Token/UserTokenBadFormatException.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-
-namespace Timeline.Services.Token
-{
- [Serializable]
- public class UserTokenBadFormatException : UserTokenException
- {
- public UserTokenBadFormatException() : base(Resource.ExceptionUserTokenBadFormat) { }
- public UserTokenBadFormatException(string token) : base(token, Resource.ExceptionUserTokenBadFormat) { }
- public UserTokenBadFormatException(string token, string message) : base(token, message) { }
- public UserTokenBadFormatException(string token, Exception inner) : base(token, Resource.ExceptionUserTokenBadFormat, inner) { }
- public UserTokenBadFormatException(string token, string message, Exception inner) : base(token, message, inner) { }
- protected UserTokenBadFormatException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
- }
-}
diff --git a/BackEnd/Timeline/Services/Token/UserTokenExpiredException.cs b/BackEnd/Timeline/Services/Token/UserTokenExpiredException.cs
new file mode 100644
index 00000000..5e91ca6c
--- /dev/null
+++ b/BackEnd/Timeline/Services/Token/UserTokenExpiredException.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Timeline.Services.Token
+{
+ [Serializable]
+ public class UserTokenExpiredException : UserTokenException
+ {
+ public UserTokenExpiredException() : base(Resource.ExceptionUserTokenExpired) { }
+ public UserTokenExpiredException(string message) : base(message) { }
+ public UserTokenExpiredException(string message, Exception inner) : base(message, inner) { }
+ public UserTokenExpiredException(string token, DateTime expireTime, DateTime verifyTime) : base(token, Resource.ExceptionUserTokenExpired) { ExpireTime = expireTime; VerifyTime = verifyTime; }
+ public UserTokenExpiredException(string token, DateTime expireTime, DateTime verifyTime, Exception inner) : base(token, Resource.ExceptionUserTokenExpired, inner) { ExpireTime = expireTime; VerifyTime = verifyTime; }
+ protected UserTokenExpiredException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
+
+ public DateTime ExpireTime { get; private set; }
+
+ public DateTime VerifyTime { get; private set; }
+ }
+}
diff --git a/BackEnd/Timeline/Services/Token/UserTokenHandler.cs b/BackEnd/Timeline/Services/Token/UserTokenHandler.cs
deleted file mode 100644
index 03b07b53..00000000
--- a/BackEnd/Timeline/Services/Token/UserTokenHandler.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using Microsoft.Extensions.Options;
-using Microsoft.IdentityModel.Tokens;
-using System;
-using System.Globalization;
-using System.IdentityModel.Tokens.Jwt;
-using System.Linq;
-using System.Security.Claims;
-using System.Threading.Tasks;
-using Timeline.Configs;
-using Timeline.Entities;
-
-namespace Timeline.Services.Token
-{
- public class JwtUserTokenHandler : IUserTokenHandler
- {
- private const string VersionClaimType = "timeline_version";
-
- private readonly IOptionsMonitor<JwtOptions> _jwtConfig;
- private readonly IClock _clock;
-
- private readonly JwtSecurityTokenHandler _tokenHandler = new JwtSecurityTokenHandler();
- private SymmetricSecurityKey _tokenSecurityKey;
-
- public JwtUserTokenHandler(IOptionsMonitor<JwtOptions> jwtConfig, IClock clock, DatabaseContext database)
- {
- _jwtConfig = jwtConfig;
- _clock = clock;
-
- var key = database.JwtToken.Select(t => t.Key).SingleOrDefault();
-
- if (key == null)
- {
- throw new InvalidOperationException(Resource.ExceptionJwtKeyNotExist);
- }
-
- _tokenSecurityKey = new SymmetricSecurityKey(key);
- }
-
- public Task<string> GenerateTokenAsync(UserTokenInfo tokenInfo)
- {
- if (tokenInfo == null)
- throw new ArgumentNullException(nameof(tokenInfo));
-
- var config = _jwtConfig.CurrentValue;
-
- var identity = new ClaimsIdentity();
- identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, tokenInfo.Id.ToString(CultureInfo.InvariantCulture.NumberFormat), ClaimValueTypes.Integer64));
- identity.AddClaim(new Claim(VersionClaimType, tokenInfo.Version.ToString(CultureInfo.InvariantCulture.NumberFormat), ClaimValueTypes.Integer64));
-
- var tokenDescriptor = new SecurityTokenDescriptor()
- {
- Subject = identity,
- Issuer = config.Issuer,
- Audience = config.Audience,
- SigningCredentials = new SigningCredentials(_tokenSecurityKey, SecurityAlgorithms.HmacSha384),
- IssuedAt = _clock.GetCurrentTime(),
- Expires = tokenInfo.ExpireAt,
- NotBefore = _clock.GetCurrentTime() // I must explicitly set this or it will use the current time by default and mock is not work in which case test will not pass.
- };
-
- var token = _tokenHandler.CreateToken(tokenDescriptor);
- var tokenString = _tokenHandler.WriteToken(token);
-
- return Task.FromResult(tokenString);
- }
-
-
- public Task<UserTokenInfo> ValidateTokenAsync(string token)
- {
- if (token == null)
- throw new ArgumentNullException(nameof(token));
-
- var config = _jwtConfig.CurrentValue;
- try
- {
- var principal = _tokenHandler.ValidateToken(token, new TokenValidationParameters
- {
- ValidateIssuer = true,
- ValidateAudience = true,
- ValidateIssuerSigningKey = true,
- ValidateLifetime = false,
- ValidIssuer = config.Issuer,
- ValidAudience = config.Audience,
- IssuerSigningKey = _tokenSecurityKey
- }, out var t);
-
- var idClaim = principal.FindFirstValue(ClaimTypes.NameIdentifier);
- if (idClaim == null)
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.NoIdClaim);
- if (!long.TryParse(idClaim, out var id))
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.IdClaimBadFormat);
-
- var versionClaim = principal.FindFirstValue(VersionClaimType);
- if (versionClaim == null)
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.NoVersionClaim);
- if (!long.TryParse(versionClaim, out var version))
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.VersionClaimBadFormat);
-
- var decodedToken = (JwtSecurityToken)t;
- var exp = decodedToken.Payload.Exp;
- if (exp is null)
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.NoExp);
-
- return Task.FromResult(new UserTokenInfo
- {
- Id = id,
- Version = version,
- ExpireAt = EpochTime.DateTime(exp.Value)
- });
- }
- catch (Exception e) when (e is SecurityTokenException || e is ArgumentException)
- {
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.Other, e);
- }
- }
- }
-}
diff --git a/BackEnd/Timeline/Services/Token/UserTokenInfo.cs b/BackEnd/Timeline/Services/Token/UserTokenInfo.cs
index 547f5ba6..b1a386d1 100644
--- a/BackEnd/Timeline/Services/Token/UserTokenInfo.cs
+++ b/BackEnd/Timeline/Services/Token/UserTokenInfo.cs
@@ -4,8 +4,8 @@ namespace Timeline.Services.Token
{
public class UserTokenInfo
{
- public long Id { get; set; }
- public long Version { get; set; }
- public DateTime ExpireAt { get; set; }
+ public long UserId { get; set; }
+ public DateTime? ExpireAt { get; set; }
+ public DateTime? CreateAt { get; set; }
}
}
diff --git a/BackEnd/Timeline/Services/Token/UserTokenManager.cs b/BackEnd/Timeline/Services/Token/UserTokenManager.cs
deleted file mode 100644
index bdb229f0..00000000
--- a/BackEnd/Timeline/Services/Token/UserTokenManager.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using System;
-using System.Threading.Tasks;
-using Timeline.Configs;
-using Timeline.Entities;
-using Timeline.Helpers;
-using Timeline.Services.User;
-
-namespace Timeline.Services.Token
-{
- public class UserTokenManager : IUserTokenManager
- {
- private readonly ILogger<UserTokenManager> _logger;
- private readonly IOptionsMonitor<TokenOptions> _tokenOptionsMonitor;
- private readonly IUserService _userService;
- private readonly IUserTokenHandler _userTokenService;
- private readonly IClock _clock;
-
- public UserTokenManager(ILogger<UserTokenManager> logger, IOptionsMonitor<TokenOptions> tokenOptionsMonitor, IUserService userService, IUserTokenHandler userTokenService, IClock clock)
- {
- _logger = logger;
- _tokenOptionsMonitor = tokenOptionsMonitor;
- _userService = userService;
- _userTokenService = userTokenService;
- _clock = clock;
- }
-
- public async Task<UserTokenCreateResult> CreateTokenAsync(string username, string password, DateTime? expireAt = null)
- {
- expireAt = expireAt?.MyToUtc();
-
- if (username == null)
- throw new ArgumentNullException(nameof(username));
- if (password == null)
- throw new ArgumentNullException(nameof(password));
-
- var userId = await _userService.VerifyCredential(username, password);
- var user = await _userService.GetUserAsync(userId);
-
- var token = await _userTokenService.GenerateTokenAsync(new UserTokenInfo
- {
- Id = user.Id,
- Version = user.Version,
- ExpireAt = expireAt ?? _clock.GetCurrentTime() + TimeSpan.FromSeconds(_tokenOptionsMonitor.CurrentValue.DefaultExpireSeconds)
- });
-
- _logger.LogInformation(Resource.LogTokenCreate, user.Username, userId);
-
- return new UserTokenCreateResult { Token = token, User = user };
- }
-
-
- public async Task<UserEntity> VerifyTokenAsync(string token)
- {
- if (token == null)
- throw new ArgumentNullException(nameof(token));
-
- UserTokenInfo tokenInfo;
-
- try
- {
- tokenInfo = await _userTokenService.ValidateTokenAsync(token);
- }
- catch (UserTokenBadFormatException e)
- {
- _logger.LogInformation(e, Resource.LogTokenVerifiedFail);
- throw;
- }
-
- var currentTime = _clock.GetCurrentTime();
- if (tokenInfo.ExpireAt < currentTime)
- {
- var e = new UserTokenTimeExpiredException(token, tokenInfo.ExpireAt, currentTime);
- _logger.LogInformation(e, Resource.LogTokenVerifiedFail);
- throw e;
- }
-
- try
- {
- var user = await _userService.GetUserAsync(tokenInfo.Id);
-
- if (tokenInfo.Version < user.Version)
- {
- var e = new UserTokenVersionExpiredException(token, tokenInfo.Version, user.Version);
- _logger.LogInformation(e, Resource.LogTokenVerifiedFail);
- throw e;
- }
-
- _logger.LogInformation(Resource.LogTokenVerified, user.Username, user.Id);
-
- return user;
- }
- catch (EntityNotExistException e)
- {
- var exception = new UserTokenUserNotExistException(token, e);
- _logger.LogInformation(exception, Resource.LogTokenVerifiedFail);
- throw exception;
- }
- }
- }
-}
diff --git a/BackEnd/Timeline/Services/Token/UserTokenTimeExpiredException.cs b/BackEnd/Timeline/Services/Token/UserTokenTimeExpiredException.cs
deleted file mode 100644
index 6e33ab4d..00000000
--- a/BackEnd/Timeline/Services/Token/UserTokenTimeExpiredException.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-
-namespace Timeline.Services.Token
-{
- [Serializable]
- public class UserTokenTimeExpiredException : UserTokenException
- {
- public UserTokenTimeExpiredException() : base(Resource.ExceptionUserTokenTimeExpired) { }
- public UserTokenTimeExpiredException(string message) : base(message) { }
- public UserTokenTimeExpiredException(string message, Exception inner) : base(message, inner) { }
- public UserTokenTimeExpiredException(string token, DateTime expireTime, DateTime verifyTime) : base(token, Resource.ExceptionUserTokenTimeExpired) { ExpireTime = expireTime; VerifyTime = verifyTime; }
- public UserTokenTimeExpiredException(string token, DateTime expireTime, DateTime verifyTime, Exception inner) : base(token, Resource.ExceptionUserTokenTimeExpired, inner) { ExpireTime = expireTime; VerifyTime = verifyTime; }
- protected UserTokenTimeExpiredException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public DateTime ExpireTime { get; private set; }
-
- public DateTime VerifyTime { get; private set; }
- }
-}
diff --git a/BackEnd/Timeline/Services/Token/UserTokenUserNotExistException.cs b/BackEnd/Timeline/Services/Token/UserTokenUserNotExistException.cs
deleted file mode 100644
index 28f56938..00000000
--- a/BackEnd/Timeline/Services/Token/UserTokenUserNotExistException.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-
-namespace Timeline.Services.Token
-{
- [Serializable]
- public class UserTokenUserNotExistException : UserTokenException
- {
- public UserTokenUserNotExistException() : base(Resource.ExceptionUserTokenUserNotExist) { }
- public UserTokenUserNotExistException(string token) : base(token, Resource.ExceptionUserTokenUserNotExist) { }
- public UserTokenUserNotExistException(string token, Exception inner) : base(token, Resource.ExceptionUserTokenUserNotExist, inner) { }
-
- protected UserTokenUserNotExistException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
- }
-}
diff --git a/BackEnd/Timeline/Services/Token/UserTokenVersionExpiredException.cs b/BackEnd/Timeline/Services/Token/UserTokenVersionExpiredException.cs
deleted file mode 100644
index db6b4669..00000000
--- a/BackEnd/Timeline/Services/Token/UserTokenVersionExpiredException.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-
-namespace Timeline.Services.Token
-{
- [Serializable]
- public class UserTokenVersionExpiredException : UserTokenException
- {
- public UserTokenVersionExpiredException() : base(Resource.ExceptionUserTokenVersionExpired) { }
- public UserTokenVersionExpiredException(string message) : base(message) { }
- public UserTokenVersionExpiredException(string message, Exception inner) : base(message, inner) { }
- public UserTokenVersionExpiredException(string token, long tokenVersion, long requiredVersion) : base(token, Resource.ExceptionUserTokenVersionExpired) { TokenVersion = tokenVersion; RequiredVersion = requiredVersion; }
- public UserTokenVersionExpiredException(string token, long tokenVersion, long requiredVersion, Exception inner) : base(token, Resource.ExceptionUserTokenVersionExpired, inner) { TokenVersion = tokenVersion; RequiredVersion = requiredVersion; }
- protected UserTokenVersionExpiredException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public long TokenVersion { get; set; }
-
- public long RequiredVersion { get; set; }
- }
-}
diff --git a/BackEnd/Timeline/Services/User/CreateTokenResult.cs b/BackEnd/Timeline/Services/User/CreateTokenResult.cs
new file mode 100644
index 00000000..b71a9e9e
--- /dev/null
+++ b/BackEnd/Timeline/Services/User/CreateTokenResult.cs
@@ -0,0 +1,12 @@
+using System;
+using Timeline.Entities;
+
+namespace Timeline.Services.User
+{
+ public class CreateTokenResult
+ {
+ public string Token { get; set; } = default!;
+ public UserEntity User { get; set; } = default!;
+ }
+}
+
diff --git a/BackEnd/Timeline/Services/User/UserService.cs b/BackEnd/Timeline/Services/User/UserService.cs
index a47bc860..1ad74bec 100644
--- a/BackEnd/Timeline/Services/User/UserService.cs
+++ b/BackEnd/Timeline/Services/User/UserService.cs
@@ -7,7 +7,8 @@ using System.Linq;
using System.Threading.Tasks;
using Timeline.Entities;
using Timeline.Models.Validation;
-
+using Timeline.Services.Token;
+
namespace Timeline.Services.User
{
public class UserService : BasicUserService, IUserService
@@ -19,14 +20,17 @@ namespace Timeline.Services.User
private readonly IPasswordService _passwordService;
+ private readonly IUserTokenService _userTokenService;
+
private readonly UsernameValidator _usernameValidator = new UsernameValidator();
private readonly NicknameValidator _nicknameValidator = new NicknameValidator();
- public UserService(ILogger<UserService> logger, DatabaseContext databaseContext, IPasswordService passwordService, IClock clock) : base(databaseContext)
+ public UserService(ILogger<UserService> logger, DatabaseContext databaseContext, IPasswordService passwordService, IUserTokenService userTokenService, IClock clock) : base(databaseContext)
{
_logger = logger;
_databaseContext = databaseContext;
_passwordService = passwordService;
+ _userTokenService = userTokenService;
_clock = clock;
}
@@ -162,6 +166,11 @@ namespace Timeline.Services.User
await _databaseContext.SaveChangesAsync();
_logger.LogInformation(Resource.LogUserModified, entity.Username, id);
+
+ if (password is not null)
+ {
+ await _userTokenService.RevokeAllTokenByUserIdAsync(id);
+ }
}
return entity;
@@ -214,6 +223,8 @@ namespace Timeline.Services.User
entity.Version += 1;
await _databaseContext.SaveChangesAsync();
_logger.LogInformation(Resource.LogChangePassowrd, entity.Username, id);
+
+ await _userTokenService.RevokeAllTokenByUserIdAsync(id);
}
}
}