auth : update

This commit is contained in:
2026-04-15 14:22:52 +03:30
parent 86c5e87e8b
commit 232d2ec072
9 changed files with 61 additions and 36 deletions
+1 -1
View File
@@ -7,6 +7,6 @@ export class RequestOtpDto {
@IsNotEmpty({ message: AuthMessage.PHONE_NOT_EMPTY }) @IsNotEmpty({ message: AuthMessage.PHONE_NOT_EMPTY })
@Length(11, 11, { message: AuthMessage.PHONE_SHOULD_BE_11_DIGIT }) @Length(11, 11, { message: AuthMessage.PHONE_SHOULD_BE_11_DIGIT })
@IsMobilePhone("fa-IR", {}, { message: AuthMessage.INVALID_PHONE_FORMAT }) @IsMobilePhone("fa-IR", {}, { message: AuthMessage.INVALID_PHONE_FORMAT })
@ApiProperty({ description: "phone number", default: "09922320740" }) @ApiProperty({ description: "phone number", default: "09185290775" })
phone: string; phone: string;
} }
+27
View File
@@ -0,0 +1,27 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsMobilePhone, IsNotEmpty, IsNumberString, IsEnum, Length } from "class-validator";
import { AuthMessage } from "../../../common/enums/message.enum";
export enum ResellerType {
agent = 'agent',
reseller = 'reseller'
}
export class VerifyResellerOtpDto {
@IsNotEmpty({ message: AuthMessage.PHONE_NOT_EMPTY })
@Length(11, 11, { message: AuthMessage.PHONE_SHOULD_BE_11_DIGIT })
@IsMobilePhone("fa-IR", {}, { message: AuthMessage.INVALID_PHONE_FORMAT })
@ApiProperty({ description: "phone number", default: "09185290775" })
phone: string;
@ApiProperty({ description: "OTP code received via SMS", example: "" })
@IsNotEmpty({ message: AuthMessage.OTP_NOT_EMPTY })
@IsNumberString(undefined, { message: AuthMessage.OTP_FORMAT_INVALID })
@Length(5, 5, { message: AuthMessage.OTP_FORMAT_INVALID })
code: string;
@ApiProperty({ enum: ResellerType })
@IsEnum(ResellerType)
type: ResellerType;
}
+2 -1
View File
@@ -16,6 +16,7 @@ import { UserDec } from "../../common/decorators/user.decorator";
import { User } from "../users/entities/user.entity"; import { User } from "../users/entities/user.entity";
import { ForgetPasswordDto } from "./DTO/forget-password.dto"; import { ForgetPasswordDto } from "./DTO/forget-password.dto";
import { VerifyForgotPasswordOtpDto } from "./DTO/verify-forgot-otp.dto"; import { VerifyForgotPasswordOtpDto } from "./DTO/verify-forgot-otp.dto";
import { VerifyResellerOtpDto } from "./DTO/verify-reseller-otp.dto";
@ApiTags("Auth") @ApiTags("Auth")
@Controller("auth") @Controller("auth")
@@ -80,7 +81,7 @@ export class AuthController {
@ApiOperation({ summary: "verify otp for login" }) @ApiOperation({ summary: "verify otp for login" })
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Post("otp/verify/reseller") @Post("otp/verify/reseller")
resellerVerifyOtp(@Body() verifyOtpDto: VerifyOtpDto) { resellerVerifyOtp(@Body() verifyOtpDto: VerifyResellerOtpDto) {
return this.authService.resellerVerifyLoginOtp(verifyOtpDto); return this.authService.resellerVerifyLoginOtp(verifyOtpDto);
} }
+1 -3
View File
@@ -16,15 +16,13 @@ export class ResellerRouteGuard implements CanActivate {
if (!requiredReseller) return true; if (!requiredReseller) return true;
console.log('hi')
const req = context.switchToHttp().getRequest<FastifyRequest>(); const req = context.switchToHttp().getRequest<FastifyRequest>();
const user = req.user; const user = req.user;
console.log(user)
if (!user) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS); if (!user) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS);
// if (!user.isReseller) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS); if (!user.resellerType) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS);
return true; return true;
} }
@@ -1,10 +1,11 @@
import { PermissionEnum } from "../../users/enums/permission.enum"; import { PermissionEnum } from "../../users/enums/permission.enum";
import { ResellerType } from "../DTO/verify-reseller-otp.dto";
// import { RoleEnum } from "../../users/enums/role.enum"; // import { RoleEnum } from "../../users/enums/role.enum";
export interface ITokenPayload { export interface ITokenPayload {
id: string; id: string;
isAdmin: boolean; isAdmin: boolean;
isReseller: boolean; resellerType?: ResellerType;
permissions: PermissionEnum[]; permissions: PermissionEnum[];
audience?: string; // Client application identifier for SSO audience?: string; // Client application identifier for SSO
} }
+19 -24
View File
@@ -21,6 +21,7 @@ import { ForgetPasswordDto } from "../DTO/forget-password.dto";
import { VerifyForgotPasswordOtpDto } from "../DTO/verify-forgot-otp.dto"; import { VerifyForgotPasswordOtpDto } from "../DTO/verify-forgot-otp.dto";
import { EmailService } from "../../utils/providers/email.service"; import { EmailService } from "../../utils/providers/email.service";
import { User } from "../../users/entities/user.entity"; import { User } from "../../users/entities/user.entity";
import { ResellerType, VerifyResellerOtpDto } from "../DTO/verify-reseller-otp.dto";
@Injectable() @Injectable()
export class AuthService { export class AuthService {
@@ -115,8 +116,14 @@ export class AuthService {
async requestLoginOtpReseller(requestOtpDto: RequestOtpDto) { async requestLoginOtpReseller(requestOtpDto: RequestOtpDto) {
const { phone } = requestOtpDto; const { phone } = requestOtpDto;
// check if agent with this phone exist or not
await this.checkAgentExistWithPhone(phone) let user: User | null = null
user = (await this.usersService.getAgentByUserPhone(phone)) ?? (await this.usersService.getResellerByUserPhone(phone))
if (!user) {
throw new BadRequestException(AuthMessage.USER_NOT_FOUND)
}
const existCode = await this.otpService.checkExistOtp(phone, "LOGIN_RESELLER_AGENT"); const existCode = await this.otpService.checkExistOtp(phone, "LOGIN_RESELLER_AGENT");
if (existCode) { if (existCode) {
@@ -227,15 +234,18 @@ export class AuthService {
} }
//****************** */ //****************** */
async resellerVerifyLoginOtp(verifyOtpDto: VerifyOtpDto) { async resellerVerifyLoginOtp(verifyOtpDto: VerifyResellerOtpDto) {
const { code, phone } = verifyOtpDto; const { code, phone, type } = verifyOtpDto;
await this.checkResellerOtpWithPhone(phone, code) await this.checkResellerOtpWithPhone(phone, code)
const { agent, reseller } = await this.checkAgentExistWithPhone(phone); let user: User | null = null
if (type == ResellerType.agent) {
const user: User | null = agent ?? reseller user = await this.usersService.getAgentByUserPhone(phone)
} else if (type == ResellerType.reseller) {
user = await this.usersService.getResellerByUserPhone(phone)
}
if (!user) { if (!user) {
throw new BadRequestException(AuthMessage.USER_NOT_FOUND) throw new BadRequestException(AuthMessage.USER_NOT_FOUND)
@@ -243,7 +253,7 @@ export class AuthService {
if (user.deletedAt) throw new BadRequestException(AuthMessage.ACCESS_DENIED); if (user.deletedAt) throw new BadRequestException(AuthMessage.ACCESS_DENIED);
const tokens = await this.tokensService.generateTokensForReseller(user); const tokens = await this.tokensService.generateTokensForReseller(user, type);
return { return {
message: AuthMessage.LOGIN_SUCCESS, message: AuthMessage.LOGIN_SUCCESS,
@@ -310,20 +320,7 @@ export class AuthService {
return true; return true;
} }
//****************** */
//****************** */
private async checkAgentExistWithPhone(phone: string) {
const { reseller } = await this.usersService.getResellerByUserPhone(phone);
const { agent } = await this.usersService.getAgentByUserPhone(phone);
if (!reseller && !agent) {
throw new BadRequestException(AuthMessage.USER_NOT_FOUND)
}
return { reseller, agent }
}
//****************** */ //****************** */
//****************** */ //****************** */
async requestForgotPasswordOtp(dto: ForgetPasswordDto) { async requestForgotPasswordOtp(dto: ForgetPasswordDto) {
@@ -383,7 +380,5 @@ export class AuthService {
} }
//****************** */ //****************** */
//****************** */ //****************** */
// private checkUserIsAgent(roles: Role[]) {
// return roles.some((role) => role.isAgent);
// }
} }
@@ -45,7 +45,6 @@ export class SSOService {
// Generate token with client-specific audience // Generate token with client-specific audience
const payload: ITokenPayload = { const payload: ITokenPayload = {
id: user.id, id: user.id,
isReseller:false,
isAdmin: user.roles.some((r) => r.isAdmin), isAdmin: user.roles.some((r) => r.isAdmin),
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])), permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
audience: clientId, audience: clientId,
@@ -106,7 +105,6 @@ export class SSOService {
const payload: ITokenPayload = { const payload: ITokenPayload = {
id: user.id, id: user.id,
isAdmin: user.roles.some((r) => r.isAdmin), isAdmin: user.roles.some((r) => r.isAdmin),
isReseller:false,
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])), permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
audience: clientId, audience: clientId,
}; };
+3 -3
View File
@@ -10,6 +10,7 @@ import { AuthMessage } from "../../../common/enums/message.enum";
import { User } from "../../users/entities/user.entity"; import { User } from "../../users/entities/user.entity";
import { RefreshTokensRepository } from "../../users/repositories/refresh-token.repository"; import { RefreshTokensRepository } from "../../users/repositories/refresh-token.repository";
import { ITokenPayload } from "../interfaces/IToken-payload"; import { ITokenPayload } from "../interfaces/IToken-payload";
import { ResellerType } from "../DTO/verify-reseller-otp.dto";
@Injectable() @Injectable()
export class TokensService { export class TokensService {
@@ -27,19 +28,18 @@ export class TokensService {
{ {
id: user.id, id: user.id,
isAdmin: user.roles.some((r) => r.isAdmin), isAdmin: user.roles.some((r) => r.isAdmin),
isReseller: false,
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])), permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
}, },
queryRunner, queryRunner,
); );
} }
// ----------------------------- // -----------------------------
async generateTokensForReseller(user: User, queryRunner?: QueryRunner) { async generateTokensForReseller(user: User, resellerType: ResellerType, queryRunner?: QueryRunner) {
return this.generateAccessAndRefreshToken( return this.generateAccessAndRefreshToken(
{ {
id: user.id, id: user.id,
isAdmin: false, isAdmin: false,
isReseller: true, resellerType,
permissions: [], permissions: [],
}, },
queryRunner, queryRunner,
+6 -1
View File
@@ -21,6 +21,11 @@ export class JwtStrategy extends PassportStrategy(Strategy, JWT_STRATEGY_NAME) {
} }
async validate(payload: ITokenPayload) { async validate(payload: ITokenPayload) {
return { id: payload.id, permissions: payload.permissions, isAdmin: payload.isAdmin }; return {
id: payload.id,
permissions: payload.permissions,
isAdmin: payload.isAdmin,
resellerType: payload.resellerType
};
} }
} }