mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-08-26 22:29:42 +00:00
feat: logout
This commit is contained in:
@ -49,6 +49,7 @@ import { TaskModule } from './task/task.module';
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
I18nModule.forRoot(buildI18nOptions()),
|
||||
|
||||
CacheModule,
|
||||
|
||||
// App modules
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Body, Controller, Headers, HttpCode, HttpStatus, Post, UseGuards } from '@nestjs/common';
|
||||
import { Body, Controller, Headers, HttpCode, HttpStatus, Post, Req, UseGuards } from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
import { Request } from 'express';
|
||||
import { DEVICE_ID_HEADER } from '~/common/constants';
|
||||
import { AuthenticatedUser, Public } from '~/common/decorators';
|
||||
import { AccessTokenGuard } from '~/common/guards';
|
||||
@ -98,4 +99,11 @@ export class AuthController {
|
||||
const [res, user] = await this.authService.login(loginDto, deviceId);
|
||||
return ResponseFactory.data(new LoginResponseDto(res, user));
|
||||
}
|
||||
|
||||
@Post('logout')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@UseGuards(AccessTokenGuard)
|
||||
async logout(@Req() request: Request) {
|
||||
await this.authService.logout(request);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ import { BadRequestException, Injectable, UnauthorizedException } from '@nestjs/
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import { Request } from 'express';
|
||||
import { CacheService } from '~/common/modules/cache/services';
|
||||
import { OtpScope, OtpType } from '~/common/modules/otp/enums';
|
||||
import { OtpService } from '~/common/modules/otp/services';
|
||||
import { JuniorTokenService } from '~/junior/services';
|
||||
@ -35,6 +37,7 @@ export class AuthService {
|
||||
private readonly userService: UserService,
|
||||
private readonly deviceService: DeviceService,
|
||||
private readonly juniorTokenService: JuniorTokenService,
|
||||
private readonly cacheService: CacheService,
|
||||
) {}
|
||||
async sendRegisterOtp({ phoneNumber, countryCode }: CreateUnverifiedUserRequestDto) {
|
||||
const user = await this.userService.findOrCreateUser({ phoneNumber, countryCode });
|
||||
@ -213,6 +216,12 @@ export class AuthService {
|
||||
}
|
||||
}
|
||||
|
||||
logout(req: Request) {
|
||||
const accessToken = req.headers.authorization?.split(' ')[1] as string;
|
||||
const expiryInTtl = this.jwtService.decode(accessToken).exp - Date.now() / ONE_THOUSAND;
|
||||
return this.cacheService.set(accessToken, 'LOGOUT', expiryInTtl);
|
||||
}
|
||||
|
||||
private async loginWithPassword(loginDto: LoginRequestDto, user: User): Promise<ILoginResponse> {
|
||||
const isPasswordValid = bcrypt.compareSync(loginDto.password, user.password);
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { Observable } from 'rxjs';
|
||||
import { IS_PUBLIC_KEY } from '../decorators';
|
||||
import { CacheService } from '../modules/cache/services';
|
||||
|
||||
@Injectable()
|
||||
export class AccessTokenGuard extends AuthGuard('access-token') {
|
||||
constructor(protected reflector: Reflector) {
|
||||
constructor(protected reflector: Reflector, private readonly cacheService: CacheService) {
|
||||
super();
|
||||
}
|
||||
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
|
||||
async canActivate(context: ExecutionContext) {
|
||||
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
|
||||
context.getHandler(),
|
||||
context.getClass(),
|
||||
@ -18,6 +18,21 @@ export class AccessTokenGuard extends AuthGuard('access-token') {
|
||||
if (isPublic) {
|
||||
return true;
|
||||
}
|
||||
return super.canActivate(context);
|
||||
|
||||
const isValid = await super.canActivate(context);
|
||||
|
||||
if (!isValid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const token = context.switchToHttp().getRequest().headers['authorization']?.split(' ')[1];
|
||||
|
||||
const isRevoked = await this.cacheService.get(token);
|
||||
|
||||
if (isRevoked) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !isRevoked;
|
||||
}
|
||||
}
|
||||
|
3
src/common/modules/cache/cache.module.ts
vendored
3
src/common/modules/cache/cache.module.ts
vendored
@ -1,4 +1,4 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { buildKeyvOptions } from '~/core/module-options';
|
||||
import { CacheService } from './services';
|
||||
@ -14,4 +14,5 @@ import { CacheService } from './services';
|
||||
],
|
||||
exports: ['CACHE_INSTANCE', CacheService],
|
||||
})
|
||||
@Global()
|
||||
export class CacheModule {}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { CacheModule } from '~/common/modules/cache/cache.module';
|
||||
import { DocumentController } from './controllers';
|
||||
import { Document } from './entities';
|
||||
import { DocumentRepository } from './repositories';
|
||||
@ -8,7 +7,7 @@ import { DocumentService, OciService } from './services';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Document]), CacheModule],
|
||||
imports: [TypeOrmModule.forFeature([Document])],
|
||||
controllers: [DocumentController],
|
||||
providers: [DocumentService, OciService, DocumentRepository],
|
||||
exports: [DocumentService, OciService],
|
||||
|
Reference in New Issue
Block a user