mirror of
https://github.com/ershisan99/flashcards-api.git
synced 2025-12-17 12:33:22 +00:00
add more auth docs
This commit is contained in:
@@ -13,7 +13,15 @@ import {
|
||||
UseGuards,
|
||||
} from '@nestjs/common'
|
||||
import { CommandBus } from '@nestjs/cqrs'
|
||||
import { ApiBody, ApiTags } from '@nestjs/swagger'
|
||||
import {
|
||||
ApiBadRequestResponse,
|
||||
ApiBody,
|
||||
ApiNoContentResponse,
|
||||
ApiNotFoundResponse,
|
||||
ApiOperation,
|
||||
ApiTags,
|
||||
ApiUnauthorizedResponse,
|
||||
} from '@nestjs/swagger'
|
||||
import { Response as ExpressResponse } from 'express'
|
||||
|
||||
import { Cookies } from '../../infrastructure/decorators'
|
||||
@@ -25,6 +33,7 @@ import {
|
||||
RegistrationDto,
|
||||
ResendVerificationEmailDto,
|
||||
} from './dto'
|
||||
import { ResetPasswordDto } from './dto/reset-password.dto'
|
||||
import { LoginResponse, UserEntity } from './entities/auth.entity'
|
||||
import { JwtAuthGuard, JwtRefreshGuard, LocalAuthGuard } from './guards'
|
||||
import {
|
||||
@@ -43,6 +52,9 @@ import {
|
||||
export class AuthController {
|
||||
constructor(private commandBus: CommandBus) {}
|
||||
|
||||
@ApiOperation({ description: 'Retrieve current user data.' })
|
||||
@ApiUnauthorizedResponse({ description: 'Not logged in' })
|
||||
@ApiBadRequestResponse({ description: 'User not found' })
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('me')
|
||||
async getUserData(@Request() req): Promise<UserEntity> {
|
||||
@@ -51,8 +63,10 @@ export class AuthController {
|
||||
return await this.commandBus.execute(new GetCurrentUserDataCommand(userId))
|
||||
}
|
||||
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ description: 'Sign in using email and password. Must have an account to do so.' })
|
||||
@ApiUnauthorizedResponse({ description: 'Invalid credentials' })
|
||||
@ApiBody({ type: LoginDto })
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@UseGuards(LocalAuthGuard)
|
||||
@Post('login')
|
||||
async login(
|
||||
@@ -76,73 +90,101 @@ export class AuthController {
|
||||
return { accessToken: req.user.data.accessToken }
|
||||
}
|
||||
|
||||
@ApiOperation({ description: 'Create a new user account' })
|
||||
@ApiBadRequestResponse({ description: 'Email already exists' })
|
||||
@HttpCode(HttpStatus.CREATED)
|
||||
@Post('sign-up')
|
||||
async registration(@Body() registrationData: RegistrationDto): Promise<UserEntity> {
|
||||
return await this.commandBus.execute(new CreateUserCommand(registrationData))
|
||||
}
|
||||
|
||||
@ApiOperation({ description: 'Verify user email' })
|
||||
@ApiBadRequestResponse({ description: 'Email has already been verified' })
|
||||
@ApiNotFoundResponse({ description: 'User not found' })
|
||||
@ApiNoContentResponse({ description: 'Email verified successfully' })
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@Post('verify-email')
|
||||
async confirmRegistration(@Body() body: EmailVerificationDto): Promise<void> {
|
||||
return await this.commandBus.execute(new VerifyEmailCommand(body.code))
|
||||
}
|
||||
|
||||
@ApiOperation({ description: 'Send verification email again' })
|
||||
@ApiBadRequestResponse({ description: 'Email has already been verified' })
|
||||
@ApiNotFoundResponse({ description: 'User not found' })
|
||||
@ApiNoContentResponse({ description: 'Verification email sent successfully' })
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@Post('resend-verification-email')
|
||||
async resendVerificationEmail(@Body() body: ResendVerificationEmailDto): Promise<void> {
|
||||
return await this.commandBus.execute(new ResendVerificationEmailCommand(body.userId))
|
||||
}
|
||||
|
||||
@ApiOperation({ description: 'Sign current user out' })
|
||||
@ApiUnauthorizedResponse({ description: 'Not logged in' })
|
||||
@ApiNoContentResponse({ description: 'Logged out successfully' })
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('logout')
|
||||
async logout(
|
||||
@Cookies('refreshToken') refreshToken: string,
|
||||
@Cookies('accessToken') accessToken: string,
|
||||
@Res({ passthrough: true }) res: ExpressResponse
|
||||
): Promise<void> {
|
||||
if (!refreshToken) throw new UnauthorizedException()
|
||||
await this.commandBus.execute(new LogoutCommand(refreshToken))
|
||||
if (!accessToken) throw new UnauthorizedException()
|
||||
await this.commandBus.execute(new LogoutCommand(accessToken))
|
||||
res.clearCookie('accessToken')
|
||||
res.clearCookie('refreshToken')
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ description: 'Get new access token using refresh token' })
|
||||
@ApiUnauthorizedResponse({ description: 'Invalid or missing refreshToken' })
|
||||
@ApiNoContentResponse({ description: 'New tokens generated successfully' })
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@UseGuards(JwtRefreshGuard)
|
||||
@Post('refresh-token')
|
||||
async refreshToken(
|
||||
@Request() req,
|
||||
@Response({ passthrough: true }) res: ExpressResponse
|
||||
): Promise<LoginResponse> {
|
||||
): Promise<void> {
|
||||
if (!req.cookies?.refreshToken) throw new UnauthorizedException()
|
||||
const userId = req.user.id
|
||||
const newTokens = await this.commandBus.execute(new RefreshTokenCommand(userId))
|
||||
|
||||
res.cookie('refreshToken', newTokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
// secure: true,
|
||||
sameSite: 'none',
|
||||
path: '/v1/auth/refresh-token',
|
||||
expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
|
||||
secure: true,
|
||||
})
|
||||
res.cookie('accessToken', newTokens.accessToken, {
|
||||
httpOnly: true,
|
||||
sameSite: 'none',
|
||||
secure: true,
|
||||
})
|
||||
|
||||
return {
|
||||
accessToken: newTokens.accessToken,
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@ApiOperation({ description: 'Send password recovery email' })
|
||||
@ApiBadRequestResponse({ description: 'Email has already been verified' })
|
||||
@ApiNotFoundResponse({ description: 'User not found' })
|
||||
@ApiNoContentResponse({ description: 'Password recovery email sent successfully' })
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@Post('recover-password')
|
||||
async recoverPassword(@Body() body: RecoverPasswordDto): Promise<void> {
|
||||
return await this.commandBus.execute(new SendPasswordRecoveryEmailCommand(body.email))
|
||||
}
|
||||
|
||||
@ApiOperation({ description: 'Reset password' })
|
||||
@ApiBadRequestResponse({ description: 'Password is required' })
|
||||
@ApiNotFoundResponse({ description: 'Incorrect or expired password reset token' })
|
||||
@ApiNoContentResponse({ description: 'Password reset successfully' })
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@Post('reset-password/:token')
|
||||
async resetPassword(
|
||||
@Body('password') password: string,
|
||||
@Body() body: ResetPasswordDto,
|
||||
@Param('token') token: string
|
||||
): Promise<void> {
|
||||
return await this.commandBus.execute(new ResetPasswordCommand(token, password))
|
||||
return await this.commandBus.execute(new ResetPasswordCommand(token, body.password))
|
||||
}
|
||||
}
|
||||
|
||||
6
src/modules/auth/dto/reset-password.dto.ts
Normal file
6
src/modules/auth/dto/reset-password.dto.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Length } from 'class-validator'
|
||||
|
||||
export class ResetPasswordDto {
|
||||
@Length(3, 30)
|
||||
password: string
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import jwt from 'jsonwebtoken'
|
||||
import { UsersRepository } from '../../users/infrastructure/users.repository'
|
||||
|
||||
export class LogoutCommand {
|
||||
constructor(public readonly refreshToken: string) {}
|
||||
constructor(public readonly accessToken: string) {}
|
||||
}
|
||||
|
||||
@CommandHandler(LogoutCommand)
|
||||
@@ -15,7 +15,7 @@ export class LogoutHandler implements ICommandHandler<LogoutCommand> {
|
||||
private readonly logger = new Logger(LogoutHandler.name)
|
||||
|
||||
async execute(command: LogoutCommand) {
|
||||
const token = command.refreshToken
|
||||
const token = command.accessToken
|
||||
|
||||
const secretKey = process.env.JWT_SECRET_KEY
|
||||
|
||||
|
||||
Reference in New Issue
Block a user