-
Notifications
You must be signed in to change notification settings - Fork 79
authentication-service: lastLogin field not updated in JWT token on subsequent logins #2402
Description
Describe the bug
The lastLogin field in the JWT token is not being updated on subsequent logins. When a user logs in, the token contains the stale/old lastLogin timestamp instead of the current login time.
To Reproduce
Steps to reproduce the behavior:
- Login as a user (not first-time login) via /auth/login endpoint
- Exchange the auth code for a token via /auth/token endpoint
- Decode the resulting accessToken JWT
- Check the lastLogin claim in the payload
- The lastLogin value will be from the previous login, not the current one
Expected behavior
The lastLogin field in the JWT token should reflect the current login timestamp after each successful login.
Root Cause
In src/services/idp-login.service.ts, the generateToken method has this logic (lines 154-161):
if (
payload.userId &&
!(await this.userRepo.firstTimeUser(payload.userId))
) {
await this.userRepo.updateLastLogin(payload.userId);
}
return await this.createJWT(payload, authClient, LoginType.ACCESS);
The issue is twofold:
- Wrong condition check: The code checks payload.userId, but after login the user data is in payload.user (not payload.userId). The condition fails because payload.userId is undefined, so updateLastLogin() is never called.
- Stale user object: Even if updateLastLogin() were called, the createJWT method (line 208-209) uses the cached payload.user object:
if (payload.user) {
user = payload.user; // ← Uses OLD user object before DB update
} - This object still has the old lastLogin value since it's not refetched from the database after the update.
Suggested Fix
Update the generateToken method to:
- Check payload.user?.id instead of payload.userId
- Update the in-memory payload.user.lastLogin with the current timestamp after the DB update
if (
payload.user?.id &&
!(await this.userRepo.firstTimeUser(payload.user.id))
) {
await this.userRepo.updateLastLogin(payload.user.id);
payload.user.lastLogin = new Date(); // Update the in-memory object
}
return await this.createJWT(payload, authClient, LoginType.ACCESS);
Additional context
- This affects all JWT-based authentication flows using the /auth/token endpoint
- The lastLogin field is important for audit trails, session management, and security monitoring
- Users relying on this field for displaying "last seen" information will see incorrect timestamps