forked from Yara724/api
Added linkToken and linkContext to OTP
This commit is contained in:
@@ -26,7 +26,10 @@ export class UserAuthController {
|
|||||||
})
|
})
|
||||||
@ApiAcceptedResponse()
|
@ApiAcceptedResponse()
|
||||||
async sendOtpRq(@Body() body: UserLoginDto) {
|
async sendOtpRq(@Body() body: UserLoginDto) {
|
||||||
const res = await this.userAuthService.sendOtpRequest(body.mobile);
|
const res = await this.userAuthService.sendOtpRequest(body.mobile, {
|
||||||
|
linkToken: body.linkToken,
|
||||||
|
linkContext: body.linkContext,
|
||||||
|
});
|
||||||
if (res) {
|
if (res) {
|
||||||
throw new HttpException(res, HttpStatus.ACCEPTED);
|
throw new HttpException(res, HttpStatus.ACCEPTED);
|
||||||
}
|
}
|
||||||
|
|||||||
46
src/auth/auth-services/user-auth-error.ts
Normal file
46
src/auth/auth-services/user-auth-error.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import {
|
||||||
|
BadRequestException,
|
||||||
|
ForbiddenException,
|
||||||
|
UnauthorizedException,
|
||||||
|
} from "@nestjs/common";
|
||||||
|
|
||||||
|
export enum UserAuthErrorCode {
|
||||||
|
USER_NOT_FOUND = "USER_NOT_FOUND",
|
||||||
|
OTP_REQUIRED = "OTP_REQUIRED",
|
||||||
|
OTP_EXPIRED = "OTP_EXPIRED",
|
||||||
|
OTP_INVALID = "OTP_INVALID",
|
||||||
|
OTP_REQUEST_TOO_SOON = "OTP_REQUEST_TOO_SOON",
|
||||||
|
LINK_NOT_FOUND = "LINK_NOT_FOUND",
|
||||||
|
LINK_MOBILE_MISMATCH = "LINK_MOBILE_MISMATCH",
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages: Record<UserAuthErrorCode, string> = {
|
||||||
|
[UserAuthErrorCode.USER_NOT_FOUND]: "User not found",
|
||||||
|
[UserAuthErrorCode.OTP_REQUIRED]: "Please request an OTP first",
|
||||||
|
[UserAuthErrorCode.OTP_EXPIRED]: "OTP has expired",
|
||||||
|
[UserAuthErrorCode.OTP_INVALID]: "OTP is invalid",
|
||||||
|
[UserAuthErrorCode.OTP_REQUEST_TOO_SOON]:
|
||||||
|
"Wait for expiry time to finish before requesting another OTP",
|
||||||
|
[UserAuthErrorCode.LINK_NOT_FOUND]: "Linked SMS token was not found",
|
||||||
|
[UserAuthErrorCode.LINK_MOBILE_MISMATCH]:
|
||||||
|
"This mobile number is not allowed to use this SMS link",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function userAuthErrorBody(code: UserAuthErrorCode) {
|
||||||
|
return {
|
||||||
|
code,
|
||||||
|
message: messages[code],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function throwUserAuthError(code: UserAuthErrorCode): never {
|
||||||
|
if (code === UserAuthErrorCode.OTP_REQUEST_TOO_SOON) {
|
||||||
|
throw new BadRequestException(userAuthErrorBody(code));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code === UserAuthErrorCode.LINK_MOBILE_MISMATCH) {
|
||||||
|
throw new ForbiddenException(userAuthErrorBody(code));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnauthorizedException(userAuthErrorBody(code));
|
||||||
|
}
|
||||||
187
src/auth/auth-services/user-link-access.service.ts
Normal file
187
src/auth/auth-services/user-link-access.service.ts
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { InjectModel } from "@nestjs/mongoose";
|
||||||
|
import { Model, Types } from "mongoose";
|
||||||
|
import {
|
||||||
|
UserAuthErrorCode,
|
||||||
|
throwUserAuthError,
|
||||||
|
} from "src/auth/auth-services/user-auth-error";
|
||||||
|
import { ClaimRequestManagementModel } from "src/claim-request-management/entites/schema/claim-request-management.schema";
|
||||||
|
import { BlameRequest } from "src/request-management/entities/schema/blame-cases.schema";
|
||||||
|
import { PartyRole } from "src/request-management/entities/schema/partyRole.enum";
|
||||||
|
import { RequestManagementModel } from "src/request-management/entities/schema/request-management.schema";
|
||||||
|
import { UserDbService } from "src/users/entities/db-service/user.db.service";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserLinkAccessService {
|
||||||
|
constructor(
|
||||||
|
@InjectModel(RequestManagementModel.name)
|
||||||
|
private readonly requestManagementModel: Model<RequestManagementModel>,
|
||||||
|
@InjectModel(BlameRequest.name)
|
||||||
|
private readonly blameRequestModel: Model<BlameRequest>,
|
||||||
|
@InjectModel(ClaimRequestManagementModel.name)
|
||||||
|
private readonly claimRequestManagementModel: Model<ClaimRequestManagementModel>,
|
||||||
|
private readonly userDbService: UserDbService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async assertMobileAllowed(params: {
|
||||||
|
mobile: string;
|
||||||
|
linkToken?: string;
|
||||||
|
linkContext?: string;
|
||||||
|
}): Promise<void> {
|
||||||
|
const linkToken = params.linkToken?.trim();
|
||||||
|
if (!linkToken) return;
|
||||||
|
|
||||||
|
const allowedMobiles = await this.resolveAllowedMobiles(
|
||||||
|
linkToken,
|
||||||
|
params.linkContext,
|
||||||
|
);
|
||||||
|
if (allowedMobiles.length === 0) {
|
||||||
|
throwUserAuthError(UserAuthErrorCode.LINK_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedMobile = this.normalizePhone(params.mobile);
|
||||||
|
if (
|
||||||
|
!normalizedMobile ||
|
||||||
|
!allowedMobiles.some((mobile) => this.normalizePhone(mobile) === normalizedMobile)
|
||||||
|
) {
|
||||||
|
throwUserAuthError(UserAuthErrorCode.LINK_MOBILE_MISMATCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async resolveAllowedMobiles(
|
||||||
|
linkToken: string,
|
||||||
|
linkContext?: string,
|
||||||
|
): Promise<string[]> {
|
||||||
|
if (!Types.ObjectId.isValid(linkToken)) return [];
|
||||||
|
|
||||||
|
const id = new Types.ObjectId(linkToken);
|
||||||
|
const context = this.normalizeContext(linkContext);
|
||||||
|
const allowedMobiles = new Set<string>();
|
||||||
|
|
||||||
|
const [legacyRequest, blameRequest, claimRequest] = await Promise.all([
|
||||||
|
this.requestManagementModel.findById(id).lean().exec(),
|
||||||
|
this.blameRequestModel.findById(id).lean().exec(),
|
||||||
|
this.claimRequestManagementModel.findById(id).lean().exec(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
this.addLegacyRequestPhones(allowedMobiles, legacyRequest, context);
|
||||||
|
this.addBlameRequestPhones(allowedMobiles, blameRequest, context);
|
||||||
|
await this.addClaimOwnerPhone(allowedMobiles, claimRequest);
|
||||||
|
|
||||||
|
return Array.from(allowedMobiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
private addLegacyRequestPhones(
|
||||||
|
allowedMobiles: Set<string>,
|
||||||
|
legacyRequest: any,
|
||||||
|
context?: string,
|
||||||
|
) {
|
||||||
|
if (!legacyRequest) return;
|
||||||
|
|
||||||
|
const shouldAddFirst = !context || this.isFirstContext(context);
|
||||||
|
const shouldAddSecond = !context || this.isSecondContext(context);
|
||||||
|
|
||||||
|
if (shouldAddFirst) {
|
||||||
|
this.addPhone(
|
||||||
|
allowedMobiles,
|
||||||
|
legacyRequest.firstPartyDetails?.firstPartyPhoneNumber,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldAddSecond) {
|
||||||
|
this.addPhone(
|
||||||
|
allowedMobiles,
|
||||||
|
legacyRequest.secondPartyDetails?.secondPartyPhoneNumber,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const event of legacyRequest.history || []) {
|
||||||
|
const metadata = event?.metadata;
|
||||||
|
if (shouldAddSecond) this.addPhone(allowedMobiles, metadata?.secondPartyPhone);
|
||||||
|
for (const sent of metadata?.sentTo || []) {
|
||||||
|
if (!context || this.matchesRoleContext(context, sent?.role)) {
|
||||||
|
this.addPhone(allowedMobiles, sent?.phoneNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private addBlameRequestPhones(
|
||||||
|
allowedMobiles: Set<string>,
|
||||||
|
blameRequest: any,
|
||||||
|
context?: string,
|
||||||
|
) {
|
||||||
|
if (!blameRequest) return;
|
||||||
|
|
||||||
|
for (const party of blameRequest.parties || []) {
|
||||||
|
if (context && !this.matchesRoleContext(context, party?.role)) continue;
|
||||||
|
this.addPhone(allowedMobiles, party?.person?.phoneNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async addClaimOwnerPhone(
|
||||||
|
allowedMobiles: Set<string>,
|
||||||
|
claimRequest: any,
|
||||||
|
) {
|
||||||
|
if (!claimRequest) return;
|
||||||
|
|
||||||
|
const ownerUserId = claimRequest.owner?.userId || claimRequest.userId;
|
||||||
|
if (!ownerUserId) return;
|
||||||
|
|
||||||
|
const ownerUserIdText = String(ownerUserId);
|
||||||
|
if (claimRequest.blameRequestId) {
|
||||||
|
const blameRequest = await this.blameRequestModel
|
||||||
|
.findById(claimRequest.blameRequestId)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
const ownerParty = (blameRequest?.parties || []).find(
|
||||||
|
(party: any) =>
|
||||||
|
party?.person?.userId && String(party.person.userId) === ownerUserIdText,
|
||||||
|
);
|
||||||
|
this.addPhone(allowedMobiles, ownerParty?.person?.phoneNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Types.ObjectId.isValid(ownerUserIdText)) {
|
||||||
|
const user = await this.userDbService.findOne({
|
||||||
|
_id: new Types.ObjectId(ownerUserIdText),
|
||||||
|
});
|
||||||
|
this.addPhone(allowedMobiles, user?.mobile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private addPhone(allowedMobiles: Set<string>, phone?: string) {
|
||||||
|
const normalized = this.normalizePhone(phone);
|
||||||
|
if (normalized) allowedMobiles.add(normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
private normalizePhone(phone?: string): string | undefined {
|
||||||
|
if (!phone) return undefined;
|
||||||
|
|
||||||
|
const digits = String(phone).replace(/\D/g, "");
|
||||||
|
if (!digits) return undefined;
|
||||||
|
if (digits.startsWith("0098")) return `0${digits.slice(4)}`;
|
||||||
|
if (digits.startsWith("98") && digits.length === 12) {
|
||||||
|
return `0${digits.slice(2)}`;
|
||||||
|
}
|
||||||
|
if (digits.length === 10 && digits.startsWith("9")) return `0${digits}`;
|
||||||
|
return digits;
|
||||||
|
}
|
||||||
|
|
||||||
|
private normalizeContext(linkContext?: string): string | undefined {
|
||||||
|
return linkContext?.trim().toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private matchesRoleContext(context: string, role?: string): boolean {
|
||||||
|
if (this.isFirstContext(context)) return role === PartyRole.FIRST;
|
||||||
|
if (this.isSecondContext(context)) return role === PartyRole.SECOND;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isFirstContext(context: string): boolean {
|
||||||
|
return ["FIRST", "USER", "USER1", "FIRST_PARTY"].includes(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private isSecondContext(context: string): boolean {
|
||||||
|
return ["SECOND", "USER2", "SECOND_PARTY"].includes(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
import {
|
import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common";
|
||||||
BadRequestException,
|
|
||||||
HttpException,
|
|
||||||
HttpStatus,
|
|
||||||
Injectable,
|
|
||||||
Logger,
|
|
||||||
NotAcceptableException,
|
|
||||||
NotFoundException,
|
|
||||||
} from "@nestjs/common";
|
|
||||||
import { JwtService } from "@nestjs/jwt";
|
import { JwtService } from "@nestjs/jwt";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
|
import {
|
||||||
|
UserAuthErrorCode,
|
||||||
|
throwUserAuthError,
|
||||||
|
} from "src/auth/auth-services/user-auth-error";
|
||||||
|
import { UserLinkAccessService } from "src/auth/auth-services/user-link-access.service";
|
||||||
import { LoginDtoRs } from "src/auth/dto/user/login.dto";
|
import { LoginDtoRs } from "src/auth/dto/user/login.dto";
|
||||||
import { OtpGeneratorService } from "src/sms-orchestration/otp-generator.service";
|
import { OtpGeneratorService } from "src/sms-orchestration/otp-generator.service";
|
||||||
import { UserDbService } from "src/users/entities/db-service/user.db.service";
|
import { UserDbService } from "src/users/entities/db-service/user.db.service";
|
||||||
import { SmsOrchestrationService } from "src/sms-orchestration/sms-orchestration.service";
|
import { SmsOrchestrationService } from "src/sms-orchestration/sms-orchestration.service";
|
||||||
import { HashService } from "src/utils/hash/hash.service";
|
import { HashService } from "src/utils/hash/hash.service";
|
||||||
|
|
||||||
|
export interface LinkBinding {
|
||||||
|
linkToken?: string;
|
||||||
|
linkContext?: string;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO FIX REGISTER TO USER.SERVICE AND AUTH IN THIS MODULE
|
// TODO FIX REGISTER TO USER.SERVICE AND AUTH IN THIS MODULE
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserAuthService {
|
export class UserAuthService {
|
||||||
@@ -26,16 +28,27 @@ export class UserAuthService {
|
|||||||
private readonly hashService: HashService,
|
private readonly hashService: HashService,
|
||||||
private readonly otpCreator: OtpGeneratorService,
|
private readonly otpCreator: OtpGeneratorService,
|
||||||
private readonly smsOrchestrationService: SmsOrchestrationService,
|
private readonly smsOrchestrationService: SmsOrchestrationService,
|
||||||
|
private readonly userLinkAccessService: UserLinkAccessService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async validateUser(username: string, pass: string): Promise<any> {
|
async validateUser(
|
||||||
|
username: string,
|
||||||
|
pass: string,
|
||||||
|
binding: LinkBinding = {},
|
||||||
|
): Promise<any> {
|
||||||
|
await this.userLinkAccessService.assertMobileAllowed({
|
||||||
|
mobile: username,
|
||||||
|
linkToken: binding.linkToken,
|
||||||
|
linkContext: binding.linkContext,
|
||||||
|
});
|
||||||
|
|
||||||
const user = await this.userDbService.findOne({ username });
|
const user = await this.userDbService.findOne({ username });
|
||||||
if (!user) throw new NotFoundException("user not found");
|
if (!user) throwUserAuthError(UserAuthErrorCode.USER_NOT_FOUND);
|
||||||
|
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
if (user.otp == null) throw new NotAcceptableException("please get otp");
|
if (user.otp == null) throwUserAuthError(UserAuthErrorCode.OTP_REQUIRED);
|
||||||
if (user.otpExpire < now) {
|
if (user.otpExpire < now) {
|
||||||
throw new NotAcceptableException("expire otp");
|
throwUserAuthError(UserAuthErrorCode.OTP_EXPIRED);
|
||||||
}
|
}
|
||||||
if (await this.hashService.compare(pass, user.otp)) {
|
if (await this.hashService.compare(pass, user.otp)) {
|
||||||
return user;
|
return user;
|
||||||
@@ -65,7 +78,16 @@ export class UserAuthService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendOtpRequest(mobile: string): Promise<LoginDtoRs> {
|
async sendOtpRequest(
|
||||||
|
mobile: string,
|
||||||
|
binding: LinkBinding = {},
|
||||||
|
): Promise<LoginDtoRs> {
|
||||||
|
await this.userLinkAccessService.assertMobileAllowed({
|
||||||
|
mobile,
|
||||||
|
linkToken: binding.linkToken,
|
||||||
|
linkContext: binding.linkContext,
|
||||||
|
});
|
||||||
|
|
||||||
const userExist = await this.userDbService.findOne({
|
const userExist = await this.userDbService.findOne({
|
||||||
mobile,
|
mobile,
|
||||||
});
|
});
|
||||||
@@ -101,7 +123,9 @@ export class UserAuthService {
|
|||||||
return new LoginDtoRs(newUser);
|
return new LoginDtoRs(newUser);
|
||||||
}
|
}
|
||||||
if (userExist) {
|
if (userExist) {
|
||||||
if (userExist.otpExpire > new Date(new Date().getTime()).getTime()) throw new BadRequestException("Wait for expiry time to finish");
|
if (userExist.otpExpire > new Date(new Date().getTime()).getTime()) {
|
||||||
|
throwUserAuthError(UserAuthErrorCode.OTP_REQUEST_TOO_SOON);
|
||||||
|
}
|
||||||
await this.smsSender(otp, mobile);
|
await this.smsSender(otp, mobile);
|
||||||
const updateTokens = await this.userDbService.findOneAndUpdate(
|
const updateTokens = await this.userDbService.findOneAndUpdate(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { JwtModule, JwtService } from "@nestjs/jwt";
|
import { JwtModule, JwtService } from "@nestjs/jwt";
|
||||||
|
import { MongooseModule } from "@nestjs/mongoose";
|
||||||
import { PassportModule } from "@nestjs/passport";
|
import { PassportModule } from "@nestjs/passport";
|
||||||
import { LocalStrategy } from "src/auth/stratregys/local.strategy";
|
import { LocalStrategy } from "src/auth/stratregys/local.strategy";
|
||||||
import { LocalActorStrategy } from "src/auth/stratregys/local-actor.strategy";
|
import { LocalActorStrategy } from "src/auth/stratregys/local-actor.strategy";
|
||||||
@@ -7,7 +8,20 @@ import { ActorAuthController } from "src/auth/auth-controllers/actor/actor.auth.
|
|||||||
import { UserAuthController } from "src/auth/auth-controllers/user/user.auth.controller";
|
import { UserAuthController } from "src/auth/auth-controllers/user/user.auth.controller";
|
||||||
import { ActorAuthService } from "src/auth/auth-services/actor.auth.service";
|
import { ActorAuthService } from "src/auth/auth-services/actor.auth.service";
|
||||||
import { UserAuthService } from "src/auth/auth-services/user.auth.service";
|
import { UserAuthService } from "src/auth/auth-services/user.auth.service";
|
||||||
|
import { UserLinkAccessService } from "src/auth/auth-services/user-link-access.service";
|
||||||
|
import {
|
||||||
|
ClaimRequestManagementModel,
|
||||||
|
ClaimRequestManagementSchema,
|
||||||
|
} from "src/claim-request-management/entites/schema/claim-request-management.schema";
|
||||||
import { ClientModule } from "src/client/client.module";
|
import { ClientModule } from "src/client/client.module";
|
||||||
|
import {
|
||||||
|
BlameRequest,
|
||||||
|
BlameRequestSchema,
|
||||||
|
} from "src/request-management/entities/schema/blame-cases.schema";
|
||||||
|
import {
|
||||||
|
RequestManagementModel,
|
||||||
|
RequestManagementSchema,
|
||||||
|
} from "src/request-management/entities/schema/request-management.schema";
|
||||||
import { UsersModule } from "src/users/users.module";
|
import { UsersModule } from "src/users/users.module";
|
||||||
import { HashModule } from "src/utils/hash/hash.module";
|
import { HashModule } from "src/utils/hash/hash.module";
|
||||||
// import { MailModule } from "src/utils/mail/mail.module";
|
// import { MailModule } from "src/utils/mail/mail.module";
|
||||||
@@ -21,6 +35,14 @@ import { SmsOrchestrationModule } from "src/sms-orchestration/sms-orchestration.
|
|||||||
HashModule,
|
HashModule,
|
||||||
PassportModule,
|
PassportModule,
|
||||||
SmsOrchestrationModule,
|
SmsOrchestrationModule,
|
||||||
|
MongooseModule.forFeature([
|
||||||
|
{ name: RequestManagementModel.name, schema: RequestManagementSchema },
|
||||||
|
{ name: BlameRequest.name, schema: BlameRequestSchema },
|
||||||
|
{
|
||||||
|
name: ClaimRequestManagementModel.name,
|
||||||
|
schema: ClaimRequestManagementSchema,
|
||||||
|
},
|
||||||
|
]),
|
||||||
JwtModule.register({
|
JwtModule.register({
|
||||||
signOptions: { expiresIn: "1h" },
|
signOptions: { expiresIn: "1h" },
|
||||||
global: true,
|
global: true,
|
||||||
@@ -29,6 +51,7 @@ import { SmsOrchestrationModule } from "src/sms-orchestration/sms-orchestration.
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
UserAuthService,
|
UserAuthService,
|
||||||
|
UserLinkAccessService,
|
||||||
ActorAuthService,
|
ActorAuthService,
|
||||||
LocalStrategy,
|
LocalStrategy,
|
||||||
LocalActorStrategy,
|
LocalActorStrategy,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { UserModel } from "src/users/entities/schema/user.schema";
|
import { UserModel } from "src/users/entities/schema/user.schema";
|
||||||
|
|
||||||
export class UserLoginDto {
|
export class UserLoginDto {
|
||||||
@@ -8,6 +8,20 @@ export class UserLoginDto {
|
|||||||
description: "User login dto",
|
description: "User login dto",
|
||||||
})
|
})
|
||||||
mobile: string;
|
mobile: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: "65f0c7f0c3f8a2a7c8b3d001",
|
||||||
|
type: "string",
|
||||||
|
description: "Raw token from linked SMS URL (?token=...).",
|
||||||
|
})
|
||||||
|
linkToken?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: "FIRST",
|
||||||
|
type: "string",
|
||||||
|
description: "Optional route/context hint for linked SMS login.",
|
||||||
|
})
|
||||||
|
linkContext?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LoginDtoRs extends UserModel {
|
export class LoginDtoRs extends UserModel {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
|
||||||
export class UserVerifyOtp {
|
export class UserVerifyOtp {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@@ -14,4 +14,18 @@ export class UserVerifyOtp {
|
|||||||
description: "User login verify dto",
|
description: "User login verify dto",
|
||||||
})
|
})
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: "65f0c7f0c3f8a2a7c8b3d001",
|
||||||
|
type: "string",
|
||||||
|
description: "Raw token from linked SMS URL (?token=...).",
|
||||||
|
})
|
||||||
|
linkToken?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: "FIRST",
|
||||||
|
type: "string",
|
||||||
|
description: "Optional route/context hint for linked SMS login.",
|
||||||
|
})
|
||||||
|
linkContext?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import {
|
import { ExecutionContext, Injectable } from "@nestjs/common";
|
||||||
ExecutionContext,
|
|
||||||
Injectable,
|
|
||||||
NotAcceptableException,
|
|
||||||
} from "@nestjs/common";
|
|
||||||
import { AuthGuard } from "@nestjs/passport";
|
import { AuthGuard } from "@nestjs/passport";
|
||||||
|
import {
|
||||||
|
UserAuthErrorCode,
|
||||||
|
throwUserAuthError,
|
||||||
|
} from "src/auth/auth-services/user-auth-error";
|
||||||
import { UserAuthService } from "src/auth/auth-services/user.auth.service";
|
import { UserAuthService } from "src/auth/auth-services/user.auth.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -13,13 +13,14 @@ export class LocalUserAuthGuard extends AuthGuard("local") {
|
|||||||
}
|
}
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||||
const request = context.switchToHttp().getRequest();
|
const request = context.switchToHttp().getRequest();
|
||||||
const { username, password } = request.body;
|
const { username, password, linkToken, linkContext } = request.body;
|
||||||
let isValidUser = await this.userAuthService.validateUser(
|
const isValidUser = await this.userAuthService.validateUser(
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
|
{ linkToken, linkContext },
|
||||||
);
|
);
|
||||||
if (!isValidUser) {
|
if (!isValidUser) {
|
||||||
throw new NotAcceptableException("otp is wrong");
|
throwUserAuthError(UserAuthErrorCode.OTP_INVALID);
|
||||||
}
|
}
|
||||||
request["user"] = isValidUser;
|
request["user"] = isValidUser;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { Injectable, UnauthorizedException } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { PassportStrategy } from "@nestjs/passport";
|
import { PassportStrategy } from "@nestjs/passport";
|
||||||
import { Strategy } from "passport-local";
|
import { Strategy } from "passport-local";
|
||||||
|
import {
|
||||||
|
UserAuthErrorCode,
|
||||||
|
throwUserAuthError,
|
||||||
|
} from "src/auth/auth-services/user-auth-error";
|
||||||
import { UserAuthService } from "src/auth/auth-services/user.auth.service";
|
import { UserAuthService } from "src/auth/auth-services/user.auth.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -12,7 +16,7 @@ export class LocalStrategy extends PassportStrategy(Strategy) {
|
|||||||
async validate(username: string, password: string): Promise<any> {
|
async validate(username: string, password: string): Promise<any> {
|
||||||
const user = await this.userAuthService.validateUser(username, password);
|
const user = await this.userAuthService.validateUser(username, password);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new UnauthorizedException("user not found please register");
|
throwUserAuthError(UserAuthErrorCode.OTP_INVALID);
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user