-
Notifications
You must be signed in to change notification settings - Fork 80
fix(authentication-service): ensure JWT lastLogin reflects current login time #2410
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
…gin time ensure JWT lastLogin reflects current login time GH-2402
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Updates last-login tracking so the database update and JWT/user payload share the same captured timestamp for a login event.
Changes:
- Add optional
timeparameter toUserRepository.updateLastLoginto support caller-provided timestamps. - Capture
Date.now()once per login flow and reuse it for both persistence and response payload population. - Set
payload.user.lastLoginduring login flows to align with the recorded timestamp.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| services/authentication-service/src/services/idp-login.service.ts | Capture a single login timestamp, persist it, and set it on the returned payload user. |
| services/authentication-service/src/repositories/user.repository.ts | Allow updateLastLogin to accept a caller-provided timestamp (defaults to now). |
| services/authentication-service/src/modules/auth/controllers/login.controller.ts | Mirror the IDP login flow change: single captured timestamp used for DB + payload. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| await this.userRepo.updateLastLogin(payload.user.id); | ||
| const time = Date.now(); | ||
| await this.userRepo.updateLastLogin(payload.user.id, time); | ||
| payload.user.lastLogin = new Date(time); |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updateLastLogin persists a numeric timestamp (ms since epoch), but the payload sets lastLogin to a Date object. When serialized into JWT/JSON this typically becomes an ISO string, which may diverge from existing clients expecting a number. Consider keeping payload.user.lastLogin the same type as what the API/JWT historically exposed (e.g., set it to time if consumers expect a number) or update the repository/model to store a Date consistently end-to-end.
| payload.user.lastLogin = new Date(time); | |
| payload.user.lastLogin = time; |
| await this.userRepo.updateLastLogin(payload.user.id); | ||
| const time = Date.now(); | ||
| await this.userRepo.updateLastLogin(payload.user.id, time); | ||
| payload.user.lastLogin = new Date(time); |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as in IdpLoginService: the DB update uses a millisecond timestamp while the payload assigns a Date. Align the payload lastLogin type with what downstream consumers expect (number vs Date/ISO string) to avoid subtle client/JWT compatibility breaks.
| payload.user.lastLogin = new Date(time); | |
| (payload.user as AnyObject).lastLogin = time; |
| } | ||
|
|
||
| async updateLastLogin(userId: string): Promise<void> { | ||
| async updateLastLogin(userId: string, time?: number): Promise<void> { |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new parameter name time is ambiguous (units and meaning). Consider renaming to something explicit like timeMs / timestampMs (or switching the parameter type to Date and naming it lastLoginAt) to reduce confusion and prevent accidental misuse.
| userId, | ||
| { | ||
| lastLogin: Date.now(), | ||
| lastLogin: time ?? Date.now(), |
Copilot
AI
Feb 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new parameter name time is ambiguous (units and meaning). Consider renaming to something explicit like timeMs / timestampMs (or switching the parameter type to Date and naming it lastLoginAt) to reduce confusion and prevent accidental misuse.
| } | ||
|
|
||
| async updateLastLogin(userId: string): Promise<void> { | ||
| async updateLastLogin(userId: string, time?: number): Promise<void> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree to this approach. We should not send the timestamp here. it should always remain Date.now. The issue is this timestamp is not provided into JWT correctly. We should only fix that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is for consistency that we store the "exact" same timestamp in the database and in the JWT token.
The issue is that if we do Date.now() inside this method and then again after or before calling this method, then the lastLogin field might be off by a few milliseconds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I never said to do that twice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now returning lastLogin as a part of the responseBody
fix trivy vulnerability GH-2402
ed25de2 to
1353dd7
Compare
| ) { | ||
| await this.userRepo.updateLastLogin(payload.user.id); | ||
| const time = Date.now(); | ||
| await this.userRepo.updateLastLogin(payload.user.id, time); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use the returned lastlogin timestamp from the method and update it into payload.
| userId, | ||
| { | ||
| lastLogin: Date.now(), | ||
| lastLogin: time ?? Date.now(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return the new lastlogin time from this function
| userId, | ||
| { | ||
| lastLogin: Date.now(), | ||
| lastLogin: time ?? Date.now(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return the new lastlogin time from this function
| } | ||
|
|
||
| async updateLastLogin(userId: string): Promise<void> { | ||
| async updateLastLogin(userId: string, time?: number): Promise<void> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I never said to do that twice
SonarQube reviewer guide
|



user lastLogin detail will be a part of the response body
#2402
GH-2402
This pull request updates the process for recording and handling the user's last login time across the authentication service. The changes ensure that a consistent timestamp is used when updating the user's last login and that this timestamp is also set in the returned in response body .
Last Login will not be a part of the token instead it will be a part of the response body.