Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,27 @@

This is how you will write the library
![FLow](diagrams/development.excalidraw.svg)

## Quick Commands

```bash
# Install dependencies
pnpm install

# Build everything
pnpm nx run-many -t build

# Run sample app (dev)
pnpm nx serve sample

# Build specific project
pnpm nx build <project-name>

# Test & Lint
pnpm nx test sample
pnpm nx lint sample

# run the production build
pnpm nx run sample:build:production
node dist/apps/sample/main.js
```
2 changes: 1 addition & 1 deletion libs/extensions/src/dtos/authDto/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ export * from './otp.dto';
export * from './otpLogin.dto';
export * from './password-login.dto';
export * from './reset-password.dto';
export * from './service-auth.dto';
export * from './set-password.dto';
export * from './walletLogin.dto';

45 changes: 45 additions & 0 deletions libs/extensions/src/dtos/authDto/service-auth.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';

/**
* DTO for OAuth2 Client Credentials Flow
* Used by external services (e.g., SMS Bridge) to authenticate
*/
export class ServiceAuthDto {
@ApiProperty({
description: 'Service client ID',
example: 'clx1234567890abcdef',
})
@IsString()
@IsNotEmpty()
clientId: string;

@ApiProperty({
description: 'Service client secret',
example: 'your-secret-key',
})
@IsString()
@IsNotEmpty()
clientSecret: string;
}

/**
* DTO for creating a new service client
*/
export class CreateServiceClientDto {
@ApiProperty({
description: 'Name of the service',
example: 'SMS Bridge',
})
@IsString()
@IsNotEmpty()
name: string;

@ApiProperty({
description: 'Description of the service',
example: 'External SMS gateway bridge service',
required: false,
})
@IsString()
description?: string;
}
2 changes: 1 addition & 1 deletion libs/extensions/src/dtos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './authDto/otp.dto';
export * from './authDto/otpLogin.dto';
export * from './authDto/password-login.dto';
export * from './authDto/reset-password.dto';
export * from './authDto/service-auth.dto';
export * from './authDto/set-password.dto';
export * from './authDto/walletChallenge.dto';
export * from './authDto/walletLogin.dto';
Expand All @@ -22,4 +23,3 @@ export * from './userDto/create-user.dto';
export * from './userDto/update-user.dto';
export * from './userDto/users-get.dto';
export * from './userDto/users-list.dto';

2 changes: 1 addition & 1 deletion libs/user/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rumsan/user",
"version": "3.0.170-rc.3",
"version": "3.0.170-rc.5",
"description": "Rumsan user management library",
"author": "rumsan.com",
"license": "AGPL-3.0",
Expand Down
2 changes: 2 additions & 0 deletions libs/user/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export * from './lib/ability/ability.subjects';
export * from './lib/auths/auths.module';
export * from './lib/auths/auths.service';
export * from './lib/auths/guard';
export * from './lib/auths/interfaces';
export * from './lib/auths/strategy';
export { ACTIONS, ERRORS, EVENTS, SUBJECTS } from './lib/constants';
export * from './lib/roles/roles.module';
export * from './lib/roles/roles.service';
Expand Down
96 changes: 93 additions & 3 deletions libs/user/src/lib/auths/auths.controller.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Param,
Patch,
Post,
Query,
Request,
UseGuards,
} from '@nestjs/common';
import { ApiQuery, ApiTags } from '@nestjs/swagger';
import {
ApiBearerAuth,
ApiOperation,
ApiQuery,
ApiTags,
} from '@nestjs/swagger';
import { RequestDetails } from '@rumsan/extensions/decorators';
import {
ChallengeDto,
ChangePasswordDto,
CreateServiceClientDto,
OtpDto,
OtpLoginDto,
PasswordLoginDto,
ResetPasswordDto,
ServiceAuthDto,
SetPasswordDto,
WalletLoginDto,
} from '@rumsan/extensions/dtos';
Expand Down Expand Up @@ -55,7 +65,10 @@ export class AuthsController {
}

@Post('challenge')
getChallenge(@Body() dto: ChallengeDto, @RequestDetails() rdetails: RequestType) {
getChallenge(
@Body() dto: ChallengeDto,
@RequestDetails() rdetails: RequestType,
) {
return this.authService.getChallengeForWallet(dto, rdetails);
}

Expand Down Expand Up @@ -110,7 +123,8 @@ export class AuthsController {
name: 'service',
enum: Service,
required: true,
description: 'Service type to check password status for (EMAIL, PHONE, USERNAME)',
description:
'Service type to check password status for (EMAIL, PHONE, USERNAME)',
example: 'EMAIL',
})
checkPasswordStatus(
Expand All @@ -119,4 +133,80 @@ export class AuthsController {
) {
return this.authService.hasPassword(user.id, service);
}

// ================== Service Authentication (OAuth2 Client Credentials) ==================

@HttpCode(HttpStatus.OK)
@Post('service/token')
@ApiOperation({
summary: 'Service Authentication',
description:
'OAuth2 Client Credentials Flow - Exchange client_id/client_secret for a Service JWT',
})
authenticateService(@Body() dto: ServiceAuthDto) {
return this.authService.authenticateService(dto);
}

@UseGuards(JwtGuard)
@Post('service/clients')
@ApiBearerAuth()
@ApiOperation({
summary: 'Create Service Client',
description:
'Create a new service client. Returns clientId and secret (shown only once)',
})
createServiceClient(
@CurrentUser() user: CurrentUserInterface,
@Body() dto: CreateServiceClientDto,
) {
return this.authService.createServiceClient(dto, user.id);
}

@UseGuards(JwtGuard)
@Get('service/clients')
@ApiBearerAuth()
@ApiOperation({ summary: 'List all service clients' })
listServiceClients() {
return this.authService.listServiceClients();
}

@UseGuards(JwtGuard)
@Post('service/clients/:clientId/regenerate')
@ApiBearerAuth()
@ApiOperation({
summary: 'Regenerate Service Client Secret',
description:
'Generate a new secret for a service client. Old secret will be invalidated',
})
regenerateServiceClientSecret(@Param('clientId') clientId: string) {
return this.authService.regenerateServiceClientSecret(clientId);
}

@UseGuards(JwtGuard)
@Patch('service/clients/:clientId')
@ApiBearerAuth()
@ApiOperation({ summary: 'Update Service Client Permissions' })
updateServiceClientPermissions(
@Param('clientId') clientId: string,
@Body()
permissions: {
canImpersonate?: boolean;
allowedRoles?: string[];
rateLimit?: number;
isActive?: boolean;
},
) {
return this.authService.updateServiceClientPermissions(
clientId,
permissions,
);
}

@UseGuards(JwtGuard)
@Delete('service/clients/:clientId')
@ApiBearerAuth()
@ApiOperation({ summary: 'Delete Service Client' })
deleteServiceClient(@Param('clientId') clientId: string) {
return this.authService.deleteServiceClient(clientId);
}
}
11 changes: 9 additions & 2 deletions libs/user/src/lib/auths/auths.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@ import { PassportModule } from '@nestjs/passport';
import { PrismaModule } from '@rumsan/prisma';
import { AuthsController } from './auths.controller';
import { AuthsService } from './auths.service';
import { HybridJwtGuard } from './guard/hybrid-jwt.guard';
import { RateLimitService } from './services/rate-limit.service';
import { JwtStrategy, LocalStrategy } from './strategy';

@Module({
imports: [JwtModule.register({}), PrismaModule, PassportModule, ConfigModule],
controllers: [AuthsController],
providers: [AuthsService, JwtStrategy, LocalStrategy, RateLimitService],
exports: [AuthsService],
providers: [
AuthsService,
JwtStrategy,
LocalStrategy,
HybridJwtGuard,
RateLimitService,
],
exports: [AuthsService, HybridJwtGuard, JwtModule, PrismaModule],
})
export class AuthsModule {}
Loading