Skip to content

Commit c7babde

Browse files
committed
Tenant id Handling in URl
1 parent aaf0260 commit c7babde

3 files changed

Lines changed: 126 additions & 14 deletions

File tree

src/sso/dto/sso-request.dto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export class SsoRequestDto {
3131
})
3232
@Expose()
3333
@IsOptional()
34-
@IsUUID()
34+
@IsUUID(undefined, { message: 'Please enter valid Tenant UUID' })
3535
tenantId?: string;
3636

3737
@ApiPropertyOptional({

src/sso/sso.controller.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Controller, Post, Body, Res, HttpStatus } from '@nestjs/common';
1+
import { Controller, Post, Body, Res, HttpStatus, HttpException } from '@nestjs/common';
22
import { ApiTags, ApiOperation, ApiResponse, ApiBody } from '@nestjs/swagger';
33
import { Response } from 'express';
44
import { SsoService } from './sso.service';
@@ -74,12 +74,30 @@ export class SsoController {
7474
return response.status(HttpStatus.OK).json(result);
7575

7676
} catch (error) {
77+
// Properly handle HttpException to preserve status codes
78+
if (error instanceof HttpException) {
79+
const status = error.getStatus();
80+
const errorResponse = error.getResponse();
81+
const message = typeof errorResponse === 'string'
82+
? errorResponse
83+
: (errorResponse as any)?.message || error.message;
84+
85+
return APIResponse.error(
86+
response,
87+
APIID.SSO_AUTHENTICATE,
88+
message || 'SSO authentication failed',
89+
error.name || 'BAD_REQUEST',
90+
status
91+
);
92+
}
93+
94+
// Fallback for non-HttpException errors
7795
return APIResponse.error(
7896
response,
7997
APIID.SSO_AUTHENTICATE,
8098
error.message || 'SSO authentication failed',
81-
error.error || 'INTERNAL_SERVER_ERROR',
82-
error.statusCode || HttpStatus.INTERNAL_SERVER_ERROR
99+
'INTERNAL_SERVER_ERROR',
100+
HttpStatus.INTERNAL_SERVER_ERROR
83101
);
84102
}
85103
}

src/sso/sso.service.ts

Lines changed: 104 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,15 @@ export class SsoService {
7474
try {
7575
this.logger.log(`Starting SSO authentication for provider: ${ssoRequestDto.ssoProvider}`, 'SSO_SERVICE');
7676

77-
// Step 1: Apply default values if not provided
78-
if (!ssoRequestDto.tenantId) {
77+
// Step 1: Validate tenantId if provided, otherwise apply default
78+
if (ssoRequestDto.tenantId) {
79+
// Validate UUID format
80+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
81+
if (!uuidRegex.test(ssoRequestDto.tenantId)) {
82+
throw new HttpException('Please enter valid Tenant UUID In URL', HttpStatus.BAD_REQUEST);
83+
}
84+
this.logger.log(`Using provided tenant ID: ${ssoRequestDto.tenantId}`, 'SSO_SERVICE');
85+
} else {
7986
ssoRequestDto.tenantId = SSO_DEFAULTS.DEFAULT_TENANT_ID;
8087
this.logger.log(`Using default tenant ID: ${SSO_DEFAULTS.DEFAULT_TENANT_ID}`, 'SSO_SERVICE');
8188
}
@@ -304,6 +311,9 @@ export class SsoService {
304311

305312
//Map User to Role and Tenant
306313

314+
// Sanitize userId for use in custom fields
315+
const sanitizedNewtonUserId = this.sanitizeUUID(newtonResponse.userId) || createdUser.userId;
316+
307317
// Update custom fields if newtonData is available
308318
if (newtonResponse.newtonData && Object.keys(newtonResponse.newtonData).length > 0) {
309319
for (const [fieldLabel, fieldValue] of Object.entries(newtonResponse.newtonData)) {
@@ -318,8 +328,8 @@ export class SsoService {
318328
{
319329
tenantId: ssoRequestDto.tenantId,
320330
contextType: "USER",
321-
createdBy: newtonResponse.userId,
322-
updatedBy: newtonResponse.userId
331+
createdBy: sanitizedNewtonUserId,
332+
updatedBy: sanitizedNewtonUserId
323333
}
324334
);
325335
}
@@ -354,6 +364,45 @@ export class SsoService {
354364
}
355365
}
356366

367+
/**
368+
* Sanitize and validate UUID by removing invalid characters
369+
* @param uuid - UUID string that may contain invalid characters
370+
* @returns Sanitized UUID string or null if invalid
371+
*/
372+
private sanitizeUUID(uuid: string | undefined | null): string | null {
373+
if (!uuid || typeof uuid !== 'string') {
374+
return null;
375+
}
376+
377+
// Remove any trailing/leading whitespace and invalid characters
378+
// UUID format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
379+
// Remove any characters that are not valid UUID characters (hex digits and hyphens)
380+
let sanitized = uuid.trim();
381+
382+
// Remove any trailing special characters (like asterisks, spaces, etc.)
383+
sanitized = sanitized.replace(/[^a-fA-F0-9-]+$/, '');
384+
385+
// Remove any leading special characters
386+
sanitized = sanitized.replace(/^[^a-fA-F0-9]+/, '');
387+
388+
// Remove any invalid characters within the UUID (keep only hex digits and hyphens)
389+
sanitized = sanitized.replace(/[^a-fA-F0-9-]/g, '');
390+
391+
// Validate UUID format: should be 8-4-4-4-12 hex digits separated by hyphens
392+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
393+
394+
if (uuidRegex.test(sanitized)) {
395+
return sanitized;
396+
}
397+
398+
this.logger.warn(
399+
`Invalid UUID format after sanitization: ${uuid} -> ${sanitized}`,
400+
'SSO_SERVICE'
401+
);
402+
403+
return null;
404+
}
405+
357406
/**
358407
* Map Newton API response to UserCreateDto with name parsing
359408
* @param newtonResponse - Newton API response
@@ -374,8 +423,31 @@ export class SsoService {
374423
EMAIL: newtonResponse.email || ''
375424
};
376425

426+
// Sanitize userId from Newton response or userData
427+
const rawUserId = newtonResponse.userId || userData.USER_ID;
428+
const sanitizedUserId = this.sanitizeUUID(rawUserId);
429+
430+
if (!sanitizedUserId) {
431+
this.logger.error(
432+
`Invalid userId received from Newton API: ${rawUserId}. Cannot create user.`,
433+
'SSO_SERVICE'
434+
);
435+
throw new HttpException(
436+
`Invalid user ID format received from authentication service: ${rawUserId}`,
437+
HttpStatus.BAD_REQUEST
438+
);
439+
}
440+
441+
// Log if sanitization changed the value
442+
if (rawUserId !== sanitizedUserId) {
443+
this.logger.warn(
444+
`UserId sanitized: "${rawUserId}" -> "${sanitizedUserId}"`,
445+
'SSO_SERVICE'
446+
);
447+
}
448+
377449
return {
378-
userId: newtonResponse.userId || userData.USER_ID,
450+
userId: sanitizedUserId,
379451
username: newtonResponse.email || userData.EMAIL,
380452
firstName: firstName,
381453
middleName: undefined,
@@ -391,8 +463,8 @@ export class SsoService {
391463
password: undefined, // No password for SSO users
392464
createdAt: new Date().toISOString(),
393465
updatedAt: new Date().toISOString(),
394-
createdBy: newtonResponse.userId, // Use the Newton user ID as creator
395-
updatedBy: newtonResponse.userId, // Use the Newton user ID as updater
466+
createdBy: sanitizedUserId, // Use the sanitized Newton user ID as creator
467+
updatedBy: sanitizedUserId, // Use the sanitized Newton user ID as updater
396468
customFields: [],
397469
automaticMember: undefined,
398470
tenantCohortRoleMapping: [{
@@ -527,18 +599,40 @@ export class SsoService {
527599

528600

529601
// Use decoded userId if available, otherwise fallback to newtonResponse.userId or ssoRequestDto.userId
530-
const userId = newtonResponse.userId || ssoRequestDto.userId;
602+
const rawUserId = newtonResponse.userId || ssoRequestDto.userId;
531603

532-
if (!userId) {
604+
if (!rawUserId) {
533605
this.logger.error('No userId available for existing user', 'SSO_SERVICE');
534606
throw new HttpException(
535607
'Unable to determine user ID for existing user',
536608
HttpStatus.BAD_REQUEST
537609
);
538610
}
539611

612+
// Sanitize userId to remove any invalid characters
613+
const userId = this.sanitizeUUID(rawUserId);
614+
615+
if (!userId) {
616+
this.logger.error(
617+
`Invalid userId format for existing user: ${rawUserId}`,
618+
'SSO_SERVICE'
619+
);
620+
throw new HttpException(
621+
`Invalid user ID format: ${rawUserId}`,
622+
HttpStatus.BAD_REQUEST
623+
);
624+
}
625+
626+
// Log if sanitization changed the value
627+
if (rawUserId !== userId) {
628+
this.logger.warn(
629+
`UserId sanitized for existing user: "${rawUserId}" -> "${userId}"`,
630+
'SSO_SERVICE'
631+
);
632+
}
633+
540634
this.logger.log(
541-
`Processing existing user: ${userId})`,
635+
`Processing existing user: ${userId}`,
542636
'SSO_SERVICE'
543637
);
544638

0 commit comments

Comments
 (0)