mirror of
https://github.com/SyncrowIOT/backend.git
synced 2025-07-10 15:17:41 +00:00
finished client endpoints
This commit is contained in:
@ -77,21 +77,28 @@ export class AuthService {
|
||||
return await this.sessionRepository.save(data);
|
||||
}
|
||||
|
||||
async getTokens(payload) {
|
||||
async getTokens(
|
||||
payload,
|
||||
isRefreshToken = true,
|
||||
accessTokenExpiry = '24h',
|
||||
refreshTokenExpiry = '30d',
|
||||
) {
|
||||
const [accessToken, refreshToken] = await Promise.all([
|
||||
this.jwtService.signAsync(payload, {
|
||||
secret: this.configService.get<string>('JWT_SECRET'),
|
||||
expiresIn: '24h',
|
||||
}),
|
||||
this.jwtService.signAsync(payload, {
|
||||
secret: this.configService.get<string>('JWT_SECRET'),
|
||||
expiresIn: '7d',
|
||||
expiresIn: accessTokenExpiry,
|
||||
}),
|
||||
isRefreshToken
|
||||
? this.jwtService.signAsync(payload, {
|
||||
secret: this.configService.get<string>('JWT_SECRET'),
|
||||
expiresIn: refreshTokenExpiry,
|
||||
})
|
||||
: null,
|
||||
]);
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken,
|
||||
...(isRefreshToken ? { refreshToken } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,17 @@
|
||||
export class ControllerRoute {
|
||||
static CLIENT = class {
|
||||
public static readonly ROUTE = 'client';
|
||||
|
||||
static ACTIONS = class {
|
||||
public static readonly REGISTER_NEW_CLIENT_SUMMARY =
|
||||
'Register a new client';
|
||||
public static readonly REGISTER_NEW_CLIENT_DESCRIPTION =
|
||||
'This endpoint registers a new client in the system.';
|
||||
public static readonly LOGIN_CLIENT_SUMMARY = 'Login a client';
|
||||
public static readonly LOGIN_CLIENT_DESCRIPTION =
|
||||
'This endpoint allows a client to log in to the system.';
|
||||
};
|
||||
};
|
||||
static PROJECT = class {
|
||||
public static readonly ROUTE = 'projects';
|
||||
static ACTIONS = class {
|
||||
|
@ -1,17 +1,18 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module';
|
||||
import { CommonModule } from '../../libs/common/src';
|
||||
import { UserAuthController } from './controllers';
|
||||
import { UserAuthService } from './services';
|
||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
||||
import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository';
|
||||
import { UserOtpRepository } from '@app/common/modules/user/repositories';
|
||||
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
|
||||
import { RoleService } from 'src/role/services';
|
||||
import { UserAuthController } from './controllers';
|
||||
import { UserAuthService } from './services';
|
||||
import { AuthService } from '@app/common/auth/services/auth.service';
|
||||
import { EmailService } from '@app/common/util/email.service';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule, UserRepositoryModule, CommonModule],
|
||||
imports: [ConfigModule],
|
||||
controllers: [UserAuthController],
|
||||
providers: [
|
||||
UserAuthService,
|
||||
@ -20,6 +21,9 @@ import { RoleService } from 'src/role/services';
|
||||
UserOtpRepository,
|
||||
RoleTypeRepository,
|
||||
RoleService,
|
||||
AuthService,
|
||||
EmailService,
|
||||
JwtService,
|
||||
],
|
||||
exports: [UserAuthService],
|
||||
})
|
||||
|
@ -19,6 +19,7 @@ import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard';
|
||||
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
|
||||
import { OtpType } from '@app/common/constants/otp-type.enum';
|
||||
import { ControllerRoute } from '@app/common/constants/controller-route';
|
||||
import { ClientGuard } from 'src/guards/client.guard';
|
||||
|
||||
@Controller({
|
||||
version: EnableDisableStatusEnum.ENABLED,
|
||||
@ -29,6 +30,8 @@ export class UserAuthController {
|
||||
constructor(private readonly userAuthService: UserAuthService) {}
|
||||
|
||||
@ResponseMessage('User Registered Successfully')
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(ClientGuard)
|
||||
@Post('user/signup')
|
||||
@ApiOperation({
|
||||
summary: ControllerRoute.AUTHENTICATION.ACTIONS.SIGN_UP_SUMMARY,
|
||||
|
@ -3,11 +3,22 @@ import { ClientController } from './controllers';
|
||||
import { ClientService } from './services';
|
||||
import { ClientRepositoryModule } from '@app/common/modules/client/client.repository.module';
|
||||
import { ClientRepository } from '@app/common/modules/client/repositories';
|
||||
import { AuthService } from '@app/common/auth/services/auth.service';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { UserRepository } from '@app/common/modules/user/repositories';
|
||||
import { UserSessionRepository } from '@app/common/modules/session/repositories/session.repository';
|
||||
|
||||
@Module({
|
||||
imports: [ClientRepositoryModule],
|
||||
controllers: [ClientController],
|
||||
providers: [ClientService, ClientRepository],
|
||||
providers: [
|
||||
ClientService,
|
||||
ClientRepository,
|
||||
AuthService,
|
||||
JwtService,
|
||||
UserRepository,
|
||||
UserSessionRepository,
|
||||
],
|
||||
exports: [ClientService],
|
||||
})
|
||||
export class ClientModule {}
|
||||
|
@ -1,15 +1,33 @@
|
||||
import { Controller, Post, Body } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { RegisterClientDto } from '../dtos/register-client.dto';
|
||||
import { ClientService } from '../services';
|
||||
import { ControllerRoute } from '@app/common/constants/controller-route';
|
||||
import { ClientTokenDto } from '../dtos/token-client.dto';
|
||||
import { EnableDisableStatusEnum } from '@app/common/constants/days.enum';
|
||||
|
||||
@ApiTags('OAuth Clients')
|
||||
@Controller('clients')
|
||||
@Controller({
|
||||
version: EnableDisableStatusEnum.ENABLED,
|
||||
path: ControllerRoute.CLIENT.ROUTE,
|
||||
})
|
||||
export class ClientController {
|
||||
constructor(private readonly clientService: ClientService) {}
|
||||
|
||||
@Post('register')
|
||||
@ApiOperation({
|
||||
summary: ControllerRoute.CLIENT.ACTIONS.REGISTER_NEW_CLIENT_SUMMARY,
|
||||
description: ControllerRoute.CLIENT.ACTIONS.REGISTER_NEW_CLIENT_DESCRIPTION,
|
||||
})
|
||||
async registerClient(@Body() dto: RegisterClientDto) {
|
||||
return this.clientService.registerClient(dto);
|
||||
}
|
||||
@Post('token')
|
||||
@ApiOperation({
|
||||
summary: ControllerRoute.CLIENT.ACTIONS.LOGIN_CLIENT_SUMMARY,
|
||||
description: ControllerRoute.CLIENT.ACTIONS.LOGIN_CLIENT_DESCRIPTION,
|
||||
})
|
||||
async login(@Body() dto: ClientTokenDto) {
|
||||
return this.clientService.loginWithClientCredentials(dto);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1 @@
|
||||
export * from './add.community.dto';
|
||||
export * from './project.param.dto';
|
||||
export * from './get.community.dto';
|
||||
export * from './register-client.dto';
|
||||
|
22
src/client/dtos/token-client.dto.ts
Normal file
22
src/client/dtos/token-client.dto.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class ClientTokenDto {
|
||||
@ApiProperty({
|
||||
example: 'abcd1234xyz',
|
||||
description: 'The client ID of the client',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
clientId: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: 'secureSecret123',
|
||||
description: 'The client secret of the client',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
clientSecret: string;
|
||||
}
|
@ -1,18 +1,37 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { HttpStatus, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import * as crypto from 'crypto';
|
||||
import { RegisterClientDto } from '../dtos/register-client.dto';
|
||||
import { ClientEntity } from '@app/common/modules/client/entities';
|
||||
import { ClientTokenDto } from '../dtos/token-client.dto';
|
||||
import { AuthService } from '@app/common/auth/services/auth.service';
|
||||
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ClientService {
|
||||
constructor(
|
||||
@InjectRepository(ClientEntity)
|
||||
private clientRepository: Repository<ClientEntity>,
|
||||
private readonly authService: AuthService,
|
||||
) {}
|
||||
|
||||
async loginWithClientCredentials(dto: ClientTokenDto) {
|
||||
const client = await this.validateClient(dto.clientId, dto.clientSecret);
|
||||
const payload = {
|
||||
client: {
|
||||
clientId: client.clientId,
|
||||
uuid: client.uuid,
|
||||
scopes: client.scopes,
|
||||
},
|
||||
};
|
||||
const tokens = await this.authService.getTokens(payload, false, '5m');
|
||||
return new SuccessResponseDto({
|
||||
message: `Client logged in successfully`,
|
||||
data: tokens,
|
||||
statusCode: HttpStatus.CREATED,
|
||||
});
|
||||
}
|
||||
async registerClient(dto: RegisterClientDto) {
|
||||
const clientId = crypto.randomBytes(16).toString('hex');
|
||||
const clientSecret = crypto.randomBytes(32).toString('hex');
|
||||
@ -27,7 +46,11 @@ export class ClientService {
|
||||
|
||||
await this.clientRepository.save(client);
|
||||
|
||||
return { clientId, clientSecret };
|
||||
return new SuccessResponseDto({
|
||||
message: `Client registered successfully`,
|
||||
data: { clientId, clientSecret },
|
||||
statusCode: HttpStatus.CREATED,
|
||||
});
|
||||
}
|
||||
|
||||
async validateClient(
|
||||
|
49
src/guards/client.guard.ts
Normal file
49
src/guards/client.guard.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { UnauthorizedException } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
|
||||
export class ClientGuard extends AuthGuard('jwt') {
|
||||
handleRequest(err, user, info, context) {
|
||||
if (err || !user) {
|
||||
throw err || new UnauthorizedException();
|
||||
}
|
||||
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const token = this.extractTokenFromHeader(request);
|
||||
|
||||
if (!token) {
|
||||
throw new UnauthorizedException('Token not found');
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.decode(token);
|
||||
if (!this.validateToken(decoded)) {
|
||||
throw new UnauthorizedException('Invalid token');
|
||||
}
|
||||
} catch (err) {
|
||||
throw new UnauthorizedException('Invalid token');
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private extractTokenFromHeader(request): string | null {
|
||||
const authHeader = request.headers.authorization;
|
||||
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||
return authHeader.split(' ')[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private validateToken(decoded: any): boolean {
|
||||
if (
|
||||
decoded &&
|
||||
decoded.client &&
|
||||
decoded.client.clientId &&
|
||||
decoded.client.uuid
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user