auth
This commit is contained in:
@@ -9,7 +9,6 @@ import { TokensService } from './services/tokens.service';
|
||||
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
||||
import { AdminAuthGuard } from './guards/adminAuth.guard';
|
||||
import { RefreshToken } from './entities/refresh-token.entity';
|
||||
import { NotificationsModule } from '../notifications/notifications.module';
|
||||
import { BusinessModule } from '../business/business.module';
|
||||
|
||||
@Module({
|
||||
@@ -32,7 +31,6 @@ import { BusinessModule } from '../business/business.module';
|
||||
forwardRef(() => AdminModule),
|
||||
forwardRef(() => BusinessModule),
|
||||
MikroOrmModule.forFeature([RefreshToken]),
|
||||
forwardRef(() => NotificationsModule),
|
||||
BusinessModule
|
||||
],
|
||||
controllers: [AuthController],
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { Controller, Post, Body, UseGuards } from '@nestjs/common';
|
||||
import { AuthService } from '../services/auth.service';
|
||||
import { RequestOtpDto } from '../dto/request-otp.dto';
|
||||
import { DirectLoginDto } from '../dto/direct-login.dto';
|
||||
import { ApiTags, ApiOperation, ApiBody } from '@nestjs/swagger';
|
||||
import { VerifyOtpDto } from '../dto/verify-otp.dto';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
import { RefreshTokenRateLimit } from 'src/common/decorators/rate-limit.decorator';
|
||||
import { RefreshTokenDto } from '../dto/refresh-token.dto';
|
||||
import { SuperAdminAuthGuard } from '../guards/superAdminAuth.guard';
|
||||
|
||||
@ApiTags('auth')
|
||||
|
||||
@@ -2,13 +2,9 @@ import { Entity, Enum, Index, Property } from '@mikro-orm/core';
|
||||
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
|
||||
export enum RefreshTokenType {
|
||||
USER = 'user',
|
||||
ADMIN = 'admin',
|
||||
}
|
||||
|
||||
|
||||
@Entity({ tableName: 'refreshtokens' })
|
||||
@Index({ properties: ['ownerId', 'restId', 'type'] })
|
||||
@Index({ properties: ['adminId', 'businessId'] })
|
||||
@Index({ properties: ['hashedToken'] })
|
||||
@Index({ properties: ['expiresAt'] })
|
||||
export class RefreshToken extends BaseEntity {
|
||||
@@ -16,13 +12,10 @@ export class RefreshToken extends BaseEntity {
|
||||
hashedToken!: string;
|
||||
|
||||
@Property()
|
||||
ownerId!: string;
|
||||
adminId!: string;
|
||||
|
||||
@Property()
|
||||
restId!: string;
|
||||
|
||||
@Enum(() => RefreshTokenType)
|
||||
type!: RefreshTokenType;
|
||||
businessId!: string;
|
||||
|
||||
@Property({ type: 'timestamptz' })
|
||||
expiresAt!: Date;
|
||||
|
||||
@@ -1,28 +1,14 @@
|
||||
import { Injectable, BadRequestException } from '@nestjs/common';
|
||||
import { RequestOtpDto } from '../dto/request-otp.dto';
|
||||
import { CacheService } from '../../utils/cache.service';
|
||||
import { SmsService } from '../../notifications/services/sms.service';
|
||||
import { randomInt } from 'crypto';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TokensService } from './tokens.service';
|
||||
import { AuthMessage, RestMessage } from 'src/common/enums/message.enum';
|
||||
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { normalizePhone } from '../../utils/phone.util';
|
||||
import { BusinessService } from 'src/modules/business/business.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
readonly ADMIN_PERMISSIONS_KEY = 'admin-perms';
|
||||
private OTP_EXPIRATION_TIME: number;
|
||||
|
||||
constructor(
|
||||
private readonly cacheService: CacheService,
|
||||
private readonly smsService: SmsService,
|
||||
private readonly tokensService: TokensService,
|
||||
private readonly adminRepository: AdminRepository,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly businessService: BusinessService,
|
||||
) {
|
||||
this.OTP_EXPIRATION_TIME = this.configService.get<number>('OTP_EXPIRATION_TIME') ?? 240;
|
||||
}
|
||||
|
||||
// private userOtpKey(restaurantSlug: string, phone: string) {
|
||||
@@ -33,8 +19,11 @@ export class AuthService {
|
||||
// return `otp-admin:${restaurantSlug}:${phone}`;
|
||||
// }
|
||||
|
||||
directLogin(danakSubId: string) {
|
||||
const business=this.businessService.findBySubIdOrFail(danakSubId)
|
||||
async directLogin(danakSubId: string) {
|
||||
const business = await this.businessService.findBySubIdOrFail(danakSubId)
|
||||
const admin = business.admin
|
||||
const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, business.id);
|
||||
return tokens
|
||||
}
|
||||
|
||||
// async requestOtp(dto: RequestOtpDto, isAdmin: boolean) {
|
||||
|
||||
@@ -3,13 +3,12 @@ import { EntityManager } from '@mikro-orm/postgresql';
|
||||
import { LockMode } from '@mikro-orm/core';
|
||||
import { Injectable, Logger, UnauthorizedException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { AuthMessage } from '../../../common/enums/message.enum';
|
||||
import { RefreshToken } from '../entities/refresh-token.entity';
|
||||
import { IAdminTokenPayload, ITokenPayload } from '../interfaces/IToken-payload';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { AuthMessage } from '../../../common/enums/message.enum';
|
||||
import { RefreshToken, RefreshTokenType } from '../entities/refresh-token.entity';
|
||||
import { IAdminTokenPayload, ITokenPayload } from '../interfaces/IToken-payload';
|
||||
|
||||
@Injectable()
|
||||
export class TokensService {
|
||||
private readonly logger = new Logger(TokensService.name);
|
||||
@@ -17,28 +16,23 @@ export class TokensService {
|
||||
private readonly configService: ConfigService,
|
||||
private readonly jwtService: JwtService,
|
||||
private readonly em: EntityManager,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
async generateAccessAndRefreshToken(
|
||||
ownerId: string,
|
||||
restaurantId: string,
|
||||
isAdmin: boolean,
|
||||
slug: string,
|
||||
adminId: string,
|
||||
businessId: string,
|
||||
em?: EntityManager,
|
||||
) {
|
||||
const refreshExpire = this.configService.getOrThrow<number>('REFRESH_TOKEN_EXPIRE');
|
||||
const accessExpire = this.configService.getOrThrow<number>('JWT_EXPIRATION_TIME');
|
||||
|
||||
const payload: ITokenPayload | IAdminTokenPayload = isAdmin
|
||||
? { adminId: ownerId, restId: restaurantId }
|
||||
: { userId: ownerId, restId: restaurantId, slug };
|
||||
const payload: IAdminTokenPayload = { adminId: adminId, restId: businessId }
|
||||
|
||||
const accessToken = await this.generateAccessToken(payload, accessExpire);
|
||||
const refreshToken = this.generateRefreshToken();
|
||||
const type = isAdmin ? RefreshTokenType.ADMIN : RefreshTokenType.USER;
|
||||
|
||||
// Only pass em if it's a transaction manager (not this.em), otherwise pass undefined to trigger flush
|
||||
await this.storeRefreshToken(ownerId, restaurantId, refreshToken, type, em);
|
||||
await this.storeRefreshToken(adminId, businessId, refreshToken, em);
|
||||
|
||||
return {
|
||||
accessToken: { token: accessToken, expire: dayjs().add(accessExpire, 'seconds').valueOf() },
|
||||
@@ -57,7 +51,6 @@ export class TokensService {
|
||||
ownerId: string,
|
||||
restId: string,
|
||||
refreshToken: string,
|
||||
type: RefreshTokenType,
|
||||
em?: EntityManager,
|
||||
) {
|
||||
const entityManager = em || this.em;
|
||||
@@ -68,9 +61,8 @@ export class TokensService {
|
||||
|
||||
const token = entityManager.create(RefreshToken, {
|
||||
hashedToken,
|
||||
ownerId,
|
||||
restId,
|
||||
type,
|
||||
adminId: ownerId,
|
||||
businessId: restId,
|
||||
expiresAt,
|
||||
});
|
||||
|
||||
@@ -104,20 +96,13 @@ export class TokensService {
|
||||
}
|
||||
|
||||
// Store token data before removal
|
||||
const ownerId = token.ownerId;
|
||||
const restId = token.restId;
|
||||
const isAdmin = token.type === RefreshTokenType.ADMIN;
|
||||
|
||||
// Verify restaurant still exists
|
||||
// const restaurant = await em.findOne(Restaurant, { id: restId });
|
||||
// if (!restaurant) {
|
||||
// throw new UnauthorizedException(AuthMessage.INVALID_REFRESH_TOKEN);
|
||||
// }
|
||||
const adminId = token.adminId;
|
||||
const businessId = token.businessId;
|
||||
|
||||
try {
|
||||
// Generate new tokens first (before removing old token)
|
||||
// Pass the transaction EntityManager to ensure operations are within the transaction
|
||||
const tokens = await this.generateAccessAndRefreshToken(ownerId, "restaurant.id", isAdmin, "restaurant.slug", em);
|
||||
const tokens = await this.generateAccessAndRefreshToken(adminId, businessId, em);
|
||||
|
||||
// Remove old token only after new token is created
|
||||
em.remove(token);
|
||||
|
||||
Reference in New Issue
Block a user