Files
2026-03-12 15:17:52 +07:00

5.0 KiB

Authentication & Token Flow (iYHCT360)

This document describes in detail the Authentication and Token (Access Token / Refresh Token) processing flow based on the project's actual source code. This is the core security framework designed to be reusable (base) across other projects.


1. Technology Overview & Design Patterns

  • JWT (JSON Web Token): Short-lived Access Token containing the user's basic Claims.
  • Refresh Token Storage: Securely stored in the Database (saving only hashed strings to prevent database leak vulnerabilities).
  • Refresh Token Rotation: Upon performing a token refresh, the old Refresh Token is revoked, and a completely new Refresh Token is issued (minimizing the risk of theft).
  • Single Active Session: Each login will revoke all currently Active Refresh Tokens of that User to ensure there is only 1 active session at a time.

1.1. JWT Configuration (appsettings.json)

System default lifecycles:

  • Access Token: Lives for 15 minutes.
  • Refresh Token: Lives for 7 days.

2. Detailed Processing Flows

2.1. Login Flow

Endpoint: POST /api/auth/login

Processing Steps:

  1. Client sends a LoginRequest containing: Email and Password.
  2. The system searches for the User by Email (only IsActive users).
  3. Verifies the password (hashing verification via IPasswordHasher).
  4. The system retrieves the User's corresponding Roles from the database.
  5. Generates an Access Token containing information (id, roles, etc.).
  6. Generates a random Refresh Token string.
  7. Ensures Single Active Session: The system queries the db to find and mark all previous active Refresh Tokens of the user (RevokedAt = null and not Expired) as revoked (RevokedAt = DateTimeOffset.UtcNow).
  8. Hashing the token: The random Refresh Token string (which is sent to the client only once) is hashed using an algorithm (e.g., TokenHasher.ComputeHash()) and ONLY this hash value is saved in the Database along with the IP + UserAgent.
  9. Updates the user's LastLoginAt and returns the information (Access Token, Refresh Token, Roles, UserType) to the Client.

2.2. Token Refresh Flow (Token Rotation)

Endpoint: POST /api/auth/refresh-token

Processing Steps:

  1. Client sends both the AccessToken (which might be expired) and the RefreshToken.
  2. The system looks up the Refresh Token in the Database (stored as the hash of the original Refresh Token string) => Verifies existence, check it's not revoked (RevokedAt == null), and check it's not expired.
  3. Decodes the AccessToken using the Extract Claims function to get the userId. The userId retrieved from the token MUST match the UserId owning the Refresh Token.
  4. Checks if the User is still IsActive.
  5. Reloads the user's roles from the Database (if roles have changed while the old token was still valid, the refresh action will incorporate the new roles into the next Access Token).
  6. Issues a new AccessToken and a completely new RefreshToken.
  7. Manually marks the current Refresh Token as revoked (RevokedAt = DateTimeOffset.UtcNow).
  8. Saves the hash value of the new RefreshToken into the DB with a 7-day expiration.
  9. Returns the data to the Client.

2.3. Logout Flow

Endpoint: POST /api/auth/logout

Processing Steps:

  1. Client sends the RefreshToken to the system.
  2. The system hashes the incoming token and finds the corresponding record in the DB.
  3. Marks that Token as revoked (RevokedAt = DateTimeOffset.UtcNow).
  4. (Optional: The Client deletes the AccessToken/RefreshToken stored locally on its device).

2.4. Password Reset (Forgot password & Reset)

  1. Forgot Password (POST /api/auth/forgot-password):
    • Generates a Raw Token (a Guid.NewGuid() string).
    • Hashes that Raw Token and saves it into the User's SecurityStamp field.
    • Concatenates ${user.Id}:{rawToken}, then applies Base64 Encode and returns it (or sends it via Email) to act as the ResetToken.
  2. Reset Password (POST /api/auth/reset-password):
    • Base64 decodes the token above to extract the userId and rawToken.
    • Finds the User in the db, compares the hash of rawToken with the SecurityStamp.
    • If valid, Hashes the NewPassword into the DB. Sets SecurityStamp = null (can only be used to change the password once).
    • Security: Immediately revokes all active Refresh Tokens for this User to forcefully log them out across all devices.

3. Best Practices Summary Applied in the Backend

  • Fully leverage the TokenHasher concept: Never store Refresh Tokens, OTP tokens, or Forgot password tokens in direct raw text. If the Database leaks, attackers still cannot use those tokens (similar to hashed passwords).
  • Always cross-check: Ensure the Refresh Token genuinely belongs to the userId present in the Access Token to prevent cross-token forgery.
  • Synchronization: When an Admin changes a User's Password/Roles or a User changes their own password (Reset/Change) -> The system must automatically revoke all Refresh Tokens to force a fresh log-in.