From c0b82b5c0157a8953f301f151a80d07c30f28491 Mon Sep 17 00:00:00 2001 From: shitrerohit Date: Thu, 25 Sep 2025 20:36:10 +0530 Subject: [PATCH 01/12] feat:API for generate token for client Signed-off-by: shitrerohit --- .../src/organization/dtos/client-token.dto.ts | 20 ++++ .../organization/organization.controller.ts | 12 ++ .../src/organization/organization.service.ts | 4 + apps/organization/dtos/client-token.dto.ts | 6 + .../src/organization.controller.ts | 109 ++++++++++-------- apps/organization/src/organization.service.ts | 36 ++++++ .../src/client-registration.service.ts | 47 +++++++- .../src/dtos/client-token.dto.ts | 6 + libs/common/src/response-messages/index.ts | 4 +- libs/keycloak-url/src/keycloak-url.service.ts | 108 +++++------------ 10 files changed, 220 insertions(+), 132 deletions(-) create mode 100644 apps/api-gateway/src/organization/dtos/client-token.dto.ts create mode 100644 apps/organization/dtos/client-token.dto.ts create mode 100644 libs/client-registration/src/dtos/client-token.dto.ts diff --git a/apps/api-gateway/src/organization/dtos/client-token.dto.ts b/apps/api-gateway/src/organization/dtos/client-token.dto.ts new file mode 100644 index 000000000..c5a558a96 --- /dev/null +++ b/apps/api-gateway/src/organization/dtos/client-token.dto.ts @@ -0,0 +1,20 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class ClientTokenDto { + @ApiProperty() + @IsString({ message: 'orgId must be in string format.' }) + orgId: string; + + @ApiProperty() + @IsString({ message: 'clientId must be in string format.' }) + clientId: string; + + @ApiProperty() + @IsString({ message: 'clientSecret must be in string format.' }) + clientSecret: string; + + @ApiProperty() + @IsString({ message: 'grantType must be in string format.' }) + grantType?: string = 'client_credentials'; +} diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index f07d44db5..6ee31cb8e 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -54,6 +54,7 @@ import { UserAccessGuard } from '../authz/guards/user-access-guard'; import { GetAllOrganizationsDto } from './dtos/get-organizations.dto'; import { PrimaryDid } from './dtos/set-primary-did.dto'; import { TrimStringParamPipe } from '@credebl/common/cast.helper'; +import { ClientTokenDto } from './dtos/client-token.dto'; @UseFilters(CustomExceptionFilter) @Controller('orgs') @@ -789,4 +790,15 @@ export class OrganizationController { }; return res.status(HttpStatus.OK).json(finalResponse); } + + @Post('/generateIssuerApiToken') + @ApiOperation({ + summary: 'Generate API Token for the issuer', + description: 'Generate API Token for the issuer' + }) + @ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto }) + async generateApiToken(@Body() clientTokenDto: ClientTokenDto, @Res() res: Response): Promise { + const finalResponse = await this.organizationService.generateClientApiToken(clientTokenDto); + return res.status(HttpStatus.OK).header('Content-Type', 'application/json').send(finalResponse); + } } diff --git a/apps/api-gateway/src/organization/organization.service.ts b/apps/api-gateway/src/organization/organization.service.ts index d5928a653..72e1c89b4 100644 --- a/apps/api-gateway/src/organization/organization.service.ts +++ b/apps/api-gateway/src/organization/organization.service.ts @@ -24,6 +24,7 @@ import { GetAllOrganizationsDto } from './dtos/get-organizations.dto'; import { PrimaryDid } from './dtos/set-primary-did.dto'; import { NATSClient } from '@credebl/common/NATSClient'; import { ClientProxy } from '@nestjs/microservices'; +import { ClientTokenDto } from './dtos/client-token.dto'; @Injectable() export class OrganizationService extends BaseService { @@ -238,4 +239,7 @@ export class OrganizationService extends BaseService { const imageBuffer = Buffer.from(base64Data, 'base64'); return imageBuffer; } + async generateClientApiToken(clientTokenDto: ClientTokenDto): Promise<{ token: string }> { + return this.natsClient.sendNatsMessage(this.serviceProxy, 'generate-client-api-token', clientTokenDto); + } } diff --git a/apps/organization/dtos/client-token.dto.ts b/apps/organization/dtos/client-token.dto.ts new file mode 100644 index 000000000..0e1a8d644 --- /dev/null +++ b/apps/organization/dtos/client-token.dto.ts @@ -0,0 +1,6 @@ +export class ClientTokenDto { + orgId: string; + clientId: string; + clientSecret: string; + grantType?: string = 'client_credentials'; +} diff --git a/apps/organization/src/organization.controller.ts b/apps/organization/src/organization.controller.ts index ca93335e0..72317bf03 100644 --- a/apps/organization/src/organization.controller.ts +++ b/apps/organization/src/organization.controller.ts @@ -4,12 +4,27 @@ import { OrganizationService } from './organization.service'; import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; import { UpdateInvitationDto } from '../dtos/update-invitation.dt'; -import { IDidList, IGetOrgById, IGetOrganization, IOrgDetails, IUpdateOrganization, Payload } from '../interfaces/organization.interface'; -import { IOrgCredentials, IOrganizationInvitations, IOrganization, IOrganizationDashboard, IDeleteOrganization, IOrgActivityCount } from '@credebl/common/interfaces/organization.interface'; +import { + IDidList, + IGetOrgById, + IGetOrganization, + IOrgDetails, + IUpdateOrganization, + Payload +} from '../interfaces/organization.interface'; +import { + IOrgCredentials, + IOrganizationInvitations, + IOrganization, + IOrganizationDashboard, + IDeleteOrganization, + IOrgActivityCount +} from '@credebl/common/interfaces/organization.interface'; import { organisation, user } from '@prisma/client'; import { IAccessTokenData } from '@credebl/common/interfaces/interface'; import { IClientRoles } from '@credebl/client-registration/interfaces/client.interface'; import { IOrgRoles } from 'libs/org-roles/interfaces/org-roles.interface'; +import { ClientTokenDto } from '../dtos/client-token.dto'; @Controller() export class OrganizationController { @@ -23,7 +38,9 @@ export class OrganizationController { */ @MessagePattern({ cmd: 'create-organization' }) - async createOrganization(@Body() payload: { createOrgDto: CreateOrganizationDto; userId: string, keycloakUserId: string }): Promise { + async createOrganization( + @Body() payload: { createOrgDto: CreateOrganizationDto; userId: string; keycloakUserId: string } + ): Promise { return this.organizationService.createOrganization(payload.createOrgDto, payload.userId, payload.keycloakUserId); } @@ -34,17 +51,19 @@ export class OrganizationController { */ @MessagePattern({ cmd: 'set-primary-did' }) - async setPrimaryDid(@Body() payload: { orgId:string, did:string, id:string}): Promise { + async setPrimaryDid(@Body() payload: { orgId: string; did: string; id: string }): Promise { return this.organizationService.setPrimaryDid(payload.orgId, payload.did, payload.id); } /** - * - * @param payload + * + * @param payload * @returns organization client credentials */ @MessagePattern({ cmd: 'create-org-credentials' }) - async createOrgCredentials(@Body() payload: { orgId: string; userId: string, keycloakUserId: string }): Promise { + async createOrgCredentials( + @Body() payload: { orgId: string; userId: string; keycloakUserId: string } + ): Promise { return this.organizationService.createOrgCredentials(payload.orgId, payload.userId, payload.keycloakUserId); } @@ -55,7 +74,11 @@ export class OrganizationController { */ @MessagePattern({ cmd: 'update-organization' }) - async updateOrganization(payload: { updateOrgDto: IUpdateOrganization; userId: string, orgId: string }): Promise { + async updateOrganization(payload: { + updateOrgDto: IUpdateOrganization; + userId: string; + orgId: string; + }): Promise { return this.organizationService.updateOrganization(payload.updateOrgDto, payload.userId, payload.orgId); } @@ -65,7 +88,7 @@ export class OrganizationController { * @returns organization's did list */ @MessagePattern({ cmd: 'fetch-organization-dids' }) - async getOrgDidList(payload: {orgId:string}): Promise { + async getOrgDidList(payload: { orgId: string }): Promise { return this.organizationService.getOrgDidList(payload.orgId); } @@ -75,9 +98,7 @@ export class OrganizationController { * @returns Get created organization details */ @MessagePattern({ cmd: 'get-organizations' }) - async getOrganizations( - @Body() payload: { userId: string} & Payload - ): Promise { + async getOrganizations(@Body() payload: { userId: string } & Payload): Promise { const { userId, pageNumber, pageSize, search, role } = payload; return this.organizationService.getOrganizations(userId, pageNumber, pageSize, search, role); } @@ -87,12 +108,9 @@ export class OrganizationController { * @returns Get created organization details */ @MessagePattern({ cmd: 'get-organizations-count' }) - async countTotalOrgs( - @Body() payload: { userId: string} - ): Promise { - + async countTotalOrgs(@Body() payload: { userId: string }): Promise { const { userId } = payload; - + return this.organizationService.countTotalOrgs(userId); } @@ -100,9 +118,7 @@ export class OrganizationController { * @returns Get public organization details */ @MessagePattern({ cmd: 'get-public-organizations' }) - async getPublicOrganizations( - @Body() payload: Payload - ): Promise { + async getPublicOrganizations(@Body() payload: Payload): Promise { const { pageNumber, pageSize, search } = payload; return this.organizationService.getPublicOrganizations(pageNumber, pageSize, search); } @@ -113,13 +129,13 @@ export class OrganizationController { * @returns Get created organization details */ @MessagePattern({ cmd: 'get-organization-by-id' }) - async getOrganization(@Body() payload: { orgId: string; userId: string}): Promise { + async getOrganization(@Body() payload: { orgId: string; userId: string }): Promise { return this.organizationService.getOrganization(payload.orgId); } -/** - * @param orgSlug - * @returns organization details - */ + /** + * @param orgSlug + * @returns organization details + */ @MessagePattern({ cmd: 'get-organization-public-profile' }) async getPublicProfile(payload: { orgSlug }): Promise { return this.organizationService.getPublicProfile(payload); @@ -131,9 +147,7 @@ export class OrganizationController { * @returns Get created invitation details */ @MessagePattern({ cmd: 'get-invitations-by-orgId' }) - async getInvitationsByOrgId( - @Body() payload: { orgId: string } & Payload - ): Promise { + async getInvitationsByOrgId(@Body() payload: { orgId: string } & Payload): Promise { return this.organizationService.getInvitationsByOrgId( payload.orgId, payload.pageNumber, @@ -143,11 +157,11 @@ export class OrganizationController { } /** - * @returns Get org-roles + * @returns Get org-roles */ @MessagePattern({ cmd: 'get-org-roles' }) - async getOrgRoles(payload: {orgId: string, user: user}): Promise { + async getOrgRoles(payload: { orgId: string; user: user }): Promise { return this.organizationService.getOrgRoles(payload.orgId, payload.user); } @@ -163,7 +177,7 @@ export class OrganizationController { */ @MessagePattern({ cmd: 'send-invitation' }) async createInvitation( - @Body() payload: { bulkInvitationDto: BulkSendInvitationDto; userId: string, userEmail: string } + @Body() payload: { bulkInvitationDto: BulkSendInvitationDto; userId: string; userEmail: string } ): Promise { return this.organizationService.createInvitation(payload.bulkInvitationDto, payload.userId, payload.userEmail); } @@ -198,7 +212,7 @@ export class OrganizationController { */ @MessagePattern({ cmd: 'update-user-roles' }) - async updateUserRoles(payload: { orgId: string; roleIds: string[]; userId: string}): Promise { + async updateUserRoles(payload: { orgId: string; roleIds: string[]; userId: string }): Promise { return this.organizationService.updateUserRoles(payload.orgId, payload.roleIds, payload.userId); } @@ -212,9 +226,9 @@ export class OrganizationController { return this.organizationService.getOrganizationActivityCount(payload.orgId, payload.userId); } -/** - * @returns organization profile details - */ + /** + * @returns organization profile details + */ @MessagePattern({ cmd: 'fetch-organization-profile' }) async getOrgPofile(payload: { orgId: string }): Promise { return this.organizationService.getOrgPofile(payload.orgId); @@ -231,27 +245,27 @@ export class OrganizationController { } @MessagePattern({ cmd: 'get-organization-details' }) - async getOrgData(payload: { orgId: string; }): Promise { + async getOrgData(payload: { orgId: string }): Promise { return this.organizationService.getOrgDetails(payload.orgId); } - + @MessagePattern({ cmd: 'delete-organization' }) - async deleteOrganization(payload: { orgId: string, user: user }): Promise { + async deleteOrganization(payload: { orgId: string; user: user }): Promise { return this.organizationService.deleteOrganization(payload.orgId, payload.user); } @MessagePattern({ cmd: 'delete-org-client-credentials' }) - async deleteOrganizationCredentials(payload: { orgId: string, user: user }): Promise { + async deleteOrganizationCredentials(payload: { orgId: string; user: user }): Promise { return this.organizationService.deleteClientCredentials(payload.orgId, payload.user); } @MessagePattern({ cmd: 'delete-organization-invitation' }) - async deleteOrganizationInvitation(payload: { orgId: string; invitationId: string; }): Promise { + async deleteOrganizationInvitation(payload: { orgId: string; invitationId: string }): Promise { return this.organizationService.deleteOrganizationInvitation(payload.orgId, payload.invitationId); } @MessagePattern({ cmd: 'authenticate-client-credentials' }) - async clientLoginCredentails(payload: { clientId: string; clientSecret: string;}): Promise { + async clientLoginCredentails(payload: { clientId: string; clientSecret: string }): Promise { return this.organizationService.clientLoginCredentails(payload); } @@ -261,12 +275,12 @@ export class OrganizationController { } @MessagePattern({ cmd: 'get-agent-type-by-org-agent-type-id' }) - async getAgentTypeByAgentTypeId(payload: {orgAgentTypeId: string}): Promise { + async getAgentTypeByAgentTypeId(payload: { orgAgentTypeId: string }): Promise { return this.organizationService.getAgentTypeByAgentTypeId(payload.orgAgentTypeId); } @MessagePattern({ cmd: 'get-org-roles-details' }) - async getOrgRolesDetails(payload: {roleName: string}): Promise { + async getOrgRolesDetails(payload: { roleName: string }): Promise { return this.organizationService.getOrgRolesDetails(payload.roleName); } @@ -286,7 +300,12 @@ export class OrganizationController { } @MessagePattern({ cmd: 'get-org-agents-and-user-roles' }) - async getOrgAgentDetailsForEcosystem(payload: {orgIds: string[], search: string}): Promise { + async getOrgAgentDetailsForEcosystem(payload: { orgIds: string[]; search: string }): Promise { return this.organizationService.getOrgAgentDetailsForEcosystem(payload); } -} \ No newline at end of file + + @MessagePattern({ cmd: 'generate-client-api-token' }) + async generateClientApiToken(payload: ClientTokenDto): Promise<{ token: string }> { + return this.organizationService.generateClientApiToken(payload); + } +} diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index 0e6972fc8..461518795 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -65,6 +65,7 @@ import { IOrgRoles } from 'libs/org-roles/interfaces/org-roles.interface'; import { NATSClient } from '@credebl/common/NATSClient'; import { UserRepository } from 'apps/user/repositories/user.repository'; import * as jwt from 'jsonwebtoken'; +import { ClientTokenDto } from '../dtos/client-token.dto'; @Injectable() export class OrganizationService { @@ -2029,4 +2030,39 @@ export class OrganizationService { throw new RpcException(error.response ? error.response : error); } } + + async generateClientApiToken(generateTokenDetails: ClientTokenDto): Promise<{ token: string }> { + try { + this.logger.debug(`generateTokenDetails ::: ${JSON.stringify(generateTokenDetails)}`); + const orgDetails = await this.organizationRepository.getOrganizationDetails(generateTokenDetails.orgId); + this.logger.debug(`orgDetails ::: ${JSON.stringify(orgDetails)}`); + if (!orgDetails) { + throw new NotFoundException(ResponseMessages.organisation.error.orgNotFound); + } + // Step:1 generate the token using admin credentials + const adminTokenDetails = + await this.clientRegistrationService.generateTokenUsingAdminCredentials(generateTokenDetails); + this.logger.debug(`adminTokenDetails ::: ${JSON.stringify(adminTokenDetails)}`); + if (!adminTokenDetails?.access_token) { + throw new InternalServerErrorException(ResponseMessages.organisation.error.adminTokenDetails); + } + // Step:2 Call API to fetch client id and secret + const clientDetails = await this.clientRegistrationService.fetchClientDetails( + generateTokenDetails.orgId, + adminTokenDetails.access_token + ); + if (!clientDetails || 0 === clientDetails.length) { + throw new NotFoundException(ResponseMessages.organisation.error.clientDetails); + } + this.logger.debug(`clientDetails ::: ${JSON.stringify(clientDetails)}`); + const { secret } = clientDetails[0]; + //step3: generate the token using client id and secret + const authenticationResult = await this.authenticateClientKeycloak(generateTokenDetails.orgId, secret); + this.logger.debug(`authenticationResult ::: ${JSON.stringify(authenticationResult)}`); + return { token: authenticationResult.access_token }; + } catch (error) { + this.logger.error(`in generating issuer api token : ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); + } + } } diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index 51dbeb923..40a6b104f 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -4,21 +4,23 @@ /* 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 { ClientTokenDto } from './dtos/client-token.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 { @@ -721,4 +723,39 @@ export class ClientRegistrationService { return userInfo; } + + async generateTokenUsingAdminCredentials(clientDetails: ClientTokenDto) { + const realmName = process.env.KEYCLOAK_REALM; + const payload = { + client_id: clientDetails.clientId, + client_secret: clientDetails.clientSecret, + grant_type: clientDetails.grantType + }; + const config = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }; + this.logger.debug(`generateTokenUsingAdminCredentials payload ${JSON.stringify(payload)}`); + const generatetedTokenDetails = await this.commonService.httpPost( + await this.keycloakUrlService.GenerateTokenUsingAdminCredentials(realmName), + qs.stringify(payload), + config + ); + this.logger.debug(`generatetedTokenDetails ${JSON.stringify(generatetedTokenDetails)}`); + return generatetedTokenDetails; + } + + async fetchClientDetails(clientId: string, token: string) { + const realmName = process.env.KEYCLOAK_REALM; + + const clientDetails = await this.commonService.httpGet( + await this.keycloakUrlService.GetClientURL(realmName, clientId), + this.getAuthHeader(token) + ); + + this.logger.debug(`clientDetails ${JSON.stringify(clientDetails)}`); + + return clientDetails; + } } diff --git a/libs/client-registration/src/dtos/client-token.dto.ts b/libs/client-registration/src/dtos/client-token.dto.ts new file mode 100644 index 000000000..0e1a8d644 --- /dev/null +++ b/libs/client-registration/src/dtos/client-token.dto.ts @@ -0,0 +1,6 @@ +export class ClientTokenDto { + orgId: string; + clientId: string; + clientSecret: string; + grantType?: string = 'client_credentials'; +} diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 49308396c..48c353c81 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -130,7 +130,9 @@ export const ResponseMessages = { primaryDid: 'This DID is already set to primary DID', didNotFound: 'DID does not exist in organiation', organizationNotFound: 'Organization not found', - MaximumOrgsLimit: 'Limit reached: You can be associated with or create maximum 10 organizations.' + MaximumOrgsLimit: 'Limit reached: You can be associated with or create maximum 10 organizations.', + adminTokenDetails: 'Error in generating admin token details', + clientDetails: ' Error in fetching client details' } }, diff --git a/libs/keycloak-url/src/keycloak-url.service.ts b/libs/keycloak-url/src/keycloak-url.service.ts index fc259d1c2..dd500e032 100644 --- a/libs/keycloak-url/src/keycloak-url.service.ts +++ b/libs/keycloak-url/src/keycloak-url.service.ts @@ -2,131 +2,77 @@ import { Injectable, Logger } from '@nestjs/common'; @Injectable() export class KeycloakUrlService { - private readonly logger = new Logger('KeycloakUrlService'); + private readonly logger = new Logger('KeycloakUrlService'); - - async createUserURL( - realm: string - ):Promise { - - return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/users`; - } - - async getUserByUsernameURL( - realm: string, - username: string - ):Promise { - - return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/users?username=${username}`; + async createUserURL(realm: string): Promise { + return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/users`; } - async GetUserInfoURL( - realm: string, - userid: string - ):Promise { - - return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/users/${userid}`; - } - - async GetSATURL( - realm: string - ):Promise { + async getUserByUsernameURL(realm: string, username: string): Promise { + return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/users?username=${username}`; + } - return `${process.env.KEYCLOAK_DOMAIN}realms/${realm}/protocol/openid-connect/token`; - } - - async ResetPasswordURL( - realm: string, - userid: string - ):Promise { + async GetUserInfoURL(realm: string, userid: string): Promise { + return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/users/${userid}`; + } - return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/users/${userid}/reset-password`; + async GetSATURL(realm: string): Promise { + return `${process.env.KEYCLOAK_DOMAIN}realms/${realm}/protocol/openid-connect/token`; } + async ResetPasswordURL(realm: string, userid: string): Promise { + return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/users/${userid}/reset-password`; + } - async CreateRealmURL():Promise { + async CreateRealmURL(): Promise { return `${process.env.KEYCLOAK_DOMAIN}admin/realms`; } - async createClientURL( - realm: string - ):Promise { - + async createClientURL(realm: string): Promise { return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/clients`; } - async GetClientURL( - realm: string, - clientid: string - ):Promise { - + async GetClientURL(realm: string, clientid: string): Promise { return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/clients?clientId=${clientid}`; - } - - async GetClientSecretURL( - realm: string, - clientid: string - ):Promise { + } + async GetClientSecretURL(realm: string, clientid: string): Promise { return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/clients/${clientid}/client-secret`; } - async GetClientRoleURL( - realm: string, - clientid: string, - roleName = '' - ):Promise { - + async GetClientRoleURL(realm: string, clientid: string, roleName = ''): Promise { if ('' === roleName) { return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/clients/${clientid}/roles`; } return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/clients/${clientid}/roles/${roleName}`; - } - async GetRealmRoleURL( - realm: string, - roleName = '' - ):Promise { - + async GetRealmRoleURL(realm: string, roleName = ''): Promise { if ('' === roleName) { return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/roles`; } return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/roles/${roleName}`; - } - async GetClientUserRoleURL( - realm: string, - userId: string, - clientId?: string - ):Promise { - + async GetClientUserRoleURL(realm: string, userId: string, clientId?: string): Promise { if (clientId) { return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/users/${userId}/role-mappings/clients/${clientId}`; } return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/users/${userId}/role-mappings/realm`; - } - - - async GetClientIdpURL( - realm: string, - idp: string - ):Promise { + async GetClientIdpURL(realm: string, idp: string): Promise { return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/clients/${idp}`; } - async GetClient( - realm: string, - clientId: string - ):Promise { - + async GetClient(realm: string, clientId: string): Promise { return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/clients?clientId=${clientId}`; } + async GenerateTokenUsingAdminCredentials(realm: string): Promise { + return `${process.env.KEYCLOAK_DOMAIN}realms/${realm}/protocol/openid-connect/token`; + } } From e686b3d9a8d166003d2fe19f8a3c10d504f9eea4 Mon Sep 17 00:00:00 2001 From: shitrerohit Date: Thu, 25 Sep 2025 12:55:57 +0530 Subject: [PATCH 02/12] feat: added app launch details column in organization table Signed-off-by: shitrerohit --- apps/organization/repositories/organization.repository.ts | 1 + .../migration.sql | 2 ++ libs/prisma-service/prisma/schema.prisma | 1 + 3 files changed, 4 insertions(+) create mode 100644 libs/prisma-service/prisma/migrations/20250925060521_added_app_launch_details_column_in_organization/migration.sql diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index deed11a4f..0cfa681d2 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -662,6 +662,7 @@ export class OrganizationRepository { countryId: true, stateId: true, cityId: true, + appLaunchDetails: true, userOrgRoles: { where: { orgRole: { diff --git a/libs/prisma-service/prisma/migrations/20250925060521_added_app_launch_details_column_in_organization/migration.sql b/libs/prisma-service/prisma/migrations/20250925060521_added_app_launch_details_column_in_organization/migration.sql new file mode 100644 index 000000000..595c14fa5 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20250925060521_added_app_launch_details_column_in_organization/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "organisation" ADD COLUMN "appLaunchDetails" JSONB; diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 108ff614e..dfba4e1f4 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -133,6 +133,7 @@ model organisation { clientId String? @db.VarChar(500) clientSecret String? @db.VarChar(500) registrationNumber String? @db.VarChar(100) + appLaunchDetails Json? countryId Int? countries countries? @relation(fields: [countryId], references: [id]) stateId Int? From 685e7f083ab68b63f62b5c2d9b5b8f80a900f38d Mon Sep 17 00:00:00 2001 From: shitrerohit Date: Thu, 25 Sep 2025 20:32:32 +0530 Subject: [PATCH 03/12] fix:exclude API path Signed-off-by: shitrerohit --- apps/api-gateway/src/organization/organization.controller.ts | 1 + apps/organization/src/organization.service.ts | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index 6ee31cb8e..6a99bf5cc 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -792,6 +792,7 @@ export class OrganizationController { } @Post('/generateIssuerApiToken') + @ApiExcludeEndpoint() @ApiOperation({ summary: 'Generate API Token for the issuer', description: 'Generate API Token for the issuer' diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index 461518795..c5e450c60 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -2033,16 +2033,13 @@ export class OrganizationService { async generateClientApiToken(generateTokenDetails: ClientTokenDto): Promise<{ token: string }> { try { - this.logger.debug(`generateTokenDetails ::: ${JSON.stringify(generateTokenDetails)}`); const orgDetails = await this.organizationRepository.getOrganizationDetails(generateTokenDetails.orgId); - this.logger.debug(`orgDetails ::: ${JSON.stringify(orgDetails)}`); if (!orgDetails) { throw new NotFoundException(ResponseMessages.organisation.error.orgNotFound); } // Step:1 generate the token using admin credentials const adminTokenDetails = await this.clientRegistrationService.generateTokenUsingAdminCredentials(generateTokenDetails); - this.logger.debug(`adminTokenDetails ::: ${JSON.stringify(adminTokenDetails)}`); if (!adminTokenDetails?.access_token) { throw new InternalServerErrorException(ResponseMessages.organisation.error.adminTokenDetails); } @@ -2054,11 +2051,9 @@ export class OrganizationService { if (!clientDetails || 0 === clientDetails.length) { throw new NotFoundException(ResponseMessages.organisation.error.clientDetails); } - this.logger.debug(`clientDetails ::: ${JSON.stringify(clientDetails)}`); const { secret } = clientDetails[0]; //step3: generate the token using client id and secret const authenticationResult = await this.authenticateClientKeycloak(generateTokenDetails.orgId, secret); - this.logger.debug(`authenticationResult ::: ${JSON.stringify(authenticationResult)}`); return { token: authenticationResult.access_token }; } catch (error) { this.logger.error(`in generating issuer api token : ${JSON.stringify(error)}`); From 2df98e3b2e58a28113eab7c9b8ac21fefb2797dc Mon Sep 17 00:00:00 2001 From: shitrerohit Date: Fri, 26 Sep 2025 11:28:31 +0530 Subject: [PATCH 04/12] fix:resolved PR comments Signed-off-by: shitrerohit --- libs/client-registration/src/client-registration.service.ts | 5 ----- libs/common/src/response-messages/index.ts | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index 40a6b104f..d46b6476c 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -736,13 +736,11 @@ export class ClientRegistrationService { 'Content-Type': 'application/x-www-form-urlencoded' } }; - this.logger.debug(`generateTokenUsingAdminCredentials payload ${JSON.stringify(payload)}`); const generatetedTokenDetails = await this.commonService.httpPost( await this.keycloakUrlService.GenerateTokenUsingAdminCredentials(realmName), qs.stringify(payload), config ); - this.logger.debug(`generatetedTokenDetails ${JSON.stringify(generatetedTokenDetails)}`); return generatetedTokenDetails; } @@ -753,9 +751,6 @@ export class ClientRegistrationService { await this.keycloakUrlService.GetClientURL(realmName, clientId), this.getAuthHeader(token) ); - - this.logger.debug(`clientDetails ${JSON.stringify(clientDetails)}`); - return clientDetails; } } diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 48c353c81..05c1f683b 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -132,7 +132,7 @@ export const ResponseMessages = { organizationNotFound: 'Organization not found', MaximumOrgsLimit: 'Limit reached: You can be associated with or create maximum 10 organizations.', adminTokenDetails: 'Error in generating admin token details', - clientDetails: ' Error in fetching client details' + clientDetails: 'Error in fetching client details' } }, From 9a1a8f9775cf864e5caeb40dcd8d563aca52d29f Mon Sep 17 00:00:00 2001 From: shitrerohit Date: Fri, 26 Sep 2025 11:38:59 +0530 Subject: [PATCH 05/12] fix:resolved PR comments Signed-off-by: shitrerohit --- libs/client-registration/src/client-registration.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index d46b6476c..1817e778a 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -729,7 +729,7 @@ export class ClientRegistrationService { const payload = { client_id: clientDetails.clientId, client_secret: clientDetails.clientSecret, - grant_type: clientDetails.grantType + grant_type: clientDetails.grantType ?? 'client_credentials' }; const config = { headers: { From a7f85c43bf3880feb37dbaffd4d4f5b41c1bb734 Mon Sep 17 00:00:00 2001 From: shitrerohit Date: Mon, 29 Sep 2025 11:55:19 +0530 Subject: [PATCH 06/12] fix:removed the redendent keyclock API call Signed-off-by: shitrerohit --- libs/client-registration/src/client-registration.service.ts | 2 +- libs/keycloak-url/src/keycloak-url.service.ts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index 1817e778a..1c2c227c5 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -737,7 +737,7 @@ export class ClientRegistrationService { } }; const generatetedTokenDetails = await this.commonService.httpPost( - await this.keycloakUrlService.GenerateTokenUsingAdminCredentials(realmName), + await this.keycloakUrlService.GetSATURL(realmName), qs.stringify(payload), config ); diff --git a/libs/keycloak-url/src/keycloak-url.service.ts b/libs/keycloak-url/src/keycloak-url.service.ts index dd500e032..b3dab1b91 100644 --- a/libs/keycloak-url/src/keycloak-url.service.ts +++ b/libs/keycloak-url/src/keycloak-url.service.ts @@ -71,8 +71,4 @@ export class KeycloakUrlService { async GetClient(realm: string, clientId: string): Promise { return `${process.env.KEYCLOAK_DOMAIN}admin/realms/${realm}/clients?clientId=${clientId}`; } - - async GenerateTokenUsingAdminCredentials(realm: string): Promise { - return `${process.env.KEYCLOAK_DOMAIN}realms/${realm}/protocol/openid-connect/token`; - } } From 97948cfc744870a0e66fcaa62229771bc2ec9225 Mon Sep 17 00:00:00 2001 From: shitrerohit Date: Mon, 29 Sep 2025 15:49:28 +0530 Subject: [PATCH 07/12] fix:added missing validation for generate client token api Signed-off-by: shitrerohit --- .../src/organization/dtos/client-token.dto.ts | 4 ++++ apps/organization/dtos/client-token.dto.ts | 1 + apps/organization/src/organization.service.ts | 10 ++++++++++ libs/common/src/response-messages/index.ts | 3 ++- 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/api-gateway/src/organization/dtos/client-token.dto.ts b/apps/api-gateway/src/organization/dtos/client-token.dto.ts index c5a558a96..abd95d8e7 100644 --- a/apps/api-gateway/src/organization/dtos/client-token.dto.ts +++ b/apps/api-gateway/src/organization/dtos/client-token.dto.ts @@ -6,6 +6,10 @@ export class ClientTokenDto { @IsString({ message: 'orgId must be in string format.' }) orgId: string; + @ApiProperty() + @IsString({ message: 'clientAlias must be in string format.' }) + clientAlias: string; + @ApiProperty() @IsString({ message: 'clientId must be in string format.' }) clientId: string; diff --git a/apps/organization/dtos/client-token.dto.ts b/apps/organization/dtos/client-token.dto.ts index 0e1a8d644..52bb2e868 100644 --- a/apps/organization/dtos/client-token.dto.ts +++ b/apps/organization/dtos/client-token.dto.ts @@ -1,5 +1,6 @@ export class ClientTokenDto { orgId: string; + clientAlias: string; clientId: string; clientSecret: string; grantType?: string = 'client_credentials'; diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index c5e450c60..da23f5cb9 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -2037,6 +2037,16 @@ export class OrganizationService { if (!orgDetails) { throw new NotFoundException(ResponseMessages.organisation.error.orgNotFound); } + // Add validation for client alias + const clientIdKey = `${generateTokenDetails.clientAlias}_KEYCLOAK_MANAGEMENT_CLIENT_ID`; + + if (!process.env[clientIdKey]) { + throw new NotFoundException(ResponseMessages.organisation.error.clientDetails); + } + const getDecryptedAlias = await this.commonService.decryptPassword(process.env[clientIdKey]); + if (generateTokenDetails.clientAlias.toLowerCase() !== getDecryptedAlias.toLowerCase()) { + throw new NotFoundException(ResponseMessages.organisation.error.missMachingAlias); + } // Step:1 generate the token using admin credentials const adminTokenDetails = await this.clientRegistrationService.generateTokenUsingAdminCredentials(generateTokenDetails); diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 05c1f683b..796d8e2a0 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -132,7 +132,8 @@ export const ResponseMessages = { organizationNotFound: 'Organization not found', MaximumOrgsLimit: 'Limit reached: You can be associated with or create maximum 10 organizations.', adminTokenDetails: 'Error in generating admin token details', - clientDetails: 'Error in fetching client details' + clientDetails: 'Error in fetching client details', + missMachingAlias: 'Client alias does not match the registered alias' } }, From ef1fbeeb54b1f55c71daa1e5c339869d34fd4ea7 Mon Sep 17 00:00:00 2001 From: shitrerohit Date: Mon, 29 Sep 2025 16:18:14 +0530 Subject: [PATCH 08/12] fix:variable name changed Signed-off-by: shitrerohit --- libs/client-registration/src/client-registration.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/client-registration/src/client-registration.service.ts b/libs/client-registration/src/client-registration.service.ts index 1c2c227c5..952439843 100644 --- a/libs/client-registration/src/client-registration.service.ts +++ b/libs/client-registration/src/client-registration.service.ts @@ -736,12 +736,12 @@ export class ClientRegistrationService { 'Content-Type': 'application/x-www-form-urlencoded' } }; - const generatetedTokenDetails = await this.commonService.httpPost( + const generatedTokenDetails = await this.commonService.httpPost( await this.keycloakUrlService.GetSATURL(realmName), qs.stringify(payload), config ); - return generatetedTokenDetails; + return generatedTokenDetails; } async fetchClientDetails(clientId: string, token: string) { From 9ce164f7c5c611afb323a9ef6178338e7971587a Mon Sep 17 00:00:00 2001 From: shitrerohit Date: Mon, 29 Sep 2025 20:03:25 +0530 Subject: [PATCH 09/12] fix: squash merges check Signed-off-by: shitrerohit --- .husky/pre-push | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.husky/pre-push b/.husky/pre-push index ee3c0acbf..67f8a6572 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -10,6 +10,9 @@ RESET="\033[0m" echo "🔒 Checking commit signatures..." +# Get the email of the current user +# CURRENT_EMAIL=$(git config user.email) + while read local_ref local_sha remote_ref remote_sha; do # Determine range of commits if [ "$remote_sha" = "0000000000000000000000000000000000000000" ]; then @@ -29,7 +32,12 @@ while read local_ref local_sha remote_ref remote_sha; do continue fi - sig_status=$(git log --format='%G?' -n 1 $commit) + # Skip commits not authored by the current user + # author_email=$(git log -1 --format='%ae' $commit) + # if [ "$author_email" != "$CURRENT_EMAIL" ]; then + # echo "${CYAN}â„šī¸ Skipping commit $commit authored by $author_email${RESET}" + # continue + # fi # Currently only check for "N" - for no signature. if [ "$sig_status" = "N" ]; then @@ -58,13 +66,12 @@ while read local_ref local_sha remote_ref remote_sha; do --exec 'if [ $(git rev-list --parents -n 1 HEAD | awk "{print NF-1}") -eq 1 ]; then git commit --amend -sS --no-edit; else echo "Skipping merge commit $(git rev-parse --short HEAD)"; fi' echo "${GREEN}✅ Earliest unsigned commit has been signed.${RESET}" - echo "Please push again: git push --force-with-lease" + echo "Please push again: git push" exit 1 else echo "${YELLOW}You can manually sign commits using:${RESET}" echo # Latest commit (HEAD) - head_commit=$(git rev-parse HEAD) echo " - For the latest commit (HEAD):" echo " git commit --amend -sS --no-edit" echo @@ -75,7 +82,6 @@ while read local_ref local_sha remote_ref remote_sha; do echo " - For the earliest unsigned commit $short_hash (skipping merge commits):" echo " git rebase --onto $parent_commit $parent_commit \\" echo " --exec 'if [ \$(git rev-list --parents -n 1 HEAD | awk \"{print NF-1}\") -eq 1 ]; then git commit --amend -sS --no-edit; else echo \"Skipping merge commit \$(git rev-parse --short HEAD)\"; fi'" - echo echo "Then push normally with:" echo " git push" @@ -92,4 +98,4 @@ while read local_ref local_sha remote_ref remote_sha; do fi done -echo "${GREEN}✅ All commits are signed and verified${RESET}" +echo "${GREEN}✅ All commits are signed and verified${RESET}" \ No newline at end of file From 5f4a60bf2ea56516e8769cbd99b87bcbf83dad5c Mon Sep 17 00:00:00 2001 From: shitrerohit Date: Tue, 30 Sep 2025 12:21:42 +0530 Subject: [PATCH 10/12] fix:updated the condition in generate issuer token API Signed-off-by: shitrerohit --- apps/organization/src/organization.service.ts | 50 ++++++++++++++----- libs/common/src/response-messages/index.ts | 2 +- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index da23f5cb9..51b89b93c 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -2033,41 +2033,67 @@ export class OrganizationService { async generateClientApiToken(generateTokenDetails: ClientTokenDto): Promise<{ token: string }> { try { + // Fetch organization and fail fast if not found const orgDetails = await this.organizationRepository.getOrganizationDetails(generateTokenDetails.orgId); if (!orgDetails) { throw new NotFoundException(ResponseMessages.organisation.error.orgNotFound); } - // Add validation for client alias + + // Compose keys const clientIdKey = `${generateTokenDetails.clientAlias}_KEYCLOAK_MANAGEMENT_CLIENT_ID`; + const clientSecretKey = `${generateTokenDetails.clientAlias}_KEYCLOAK_MANAGEMENT_CLIENT_SECRET`; - if (!process.env[clientIdKey]) { - throw new NotFoundException(ResponseMessages.organisation.error.clientDetails); + // Fetch env vars once + const encryptedClientId = process.env[clientIdKey]; + const encryptedClientSecret = process.env[clientSecretKey]; + + // Fail fast if not present + if (!encryptedClientId || !encryptedClientSecret) { + throw new NotFoundException(ResponseMessages.organisation.error.invalidClientCredentials); } - const getDecryptedAlias = await this.commonService.decryptPassword(process.env[clientIdKey]); - if (generateTokenDetails.clientAlias.toLowerCase() !== getDecryptedAlias.toLowerCase()) { - throw new NotFoundException(ResponseMessages.organisation.error.missMachingAlias); + + // Decrypt env vars once + const decryptedClientId = await this.commonService.decryptPassword(encryptedClientId); + if (!decryptedClientId) { + throw new NotFoundException(ResponseMessages.organisation.error.invalidClientCredentials); + } + const decryptedSecret = await this.commonService.decryptPassword(encryptedClientSecret); + if (!decryptedSecret) { + throw new NotFoundException(ResponseMessages.organisation.error.invalidClientCredentials); + } + + // Guard clauses for validation + if (generateTokenDetails.clientAlias.toLowerCase() !== decryptedClientId.toLowerCase()) { + throw new NotFoundException(ResponseMessages.organisation.error.invalidClientCredentials); } - // Step:1 generate the token using admin credentials + if (generateTokenDetails.clientSecret !== decryptedSecret) { + throw new NotFoundException(ResponseMessages.organisation.error.invalidClientCredentials); + } + + // Generate admin token const adminTokenDetails = await this.clientRegistrationService.generateTokenUsingAdminCredentials(generateTokenDetails); if (!adminTokenDetails?.access_token) { throw new InternalServerErrorException(ResponseMessages.organisation.error.adminTokenDetails); } - // Step:2 Call API to fetch client id and secret + + // Fetch client details const clientDetails = await this.clientRegistrationService.fetchClientDetails( generateTokenDetails.orgId, adminTokenDetails.access_token ); - if (!clientDetails || 0 === clientDetails.length) { + if (!clientDetails?.length) { throw new NotFoundException(ResponseMessages.organisation.error.clientDetails); } + + // Authenticate client const { secret } = clientDetails[0]; - //step3: generate the token using client id and secret const authenticationResult = await this.authenticateClientKeycloak(generateTokenDetails.orgId, secret); + return { token: authenticationResult.access_token }; } catch (error) { - this.logger.error(`in generating issuer api token : ${JSON.stringify(error)}`); - throw new RpcException(error.response ? error.response : error); + this.logger.error(`in generating issuer api token: ${JSON.stringify(error)}`); + throw new RpcException(error.response || error); } } } diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 796d8e2a0..8be466d1c 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -133,7 +133,7 @@ export const ResponseMessages = { MaximumOrgsLimit: 'Limit reached: You can be associated with or create maximum 10 organizations.', adminTokenDetails: 'Error in generating admin token details', clientDetails: 'Error in fetching client details', - missMachingAlias: 'Client alias does not match the registered alias' + invalidClientCredentials: 'Invalid client credentials' } }, From 598d1820d206a73c2c8b5d072542390b719e4e05 Mon Sep 17 00:00:00 2001 From: shitrerohit Date: Tue, 30 Sep 2025 13:10:21 +0530 Subject: [PATCH 11/12] fix:removed commented code Signed-off-by: shitrerohit --- .husky/pre-push | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.husky/pre-push b/.husky/pre-push index 67f8a6572..24dc6b378 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -10,9 +10,6 @@ RESET="\033[0m" echo "🔒 Checking commit signatures..." -# Get the email of the current user -# CURRENT_EMAIL=$(git config user.email) - while read local_ref local_sha remote_ref remote_sha; do # Determine range of commits if [ "$remote_sha" = "0000000000000000000000000000000000000000" ]; then @@ -32,13 +29,6 @@ while read local_ref local_sha remote_ref remote_sha; do continue fi - # Skip commits not authored by the current user - # author_email=$(git log -1 --format='%ae' $commit) - # if [ "$author_email" != "$CURRENT_EMAIL" ]; then - # echo "${CYAN}â„šī¸ Skipping commit $commit authored by $author_email${RESET}" - # continue - # fi - # Currently only check for "N" - for no signature. if [ "$sig_status" = "N" ]; then unsigned_commits="$unsigned_commits $commit" From 42eca1142c062661f2535f28965b5bb3413cbe85 Mon Sep 17 00:00:00 2001 From: shitrerohit Date: Tue, 30 Sep 2025 13:15:29 +0530 Subject: [PATCH 12/12] fix:resolved PR comments Signed-off-by: shitrerohit --- .husky/pre-push | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.husky/pre-push b/.husky/pre-push index 24dc6b378..ccf05aa56 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -28,7 +28,7 @@ while read local_ref local_sha remote_ref remote_sha; do echo "${CYAN}â„šī¸ Skipping merge commit $commit${RESET}" continue fi - + sig_status=$(git log --format='%G?' -n 1 $commit) # Currently only check for "N" - for no signature. if [ "$sig_status" = "N" ]; then unsigned_commits="$unsigned_commits $commit" @@ -56,7 +56,7 @@ while read local_ref local_sha remote_ref remote_sha; do --exec 'if [ $(git rev-list --parents -n 1 HEAD | awk "{print NF-1}") -eq 1 ]; then git commit --amend -sS --no-edit; else echo "Skipping merge commit $(git rev-parse --short HEAD)"; fi' echo "${GREEN}✅ Earliest unsigned commit has been signed.${RESET}" - echo "Please push again: git push" + echo "Please push again: git push --force-with-lease" exit 1 else echo "${YELLOW}You can manually sign commits using:${RESET}" @@ -88,4 +88,4 @@ while read local_ref local_sha remote_ref remote_sha; do fi done -echo "${GREEN}✅ All commits are signed and verified${RESET}" \ No newline at end of file +echo "${GREEN}✅ No unsigned commits found${RESET}" \ No newline at end of file