feat: logout

This commit is contained in:
Abdalhamid Alhamad
2024-12-19 16:50:39 +03:00
parent 93f5d83825
commit 8112fb81a2
6 changed files with 41 additions and 8 deletions

View File

@ -49,6 +49,7 @@ import { TaskModule } from './task/task.module';
inject: [ConfigService],
}),
I18nModule.forRoot(buildI18nOptions()),
CacheModule,
// App modules

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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 {}

View File

@ -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],