Problem Statement
The current authentication mechanism exposes the application to security vulnerabilities, specifically Cross-Site Scripting (XSS). If authentication tokens are stored in localStorage or sessionStorage on the client side, malicious scripts can easily access and exfiltrate them. Additionally, relying on a single long-lived JWT requires users to re-login frequently if the expiration is short, or exposes a larger security window if the expiration is long.
To mitigate this, we need to separate authentication into two tokens: a Short-lived Access Token for API requests and a Long-lived Refresh Token for session maintenance.
Current Behavior / Limitation
- Single Token Flow: Users likely receive a single JWT upon login.
- Insecure Storage: If the frontend stores this token in local storage, it is accessible via JavaScript.
- Session UX: When the token expires, the user is immediately forced to log out, or the token is set with a very long expiration time, which is a security risk if the token is stolen.
- Missing Endpoints: There is no dedicated endpoint to renew an access token without user credentials.
Expected Improvement
- Access Tokens should be short-lived (e.g., 15 minutes) and sent to the client in the JSON response body (kept in memory by the frontend).
- Refresh Tokens should be long-lived (e.g., 7 days) and sent in an HttpOnly, Secure, SameSite cookie. This makes the token inaccessible to client-side JavaScript, preventing XSS attacks from stealing the session.
- A new
/refresh-token endpoint will allow the client to silently acquire a new Access Token using the HttpOnly cookie.
Proposed Approach
1. Update Configuration
Ensure new environment variables are available (e.g., REFRESH_TOKEN_SECRET, REFRESH_TOKEN_EXPIRY).
2. Database Schema (server/models/User.js)
- (Optional but recommended) Add a
refreshToken array field to the User schema if we want to support multi-device login or token invalidation/rotation on the server side.
3. Controller Logic (server/controllers/auth.controller.js)
- Login/Register:
- Generate Access Token (short expiration).
- Generate Refresh Token (long expiration).
- Send Access Token in the JSON response.
- Send Refresh Token in an HTTP cookie with flags:
httpOnly: true, secure: true (in production), sameSite: 'strict'.
- New Endpoint (
refresh-token):
- Read the cookie from the request.
- Verify the Refresh Token signature.
- (Optional) Check if the token exists in the User DB record (Token Rotation).
- Issue a new Access Token.
- Logout:
- Clear the Refresh Token cookie.
4. Middleware (server/middleware/auth.middleware.js)
- Ensure the middleware only looks for the Access Token in the
Authorization: Bearer <token> header.
- It should reject expired tokens, prompting the frontend (via 401/403) to hit the refresh endpoint.
Verification Steps
These steps are crucial to ensure the security features are working as intended.
- Environment Setup: Ensure you have
.env variables set for ACCESS_TOKEN_SECRET and REFRESH_TOKEN_SECRET.
- Test Login (Postman/cURL):
- Send a
POST request to /api/auth/login.
- Check Body: Ensure it contains an
accessToken.
- Check Cookies: Ensure the response sets a cookie named
refreshToken (or similar). Verify the flag HttpOnly is checked.
- Test Protected Route:
- Copy the
accessToken from the login response.
- Send a
GET request to a protected route adding the header Authorization: Bearer <your_access_token>.
- Result: Should return 200 OK data.
- Test Refresh Logic:
- Wait for the Access Token to expire (or temporarily set the expiry to 10s in code).
- Send a
GET request to the new /api/auth/refresh-token endpoint (ensure Postman is sending cookies).
- Result: The API should return a new
accessToken.
- Test Logout:
- Hit the logout endpoint.
- Check Cookies: The
refreshToken cookie should be empty or expired.
label ECWoC26
Problem Statement
The current authentication mechanism exposes the application to security vulnerabilities, specifically Cross-Site Scripting (XSS). If authentication tokens are stored in
localStorageorsessionStorageon the client side, malicious scripts can easily access and exfiltrate them. Additionally, relying on a single long-lived JWT requires users to re-login frequently if the expiration is short, or exposes a larger security window if the expiration is long.To mitigate this, we need to separate authentication into two tokens: a Short-lived Access Token for API requests and a Long-lived Refresh Token for session maintenance.
Current Behavior / Limitation
Expected Improvement
/refresh-tokenendpoint will allow the client to silently acquire a new Access Token using the HttpOnly cookie.Proposed Approach
1. Update Configuration
Ensure new environment variables are available (e.g.,
REFRESH_TOKEN_SECRET,REFRESH_TOKEN_EXPIRY).2. Database Schema (
server/models/User.js)refreshTokenarray field to the User schema if we want to support multi-device login or token invalidation/rotation on the server side.3. Controller Logic (
server/controllers/auth.controller.js)httpOnly: true,secure: true(in production),sameSite: 'strict'.refresh-token):4. Middleware (
server/middleware/auth.middleware.js)Authorization: Bearer <token>header.Verification Steps
These steps are crucial to ensure the security features are working as intended.
.envvariables set forACCESS_TOKEN_SECRETandREFRESH_TOKEN_SECRET.POSTrequest to/api/auth/login.accessToken.refreshToken(or similar). Verify the flagHttpOnlyis checked.accessTokenfrom the login response.GETrequest to a protected route adding the headerAuthorization: Bearer <your_access_token>.GETrequest to the new/api/auth/refresh-tokenendpoint (ensure Postman is sending cookies).accessToken.refreshTokencookie should be empty or expired.label ECWoC26