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 })
@Length(11, 11, { message: AuthMessage.PHONE_SHOULD_BE_11_DIGIT })
@IsMobilePhone("fa-IR", {}, { message: AuthMessage.INVALID_PHONE_FORMAT })
@ApiProperty({ description: "phone number", default: "09922320740" })
@ApiProperty({ description: "phone number", default: "09185290775" })
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 { ForgetPasswordDto } from "./DTO/forget-password.dto";
import { VerifyForgotPasswordOtpDto } from "./DTO/verify-forgot-otp.dto";
import { VerifyResellerOtpDto } from "./DTO/verify-reseller-otp.dto";
@ApiTags("Auth")
@Controller("auth")
@@ -80,7 +81,7 @@ export class AuthController {
@ApiOperation({ summary: "verify otp for login" })
@HttpCode(HttpStatus.OK)
@Post("otp/verify/reseller")
resellerVerifyOtp(@Body() verifyOtpDto: VerifyOtpDto) {
resellerVerifyOtp(@Body() verifyOtpDto: VerifyResellerOtpDto) {
return this.authService.resellerVerifyLoginOtp(verifyOtpDto);
}
+1 -3
View File
@@ -16,15 +16,13 @@ export class ResellerRouteGuard implements CanActivate {
if (!requiredReseller) return true;
console.log('hi')
const req = context.switchToHttp().getRequest<FastifyRequest>();
const user = req.user;
console.log(user)
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;
}
@@ -1,10 +1,11 @@
import { PermissionEnum } from "../../users/enums/permission.enum";
import { ResellerType } from "../DTO/verify-reseller-otp.dto";
// import { RoleEnum } from "../../users/enums/role.enum";
export interface ITokenPayload {
id: string;
isAdmin: boolean;
isReseller: boolean;
resellerType?: ResellerType;
permissions: PermissionEnum[];
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 { EmailService } from "../../utils/providers/email.service";
import { User } from "../../users/entities/user.entity";
import { ResellerType, VerifyResellerOtpDto } from "../DTO/verify-reseller-otp.dto";
@Injectable()
export class AuthService {
@@ -115,8 +116,14 @@ export class AuthService {
async requestLoginOtpReseller(requestOtpDto: 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");
if (existCode) {
@@ -227,15 +234,18 @@ export class AuthService {
}
//****************** */
async resellerVerifyLoginOtp(verifyOtpDto: VerifyOtpDto) {
const { code, phone } = verifyOtpDto;
async resellerVerifyLoginOtp(verifyOtpDto: VerifyResellerOtpDto) {
const { code, phone, type } = verifyOtpDto;
await this.checkResellerOtpWithPhone(phone, code)
const { agent, reseller } = await this.checkAgentExistWithPhone(phone);
let user: User | null = null
const user: User | null = agent ?? reseller
if (type == ResellerType.agent) {
user = await this.usersService.getAgentByUserPhone(phone)
} else if (type == ResellerType.reseller) {
user = await this.usersService.getResellerByUserPhone(phone)
}
if (!user) {
throw new BadRequestException(AuthMessage.USER_NOT_FOUND)
@@ -243,7 +253,7 @@ export class AuthService {
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 {
message: AuthMessage.LOGIN_SUCCESS,
@@ -310,20 +320,7 @@ export class AuthService {
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) {
@@ -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
const payload: ITokenPayload = {
id: user.id,
isReseller:false,
isAdmin: user.roles.some((r) => r.isAdmin),
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
audience: clientId,
@@ -106,7 +105,6 @@ export class SSOService {
const payload: ITokenPayload = {
id: user.id,
isAdmin: user.roles.some((r) => r.isAdmin),
isReseller:false,
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
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 { RefreshTokensRepository } from "../../users/repositories/refresh-token.repository";
import { ITokenPayload } from "../interfaces/IToken-payload";
import { ResellerType } from "../DTO/verify-reseller-otp.dto";
@Injectable()
export class TokensService {
@@ -27,19 +28,18 @@ export class TokensService {
{
id: user.id,
isAdmin: user.roles.some((r) => r.isAdmin),
isReseller: false,
permissions: user.roles.flatMap((r) => (r?.permissions?.length ? r.permissions.map((p) => p.name) : [])),
},
queryRunner,
);
}
// -----------------------------
async generateTokensForReseller(user: User, queryRunner?: QueryRunner) {
async generateTokensForReseller(user: User, resellerType: ResellerType, queryRunner?: QueryRunner) {
return this.generateAccessAndRefreshToken(
{
id: user.id,
isAdmin: false,
isReseller: true,
resellerType,
permissions: [],
},
queryRunner,
+6 -1
View File
@@ -21,6 +21,11 @@ export class JwtStrategy extends PassportStrategy(Strategy, JWT_STRATEGY_NAME) {
}
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
};
}
}