From fbc34ff8f8068d65fc584c0111224e170ea43f34 Mon Sep 17 00:00:00 2001 From: sujitaw Date: Thu, 9 Oct 2025 18:24:14 +0530 Subject: [PATCH 1/4] fix/session management using refresh token Signed-off-by: sujitaw --- apps/api-gateway/common/interface.ts | 22 ++- apps/api-gateway/src/authz/jwt.strategy.ts | 5 - apps/api-gateway/src/user/user.service.ts | 7 +- apps/user/interfaces/user.interface.ts | 7 + apps/user/repositories/user.repository.ts | 51 ++++++- apps/user/src/user.controller.ts | 5 + apps/user/src/user.service.ts | 125 +++++++++++------- .../src/client-registration.service.ts | 11 +- libs/common/src/interfaces/user.interface.ts | 1 + libs/common/src/response-messages/index.ts | 3 +- 10 files changed, 172 insertions(+), 65 deletions(-) diff --git a/apps/api-gateway/common/interface.ts b/apps/api-gateway/common/interface.ts index d3869d75f..c1791888b 100644 --- a/apps/api-gateway/common/interface.ts +++ b/apps/api-gateway/common/interface.ts @@ -1,3 +1,5 @@ +import { Prisma } from '@prisma/client'; + export interface ResponseType { statusCode: number; message: string; @@ -6,7 +8,21 @@ export interface ResponseType { } export interface ExceptionResponse { - message: string | string[] - error: string - statusCode: number + message: string | string[]; + error: string; + statusCode: number; +} + +export interface ISession { + id?: string; + sessionToken?: string; + userId?: string; + expires?: number; + refreshToken?: string; + keycloakUserId?: string; + type?: string; + accountId?: string; + sessionType?: string; + expiresAt?: Date; + clientInfo?: Prisma.JsonValue | null; } diff --git a/apps/api-gateway/src/authz/jwt.strategy.ts b/apps/api-gateway/src/authz/jwt.strategy.ts index e5f9860f1..5120fd914 100644 --- a/apps/api-gateway/src/authz/jwt.strategy.ts +++ b/apps/api-gateway/src/authz/jwt.strategy.ts @@ -31,11 +31,6 @@ export class JwtStrategy extends PassportStrategy(Strategy) { // Todo: We need to add this logic in seprate jwt gurd to handle the token expiration functionality. // eslint-disable-next-line @typescript-eslint/no-explicit-any const decodedToken: any = jwt.decode(jwtToken); - const currentTime = Math.floor(Date.now() / 1000); - if (decodedToken?.exp < currentTime) { - const sessionIds = { sessions: [decodedToken?.sid] }; - await this.authzService.logout(sessionIds); - } if (!decodedToken) { throw new UnauthorizedException(ResponseMessages.user.error.invalidAccessToken); } diff --git a/apps/api-gateway/src/user/user.service.ts b/apps/api-gateway/src/user/user.service.ts index 03029dd27..6fc8b9963 100644 --- a/apps/api-gateway/src/user/user.service.ts +++ b/apps/api-gateway/src/user/user.service.ts @@ -7,7 +7,7 @@ import { AddPasskeyDetailsDto } from './dto/add-user.dto'; import { UpdatePlatformSettingsDto } from './dto/update-platform-settings.dto'; import { IUsersProfile, ICheckUserDetails } from 'apps/user/interfaces/user.interface'; import { IUsersActivity } from 'libs/user-activity/interface'; -import { IUserInvitations } from '@credebl/common/interfaces/user.interface'; +import { ISignInUser, IUserInvitations } from '@credebl/common/interfaces/user.interface'; import { user } from '@prisma/client'; import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; import { NATSClient } from '@credebl/common/NATSClient'; @@ -74,6 +74,11 @@ export class UserService extends BaseService { return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-user-activity', payload); } + async getAccessTokenUsingRefreshToken(refreshToken: string): Promise { + const payload = { refreshToken }; + return this.natsClient.sendNatsMessage(this.serviceProxy, 'generate-accessToken-using-refresh-token', payload); + } + async addPasskey(userEmail: string, userInfo: AddPasskeyDetailsDto): Promise { const payload = { userEmail, userInfo }; return this.natsClient.sendNatsMessage(this.serviceProxy, 'add-passkey', payload); diff --git a/apps/user/interfaces/user.interface.ts b/apps/user/interfaces/user.interface.ts index d080d596a..6b4061fc0 100644 --- a/apps/user/interfaces/user.interface.ts +++ b/apps/user/interfaces/user.interface.ts @@ -301,3 +301,10 @@ export interface IRestrictedUserSession { clientInfo: Prisma.JsonValue | null; sessionType: string; } + +export interface ITokenData { + sessionToken: string; + expires: number; + refreshToken: string; + expiresAt: Date; +} diff --git a/apps/user/repositories/user.repository.ts b/apps/user/repositories/user.repository.ts index 38f8ef0a8..2b4f9a867 100644 --- a/apps/user/repositories/user.repository.ts +++ b/apps/user/repositories/user.repository.ts @@ -1,12 +1,20 @@ /* eslint-disable camelcase */ /* eslint-disable prefer-destructuring */ +import { + BadRequestException, + Injectable, + InternalServerErrorException, + Logger, + NotFoundException +} from '@nestjs/common'; import { IOrgUsers, IRestrictedUserSession, ISendVerificationEmail, ISession, IShareUserCertificate, + ITokenData, IUserDeletedActivity, IUserInformation, IUsersProfile, @@ -17,7 +25,6 @@ import { UserRoleDetails, UserRoleMapping } from '../interfaces/user.interface'; -import { Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'; import { Prisma, RecordType, @@ -722,6 +729,21 @@ export class UserRepository { } } + //this function is to fetch all session details for a user including token details without any restriction + async fetchUserSessionDetails(userId: string): Promise { + try { + const userSessionCount = await this.prisma.session.findMany({ + where: { + userId + } + }); + return userSessionCount; + } catch (error) { + this.logger.error(`Error in getting user session details: ${error.message} `); + throw error; + } + } + async checkAccountDetails(userId: string): Promise { try { const accountDetails = await this.prisma.account.findUnique({ @@ -980,8 +1002,13 @@ export class UserRepository { }); return userSession; } catch (error) { - this.logger.error(`Error in logging out user: ${error.message}`); - throw error; + if (error instanceof Prisma.PrismaClientKnownRequestError && 'P2025' === error.code) { + this.logger.warn(`Session not found for deletion: ${sessionId}`); + throw new NotFoundException('Record to be deleted not found'); + } else { + this.logger.error(`Error in logging out user: ${error.message}`); + throw error; + } } } @@ -1032,4 +1059,22 @@ export class UserRepository { throw error; } } + + async updateSessionToken(id: string, tokenData: ITokenData): Promise { + if (!id || !tokenData) { + throw new BadRequestException(`Missing id or tokenData for session details update`); + } + try { + const sessionResponse = await this.prisma.session.update({ + where: { + id + }, + data: tokenData + }); + return sessionResponse; + } catch (error) { + this.logger.error(`Error in creating session: ${error.message} `); + throw error; + } + } } diff --git a/apps/user/src/user.controller.ts b/apps/user/src/user.controller.ts index 5731998f1..356c56842 100644 --- a/apps/user/src/user.controller.ts +++ b/apps/user/src/user.controller.ts @@ -95,6 +95,11 @@ export class UserController { return this.userService.refreshTokenDetails(refreshToken); } + @MessagePattern({ cmd: 'generate-accessToken-using-refresh-token' }) + async generateAccessTokenUsingRefreshToken(payload: { refreshToken: string }): Promise { + return this.userService.generateAccessTokenUsingRefreshToken(payload?.refreshToken); + } + @MessagePattern({ cmd: 'session-details-by-userId' }) async userSessions(userId: string): Promise { return this.userService.userSessions(userId); diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index 474dc6752..a2e6c5fc8 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -452,7 +452,21 @@ export class UserService { throw new UnauthorizedException(ResponseMessages.user.error.registerFido); } // called seprate method to delete exp session - this.userRepository.deleteInactiveSessions(userData?.id); + if (userData?.id) { + const sessions = await this.userRepository.fetchUserSessionDetails(userData?.id); + if (sessions && 0 < sessions.length) { + for (const session of sessions) { + const decodedToken = jwt.decode(session.refreshToken); + const expiresAt = new Date(decodedToken.exp * 1000); + const currentTime = new Date(); + if (expiresAt < currentTime) { + await this.userRepository.deleteSession(session?.id); + } + } + } + } + + // this.userRepository.deleteInactiveSessions(userData?.id); const userSessionDetails = await this.userRepository.fetchUserSessions(userData?.id); if (Number(process.env.SESSIONS_LIMIT) <= userSessionDetails?.length) { throw new BadRequestException(ResponseMessages.user.error.sessionLimitReached); @@ -467,7 +481,7 @@ export class UserService { tokenDetails = await this.generateToken(email.toLowerCase(), decryptedPassword, userData); } // eslint-disable-next-line @typescript-eslint/no-explicit-any - const decodedToken: any = jwt.decode(tokenDetails?.access_token); + const decodedToken: any = jwt.decode(tokenDetails?.refresh_token); const expiresAt = new Date(decodedToken.exp * 1000); const sessionData = { @@ -537,59 +551,76 @@ export class UserService { async refreshTokenDetails(refreshToken: string): Promise { try { - try { - const data = jwt.decode(refreshToken) as jwt.JwtPayload; - const userByKeycloakId = await this.userRepository.getUserByKeycloakId(data?.sub); - this.logger.debug(`User details::;${JSON.stringify(userByKeycloakId)}`); - const tokenResponse = await this.clientRegistrationService.getAccessToken( - refreshToken, - userByKeycloakId?.['clientId'], - userByKeycloakId?.['clientSecret'] - ); - this.logger.debug(`tokenResponse::::${JSON.stringify(tokenResponse)}`); - // Fetch the details from account table based on userid and refresh token - const userAccountDetails = await this.userRepository.checkAccountDetails(userByKeycloakId?.['id']); - // Update the account details with latest access token, refresh token and exp date - if (!userAccountDetails) { - throw new NotFoundException(ResponseMessages.user.error.userAccountNotFound); - } - // Fetch session details - const sessionDetails = await this.userRepository.fetchSessionByRefreshToken(refreshToken); - if (!sessionDetails) { - throw new NotFoundException(ResponseMessages.user.error.userSeesionNotFound); - } - // Delete previous session - const deletePreviousSession = await this.userRepository.deleteSession(sessionDetails.id); - if (!deletePreviousSession) { - throw new InternalServerErrorException(ResponseMessages.user.error.errorInDeleteSession); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const decodedToken: any = jwt.decode(tokenResponse?.access_token); - const expiresAt = new Date(decodedToken.exp * 1000); - const sessionData = { - sessionToken: tokenResponse.access_token, - userId: userByKeycloakId?.['id'], - expires: tokenResponse.expires_in, - refreshToken: tokenResponse.refresh_token, - sessionType: SessionType.USER_SESSION, - accountId: userAccountDetails.id, - expiresAt - }; - const addSessionDetails = await this.userRepository.createSession(sessionData); - if (!addSessionDetails) { - throw new InternalServerErrorException(ResponseMessages.user.error.errorInSessionCreation); - } + const data = jwt.decode(refreshToken) as jwt.JwtPayload; + const refreshTokenExp = new Date(data.exp * 1000); + const currentTime = new Date(); + if (refreshTokenExp < currentTime) { + await this.userRepository.deleteSession(data?.sid); + throw new UnauthorizedException(ResponseMessages.user.error.refreshTokenExpired); + } + const userByKeycloakId = await this.userRepository.getUserByKeycloakId(data?.sub); + this.logger.debug(`User details::;${JSON.stringify(userByKeycloakId)}`); + const tokenResponse = await this.clientRegistrationService.getAccessToken( + refreshToken, + userByKeycloakId?.['clientId'], + userByKeycloakId?.['clientSecret'] + ); + this.logger.debug(`tokenResponse::::${JSON.stringify(tokenResponse)}`); + // Fetch the details from account table based on userid and refresh token + const userAccountDetails = await this.userRepository.checkAccountDetails(userByKeycloakId?.['id']); + // Update the account details with latest access token, refresh token and exp date + if (!userAccountDetails) { + throw new NotFoundException(ResponseMessages.user.error.userAccountNotFound); + } + // Fetch session details - return tokenResponse; - } catch (error) { - throw new BadRequestException(ResponseMessages.user.error.invalidRefreshToken); + const sessionDetails = await this.userRepository.getSession(data.sid); + if (!sessionDetails) { + throw new NotFoundException(ResponseMessages.user.error.userSeesionNotFound); } + // Delete previous session + // const deletePreviousSession = await this.userRepository.deleteSession(sessionDetails.id); + // if (!deletePreviousSession) { + // throw new InternalServerErrorException(ResponseMessages.user.error.errorInDeleteSession); + // } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const decodedToken: any = jwt.decode(tokenResponse?.refresh_token); + const expiresAt = new Date(decodedToken.exp * 1000); + const sessionData = { + sessionToken: tokenResponse.access_token, + expires: tokenResponse.expires_in, + refreshToken: tokenResponse.refresh_token, + expiresAt + }; + const addSessionDetails = await this.userRepository.updateSessionToken(tokenResponse.session_state, sessionData); + if (!addSessionDetails) { + throw new InternalServerErrorException(ResponseMessages.user.error.errorInSessionCreation); + } + + return tokenResponse; } catch (error) { this.logger.error(`In refreshTokenDetails : ${JSON.stringify(error)}`); throw new RpcException(error.response ? error.response : error); } } + async generateAccessTokenUsingRefreshToken(refreshToken: string): Promise { + try { + const data = jwt.decode(refreshToken) as jwt.JwtPayload; + const userByKeycloakId = await this.userRepository.getUserByKeycloakId(data?.sub); + this.logger.debug(`User details::;${JSON.stringify(userByKeycloakId)}`); + const tokenResponse = await this.clientRegistrationService.getAccessToken( + refreshToken, + userByKeycloakId?.['clientId'], + userByKeycloakId?.['clientSecret'] + ); + return tokenResponse; + } catch (error) { + this.logger.error(`In generateAccessTokenUsingRefreshToken : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } + async userSessions(userId: string): Promise { try { return await this.userRepository.fetchUserSessions(userId); diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index 51dbeb923..3cad3f8cf 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -4,21 +4,22 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { BadRequestException, Injectable, Logger, NotFoundException, UnauthorizedException } from '@nestjs/common'; import * as qs from 'qs'; +import { BadRequestException, Injectable, Logger, NotFoundException, UnauthorizedException } from '@nestjs/common'; + import { ClientCredentialTokenPayloadDto } from './dtos/client-credential-token-payload.dto'; import { CommonConstants } from '@credebl/common/common.constant'; import { CommonService } from '@credebl/common'; import { CreateUserDto } from './dtos/create-user.dto'; +import { IClientRoles } from './interfaces/client.interface'; +import { IFormattedResponse } from '@credebl/common/interfaces/interface'; import { JwtService } from '@nestjs/jwt'; import { KeycloakUrlService } from '@credebl/keycloak-url'; -import { accessTokenPayloadDto } from './dtos/accessTokenPayloadDto'; -import { userTokenPayloadDto } from './dtos/userTokenPayloadDto'; import { KeycloakUserRegistrationDto } from 'apps/user/dtos/keycloak-register.dto'; import { ResponseMessages } from '@credebl/common/response-messages'; -import { IClientRoles } from './interfaces/client.interface'; -import { IFormattedResponse } from '@credebl/common/interfaces/interface'; +import { accessTokenPayloadDto } from './dtos/accessTokenPayloadDto'; +import { userTokenPayloadDto } from './dtos/userTokenPayloadDto'; @Injectable() export class ClientRegistrationService { diff --git a/libs/common/src/interfaces/user.interface.ts b/libs/common/src/interfaces/user.interface.ts index 54ab6f7fc..98e36572c 100644 --- a/libs/common/src/interfaces/user.interface.ts +++ b/libs/common/src/interfaces/user.interface.ts @@ -6,6 +6,7 @@ export interface ISignInUser { refresh_token?: string; isRegisteredToSupabase?: boolean; sessionId?: string; + refresh_expires_in?: number; } export interface IVerifyUserEmail { email: string; diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 49308396c..3e888a3bd 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -73,7 +73,8 @@ export const ResponseMessages = { errorInDeleteSession: 'Error in deleting the session', errorInSessionCreation: 'Error in create session', userAccountNotFound: 'User account not found', - userSeesionNotFound: 'User session not found' + userSeesionNotFound: 'User session not found', + refreshTokenExpired: 'Refresh token has expired' } }, organisation: { From 71b3285dc99d359fd721b6ec9e3bdbd6636ae98a Mon Sep 17 00:00:00 2001 From: sujitaw Date: Mon, 13 Oct 2025 11:49:21 +0530 Subject: [PATCH 2/4] fix/code rabbit comments Signed-off-by: sujitaw --- apps/api-gateway/src/user/user.service.ts | 7 +--- apps/user/repositories/user.repository.ts | 4 +++ apps/user/src/user.controller.ts | 5 --- apps/user/src/user.service.ts | 41 ++-------------------- libs/common/src/response-messages/index.ts | 2 +- 5 files changed, 9 insertions(+), 50 deletions(-) diff --git a/apps/api-gateway/src/user/user.service.ts b/apps/api-gateway/src/user/user.service.ts index 6fc8b9963..03029dd27 100644 --- a/apps/api-gateway/src/user/user.service.ts +++ b/apps/api-gateway/src/user/user.service.ts @@ -7,7 +7,7 @@ import { AddPasskeyDetailsDto } from './dto/add-user.dto'; import { UpdatePlatformSettingsDto } from './dto/update-platform-settings.dto'; import { IUsersProfile, ICheckUserDetails } from 'apps/user/interfaces/user.interface'; import { IUsersActivity } from 'libs/user-activity/interface'; -import { ISignInUser, IUserInvitations } from '@credebl/common/interfaces/user.interface'; +import { IUserInvitations } from '@credebl/common/interfaces/user.interface'; import { user } from '@prisma/client'; import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; import { NATSClient } from '@credebl/common/NATSClient'; @@ -74,11 +74,6 @@ export class UserService extends BaseService { return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-user-activity', payload); } - async getAccessTokenUsingRefreshToken(refreshToken: string): Promise { - const payload = { refreshToken }; - return this.natsClient.sendNatsMessage(this.serviceProxy, 'generate-accessToken-using-refresh-token', payload); - } - async addPasskey(userEmail: string, userInfo: AddPasskeyDetailsDto): Promise { const payload = { userEmail, userInfo }; return this.natsClient.sendNatsMessage(this.serviceProxy, 'add-passkey', payload); diff --git a/apps/user/repositories/user.repository.ts b/apps/user/repositories/user.repository.ts index 2b4f9a867..3889e96c2 100644 --- a/apps/user/repositories/user.repository.ts +++ b/apps/user/repositories/user.repository.ts @@ -1074,6 +1074,10 @@ export class UserRepository { return sessionResponse; } catch (error) { this.logger.error(`Error in creating session: ${error.message} `); + if (error instanceof Prisma.PrismaClientKnownRequestError && 'P2025' === error.code) { + this.logger.warn(`Session not found for update: ${id}`); + throw new NotFoundException('Session not found'); + } throw error; } } diff --git a/apps/user/src/user.controller.ts b/apps/user/src/user.controller.ts index 356c56842..5731998f1 100644 --- a/apps/user/src/user.controller.ts +++ b/apps/user/src/user.controller.ts @@ -95,11 +95,6 @@ export class UserController { return this.userService.refreshTokenDetails(refreshToken); } - @MessagePattern({ cmd: 'generate-accessToken-using-refresh-token' }) - async generateAccessTokenUsingRefreshToken(payload: { refreshToken: string }): Promise { - return this.userService.generateAccessTokenUsingRefreshToken(payload?.refreshToken); - } - @MessagePattern({ cmd: 'session-details-by-userId' }) async userSessions(userId: string): Promise { return this.userService.userSessions(userId); diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index a2e6c5fc8..f57c49af0 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -451,22 +451,8 @@ export class UserService { if (true === isPasskey && false === userData?.isFidoVerified) { throw new UnauthorizedException(ResponseMessages.user.error.registerFido); } - // called seprate method to delete exp session - if (userData?.id) { - const sessions = await this.userRepository.fetchUserSessionDetails(userData?.id); - if (sessions && 0 < sessions.length) { - for (const session of sessions) { - const decodedToken = jwt.decode(session.refreshToken); - const expiresAt = new Date(decodedToken.exp * 1000); - const currentTime = new Date(); - if (expiresAt < currentTime) { - await this.userRepository.deleteSession(session?.id); - } - } - } - } - // this.userRepository.deleteInactiveSessions(userData?.id); + this.userRepository.deleteInactiveSessions(userData?.id); const userSessionDetails = await this.userRepository.fetchUserSessions(userData?.id); if (Number(process.env.SESSIONS_LIMIT) <= userSessionDetails?.length) { throw new BadRequestException(ResponseMessages.user.error.sessionLimitReached); @@ -576,13 +562,9 @@ export class UserService { const sessionDetails = await this.userRepository.getSession(data.sid); if (!sessionDetails) { - throw new NotFoundException(ResponseMessages.user.error.userSeesionNotFound); + throw new NotFoundException(ResponseMessages.user.error.userSessionNotFound); } - // Delete previous session - // const deletePreviousSession = await this.userRepository.deleteSession(sessionDetails.id); - // if (!deletePreviousSession) { - // throw new InternalServerErrorException(ResponseMessages.user.error.errorInDeleteSession); - // } + // eslint-disable-next-line @typescript-eslint/no-explicit-any const decodedToken: any = jwt.decode(tokenResponse?.refresh_token); const expiresAt = new Date(decodedToken.exp * 1000); @@ -604,23 +586,6 @@ export class UserService { } } - async generateAccessTokenUsingRefreshToken(refreshToken: string): Promise { - try { - const data = jwt.decode(refreshToken) as jwt.JwtPayload; - const userByKeycloakId = await this.userRepository.getUserByKeycloakId(data?.sub); - this.logger.debug(`User details::;${JSON.stringify(userByKeycloakId)}`); - const tokenResponse = await this.clientRegistrationService.getAccessToken( - refreshToken, - userByKeycloakId?.['clientId'], - userByKeycloakId?.['clientSecret'] - ); - return tokenResponse; - } catch (error) { - this.logger.error(`In generateAccessTokenUsingRefreshToken : ${JSON.stringify(error)}`); - throw new RpcException(error.response ? error.response : error); - } - } - async userSessions(userId: string): Promise { try { return await this.userRepository.fetchUserSessions(userId); diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 3e888a3bd..8ca70aed4 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -73,7 +73,7 @@ export const ResponseMessages = { errorInDeleteSession: 'Error in deleting the session', errorInSessionCreation: 'Error in create session', userAccountNotFound: 'User account not found', - userSeesionNotFound: 'User session not found', + userSessionNotFound: 'User session not found', refreshTokenExpired: 'Refresh token has expired' } }, From a1bb7cb81e867173c1b65f45279977f99f20dc2d Mon Sep 17 00:00:00 2001 From: sujitaw Date: Mon, 13 Oct 2025 12:08:14 +0530 Subject: [PATCH 3/4] fix/code rabbit comment Signed-off-by: sujitaw --- apps/user/src/user.service.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index f57c49af0..26071a22f 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -466,8 +466,10 @@ export class UserService { const decryptedPassword = await this.commonService.decryptPassword(password); tokenDetails = await this.generateToken(email.toLowerCase(), decryptedPassword, userData); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const decodedToken: any = jwt.decode(tokenDetails?.refresh_token); + const decodedToken = jwt.decode(tokenDetails?.refresh_token); + if (!decodedToken || 'object' !== typeof decodedToken || !decodedToken.exp || !decodedToken.sid) { + throw new UnauthorizedException(ResponseMessages.user.error.refreshTokenExpired); + } const expiresAt = new Date(decodedToken.exp * 1000); const sessionData = { From d838b4740a86077d724ae3674c86d0e4a3f24ffa Mon Sep 17 00:00:00 2001 From: sujitaw Date: Thu, 30 Oct 2025 12:59:07 +0530 Subject: [PATCH 4/4] fix/pr comments Signed-off-by: sujitaw --- apps/user/src/user.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index 26071a22f..8df037eda 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -547,13 +547,11 @@ export class UserService { throw new UnauthorizedException(ResponseMessages.user.error.refreshTokenExpired); } const userByKeycloakId = await this.userRepository.getUserByKeycloakId(data?.sub); - this.logger.debug(`User details::;${JSON.stringify(userByKeycloakId)}`); const tokenResponse = await this.clientRegistrationService.getAccessToken( refreshToken, userByKeycloakId?.['clientId'], userByKeycloakId?.['clientSecret'] ); - this.logger.debug(`tokenResponse::::${JSON.stringify(tokenResponse)}`); // Fetch the details from account table based on userid and refresh token const userAccountDetails = await this.userRepository.checkAccountDetails(userByKeycloakId?.['id']); // Update the account details with latest access token, refresh token and exp date