From ca5af30e688d9419a7e63e30ddf0f59ef4fb3fe6 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 14 Dec 2024 13:08:50 +0700 Subject: [PATCH] refactor and update docs --- README.md | 3 ++- src/application/actions/handlers/index.ts | 2 ++ .../actions/handlers/login.command.handler.ts | 15 +++++++++++++++ .../handlers/user-logout.command.handler.ts | 15 +++++++++++++++ src/application/actions/index.ts | 2 ++ src/application/actions/login.command.ts | 5 +++++ src/application/actions/user-logout.command.ts | 5 +++++ src/application/queries/handlers/index.ts | 2 -- .../queries/handlers/login.query.handler.ts | 15 --------------- .../queries/handlers/user-logout.query.handler.ts | 15 --------------- src/application/queries/index.ts | 2 -- src/application/queries/login.query.ts | 5 ----- src/application/queries/user-logout.query.ts | 7 ------- src/auth.module.ts | 15 +++++++++------ src/domain/actions/local-login.action.ts | 4 ++-- src/domain/definitions/auth-definitions.ts | 1 + src/domain/services/token.service.ts | 1 + src/domain/strategies/jwt.strategy.ts | 10 +++++----- src/domain/strategies/local.strategy.ts | 10 +++++----- .../helpers/cookie.interceptor.helper.ts | 1 + .../interceptors/user-logout.interceptor.ts | 8 ++++---- 21 files changed, 74 insertions(+), 69 deletions(-) create mode 100644 src/application/actions/handlers/index.ts create mode 100644 src/application/actions/handlers/login.command.handler.ts create mode 100644 src/application/actions/handlers/user-logout.command.handler.ts create mode 100644 src/application/actions/index.ts create mode 100644 src/application/actions/login.command.ts create mode 100644 src/application/actions/user-logout.command.ts delete mode 100644 src/application/queries/handlers/login.query.handler.ts delete mode 100644 src/application/queries/handlers/user-logout.query.handler.ts delete mode 100644 src/application/queries/login.query.ts delete mode 100644 src/application/queries/user-logout.query.ts diff --git a/README.md b/README.md index b271bcb..834b0e7 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,8 @@ import { Module } from '@nestjs/common' }, jwt: { secret: config.get('AUTH_JWT_SECRET'), - expiresIn: config.get('AUTH_JWT_EXPIRES') || '1d', + expiresIn: envRequired('AUTH_JWT_EXPIRES') || '1h', + refreshTokenExpiresIn: envRequired('AUTH_JWT_REFRESH_EXPIRES') || '7d', }, transferTokenMethod: config.get( 'AUTH_TRANSFER_TOKEN_METHOD' diff --git a/src/application/actions/handlers/index.ts b/src/application/actions/handlers/index.ts new file mode 100644 index 0000000..291decb --- /dev/null +++ b/src/application/actions/handlers/index.ts @@ -0,0 +1,2 @@ +export * from './login.command.handler' +export * from './user-logout.command.handler' diff --git a/src/application/actions/handlers/login.command.handler.ts b/src/application/actions/handlers/login.command.handler.ts new file mode 100644 index 0000000..2e55f11 --- /dev/null +++ b/src/application/actions/handlers/login.command.handler.ts @@ -0,0 +1,15 @@ +import { ICommandHandler, CommandHandler } from '@nestjs/cqrs' +import { lastValueFrom } from 'rxjs' +import { IAuthUserEntityForResponse, LocalLoginAction } from '../../../domain' +import { LoginCommand } from '../login.command' + +@CommandHandler(LoginCommand) +export class LoginCommandHandler + implements ICommandHandler +{ + public constructor(protected readonly action: LocalLoginAction) {} + + public async execute(query: LoginCommand) { + return lastValueFrom(this.action.handle(query.input)) + } +} diff --git a/src/application/actions/handlers/user-logout.command.handler.ts b/src/application/actions/handlers/user-logout.command.handler.ts new file mode 100644 index 0000000..8caaec6 --- /dev/null +++ b/src/application/actions/handlers/user-logout.command.handler.ts @@ -0,0 +1,15 @@ +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs' +import { lastValueFrom } from 'rxjs' +import { LogoutAction } from '../../../domain' +import { UserLogoutCommand } from '../user-logout.command' + +@CommandHandler(UserLogoutCommand) +export class UserLogoutCommandHandler + implements ICommandHandler +{ + public constructor(protected readonly action: LogoutAction) {} + + public async execute(query: UserLogoutCommand): Promise { + return lastValueFrom(this.action.handle(query)) + } +} diff --git a/src/application/actions/index.ts b/src/application/actions/index.ts new file mode 100644 index 0000000..6df868d --- /dev/null +++ b/src/application/actions/index.ts @@ -0,0 +1,2 @@ +export * from './login.command' +export * from './user-logout.command' diff --git a/src/application/actions/login.command.ts b/src/application/actions/login.command.ts new file mode 100644 index 0000000..e0cb74f --- /dev/null +++ b/src/application/actions/login.command.ts @@ -0,0 +1,5 @@ +import { AuthDto } from '../../domain' + +export class LoginCommand { + public constructor(public readonly input: AuthDto) {} +} diff --git a/src/application/actions/user-logout.command.ts b/src/application/actions/user-logout.command.ts new file mode 100644 index 0000000..f946f8a --- /dev/null +++ b/src/application/actions/user-logout.command.ts @@ -0,0 +1,5 @@ +import { ExecutionContext } from '@nestjs/common' + +export class UserLogoutCommand { + public constructor(public readonly context: ExecutionContext) {} +} diff --git a/src/application/queries/handlers/index.ts b/src/application/queries/handlers/index.ts index 5eb436a..ed11bc9 100644 --- a/src/application/queries/handlers/index.ts +++ b/src/application/queries/handlers/index.ts @@ -1,4 +1,2 @@ export * from './get-current-user-by-access-token.query.handler' export * from './get-current-user-by-refresh-token.query.handler' -export * from './login.query.handler' -export * from './user-logout.query.handler' diff --git a/src/application/queries/handlers/login.query.handler.ts b/src/application/queries/handlers/login.query.handler.ts deleted file mode 100644 index f514ba3..0000000 --- a/src/application/queries/handlers/login.query.handler.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IQueryHandler, QueryHandler } from '@nestjs/cqrs' -import { lastValueFrom } from 'rxjs' -import { IAuthUserEntityForResponse, LocalLoginAction } from '../../../domain' -import { LoginQuery } from '../login.query' - -@QueryHandler(LoginQuery) -export class LoginQueryHandler - implements IQueryHandler -{ - public constructor(protected readonly action: LocalLoginAction) {} - - public async execute(query: LoginQuery) { - return lastValueFrom(this.action.handle(query.input)) - } -} diff --git a/src/application/queries/handlers/user-logout.query.handler.ts b/src/application/queries/handlers/user-logout.query.handler.ts deleted file mode 100644 index b32e54d..0000000 --- a/src/application/queries/handlers/user-logout.query.handler.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IQueryHandler, QueryHandler } from '@nestjs/cqrs' -import { lastValueFrom } from 'rxjs' -import { LogoutAction } from '../../../domain' -import { UserLogoutQuery } from '../user-logout.query' - -@QueryHandler(UserLogoutQuery) -export class UserLogoutQueryHandler - implements IQueryHandler -{ - public constructor(protected readonly action: LogoutAction) {} - - public async execute(query: UserLogoutQuery): Promise { - return lastValueFrom(this.action.handle(query)) - } -} diff --git a/src/application/queries/index.ts b/src/application/queries/index.ts index 7b429e4..0579d5d 100644 --- a/src/application/queries/index.ts +++ b/src/application/queries/index.ts @@ -1,4 +1,2 @@ export * from './get-current-user-by-access-token.query' export * from './get-current-user-by-refresh-token.query' -export * from './login.query' -export * from './user-logout.query' diff --git a/src/application/queries/login.query.ts b/src/application/queries/login.query.ts deleted file mode 100644 index 494fe00..0000000 --- a/src/application/queries/login.query.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { AuthDto } from '../../domain' - -export class LoginQuery { - public constructor(public input: AuthDto) {} -} diff --git a/src/application/queries/user-logout.query.ts b/src/application/queries/user-logout.query.ts deleted file mode 100644 index 4b76246..0000000 --- a/src/application/queries/user-logout.query.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ExecutionContext } from '@nestjs/common' - -export class UserLogoutQuery { - public constructor( - public context: ExecutionContext - ) {} -} diff --git a/src/auth.module.ts b/src/auth.module.ts index ad36ce0..9f690a6 100644 --- a/src/auth.module.ts +++ b/src/auth.module.ts @@ -15,8 +15,6 @@ import { JwtModule } from '@nestjs/jwt' import { GetCurrentUserByAccessTokenQueryHandler, GetCurrentUserByRefreshTokenQueryHandler, - LoginQueryHandler, - UserLogoutQueryHandler, } from './application/queries/handlers' import { AUTH_PASSWORD_HASHER, @@ -55,6 +53,10 @@ import { } from './presentation' import { LogoutController } from './presentation/controllers/logout.controller' import { HashingModule } from './hashing.module' +import { + LoginCommandHandler, + UserLogoutCommandHandler, +} from './application/actions/handlers' export interface IAuthModuleOptions extends Pick { @@ -92,7 +94,7 @@ export class AuthModule implements NestModule { useFactory: (definitions: IAuthDefinitions) => { return { secretKey: definitions.hashingSecretKey, - enabled: !!definitions.jwt && !!definitions.hashingSecretKey, + enabled: definitions.enableHashingToken, } }, inject: [AUTH_DEFINITIONS_MODULE_OPTIONS], @@ -130,10 +132,10 @@ export class AuthModule implements NestModule { TokenService, - LoginQueryHandler, + LoginCommandHandler, GetCurrentUserByAccessTokenQueryHandler, GetCurrentUserByRefreshTokenQueryHandler, - UserLogoutQueryHandler, + UserLogoutCommandHandler, GetUserByJwtTokenAction, GetUserByRefreshTokenAction, @@ -217,7 +219,8 @@ export class AuthModule implements NestModule { JwtStrategy, RefreshTokenStrategy, - LoginQueryHandler, + LoginCommandHandler, + UserLogoutCommandHandler, GetCurrentUserByAccessTokenQueryHandler, GetCurrentUserByRefreshTokenQueryHandler, diff --git a/src/domain/actions/local-login.action.ts b/src/domain/actions/local-login.action.ts index 1c35b6a..ac94e74 100644 --- a/src/domain/actions/local-login.action.ts +++ b/src/domain/actions/local-login.action.ts @@ -39,9 +39,9 @@ export class LocalLoginAction { public constructor( @InjectAuthDefinitions() protected readonly authDefinitions: IAuthDefinitions, - protected readonly authRepository: AuthRepository, @InjectPasswordHasher() protected readonly passwordHasherService: PasswordHasherService, + protected readonly authRepository: AuthRepository, protected readonly tokenService: TokenService, protected readonly eventBus: EventBus ) {} @@ -108,7 +108,7 @@ export class LocalLoginAction { new UserLoggedInEvent(user, impersonated, validatedDto, token) ) ), - map(({ user: userData, token }) => ({ userData, token } as T)) + map(({ user: userData, token }) => ({ userData, token }) as T) ) } diff --git a/src/domain/definitions/auth-definitions.ts b/src/domain/definitions/auth-definitions.ts index 7a57742..e7845fa 100644 --- a/src/domain/definitions/auth-definitions.ts +++ b/src/domain/definitions/auth-definitions.ts @@ -31,6 +31,7 @@ export interface IAuthDefinitions { redactedFields?: string[] ignoredRoutes?: string[] + enableHashingToken?: boolean hashingSecretKey?: string transferTokenMethod?: AuthTransferTokenMethod diff --git a/src/domain/services/token.service.ts b/src/domain/services/token.service.ts index 03f0fb0..70ed996 100644 --- a/src/domain/services/token.service.ts +++ b/src/domain/services/token.service.ts @@ -62,6 +62,7 @@ export class TokenService { userInfo[this.authDefinitions.usernameField || 'username'] ), sub: this.hashTextService.encode(userInfo.id), + iss: 'self-signed', }).pipe( map((payload) => this.jwtService.sign(payload, { diff --git a/src/domain/strategies/jwt.strategy.ts b/src/domain/strategies/jwt.strategy.ts index 0bd5c4d..620c812 100644 --- a/src/domain/strategies/jwt.strategy.ts +++ b/src/domain/strategies/jwt.strategy.ts @@ -17,7 +17,7 @@ import { IAuthDefinitions } from '../index' export const JWT_STRATEGY_NAME: string = 'jwt' -const cookieExtractor: ( +export const cookieExtractor: ( transferTokenMethod: AuthTransferTokenMethod | undefined ) => JwtFromRequestFunction = (transferTokenMethod) => @@ -33,7 +33,7 @@ const cookieExtractor: ( ) } -const accessTokenHeaderExtractor: ( +export const accessTokenHeaderExtractor: ( transferTokenMethod: AuthTransferTokenMethod | undefined ) => JwtFromRequestFunction = (transferTokenMethod) => @@ -53,7 +53,7 @@ const accessTokenHeaderExtractor: ( @Injectable() export class JwtStrategy extends PassportStrategy(Strategy, JWT_STRATEGY_NAME) { - private readonly jwtFromRequest: JwtFromRequestFunction + private readonly _jwtFromRequest: JwtFromRequestFunction public constructor( @InjectAuthDefinitions() @@ -72,7 +72,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, JWT_STRATEGY_NAME) { passReqToCallback: true, }) - this.jwtFromRequest = jwtFromRequest + this._jwtFromRequest = jwtFromRequest } public async validate( @@ -81,7 +81,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, JWT_STRATEGY_NAME) { ): Promise { return this.queryBus.execute( new GetCurrentUserByAccessTokenQuery( - this.jwtFromRequest(request) || '', + this._jwtFromRequest(request) || '', payload ) ) diff --git a/src/domain/strategies/local.strategy.ts b/src/domain/strategies/local.strategy.ts index 344d1c1..2ddc36f 100644 --- a/src/domain/strategies/local.strategy.ts +++ b/src/domain/strategies/local.strategy.ts @@ -1,11 +1,11 @@ import { Injectable } from '@nestjs/common' -import { QueryBus } from '@nestjs/cqrs' +import { CommandBus } from '@nestjs/cqrs' import { PassportStrategy } from '@nestjs/passport' import { Strategy } from 'passport-local' -import { LoginQuery } from '../../application/queries' import { InjectAuthDefinitions } from '../decorators' import type { IAuthUserEntityForResponse } from '../definitions' import { IAuthDefinitions } from '../index' +import { LoginCommand } from '../../application/actions' export const LOCAL_STRATEGY_NAME: string = 'local' @@ -14,7 +14,7 @@ export class LocalStrategy extends PassportStrategy(Strategy) { public constructor( @InjectAuthDefinitions() protected readonly authDefinitions: IAuthDefinitions, - protected readonly queryBus: QueryBus + protected readonly bus: CommandBus ) { super({ usernameField: authDefinitions.usernameField || 'username', @@ -28,8 +28,8 @@ export class LocalStrategy extends PassportStrategy(Strategy) { username: string, password: string ): Promise { - return this.queryBus.execute( - new LoginQuery({ + return this.bus.execute( + new LoginCommand({ ...request.body, username, password, diff --git a/src/presentation/interceptors/helpers/cookie.interceptor.helper.ts b/src/presentation/interceptors/helpers/cookie.interceptor.helper.ts index dd0707d..54407d4 100644 --- a/src/presentation/interceptors/helpers/cookie.interceptor.helper.ts +++ b/src/presentation/interceptors/helpers/cookie.interceptor.helper.ts @@ -33,6 +33,7 @@ export const transferTokenFromResponseToCookie: ( const token: IJwtTokenResponse = authResponse.token + // eslint-disable-next-line guard-for-in for (const responseKey in mapKeys) { const currentToken = token[responseKey] const currentKey = mapKeys[responseKey] diff --git a/src/presentation/interceptors/user-logout.interceptor.ts b/src/presentation/interceptors/user-logout.interceptor.ts index 86f00f9..4e8a69b 100644 --- a/src/presentation/interceptors/user-logout.interceptor.ts +++ b/src/presentation/interceptors/user-logout.interceptor.ts @@ -4,13 +4,13 @@ import { Injectable, NestInterceptor, } from '@nestjs/common' -import { QueryBus } from '@nestjs/cqrs' +import { CommandBus } from '@nestjs/cqrs' import { asyncScheduler, map, mergeMap, Observable, scheduled } from 'rxjs' -import { UserLogoutQuery } from '../../application/queries' +import { UserLogoutCommand } from '../../application/actions' @Injectable() export class UserLogoutInterceptor implements NestInterceptor { - public constructor(protected readonly queryBus: QueryBus) {} + public constructor(protected readonly _bus: CommandBus) {} public intercept( context: ExecutionContext, @@ -19,7 +19,7 @@ export class UserLogoutInterceptor implements NestInterceptor { return next.handle().pipe( mergeMap((res) => { return scheduled( - this.queryBus.execute(new UserLogoutQuery(context)), + this._bus.execute(new UserLogoutCommand(context)), asyncScheduler ).pipe(map(() => res)) })