- 로그인 이후 access/refresh token 생성 및 반환 로직 구현
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
envFilePath: [
|
||||
'.env.common',
|
||||
`.env.${process.env.NODE_ENV}`
|
||||
`.env.${process.env.NODE_ENV}`,
|
||||
'.env.common'
|
||||
]
|
||||
})
|
||||
]
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
@@ -25,7 +22,7 @@ async function bootstrap() {
|
||||
|
||||
app.enableShutdownHooks();
|
||||
|
||||
await app.listen(process.env.PORT ?? 3000);
|
||||
await app.listen(process.env.PORT ?? 3000, () => { process.env.NODE_ENV !== 'prod' && console.log(`servier is running on ${process.env.PORT}`) });
|
||||
|
||||
}
|
||||
bootstrap();
|
||||
|
||||
24
src/middleware/auth/auth.module.ts
Normal file
24
src/middleware/auth/auth.module.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { forwardRef, Module } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { JwtStrategy } from './jwt.strategy';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { AccountModule } from 'src/modules/account/account.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule,
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
useFactory: (config: ConfigService) => ({
|
||||
secret: config.get<string>('JWT_SECRET')!,
|
||||
signOptions: { expiresIn: '1h' }
|
||||
})
|
||||
}),
|
||||
forwardRef(() => AccountModule)
|
||||
],
|
||||
providers: [AuthService, JwtStrategy],
|
||||
exports: [AuthService]
|
||||
})
|
||||
export class AuthModule{}
|
||||
23
src/middleware/auth/auth.service.ts
Normal file
23
src/middleware/auth/auth.service.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(private readonly jwtService: JwtService) {}
|
||||
|
||||
generateTokens(payload: any) {
|
||||
const accessToken = this.jwtService.sign(payload, { expiresIn: '1h' });
|
||||
const refreshToken = this.jwtService.sign(payload, { expiresIn: '7d' });
|
||||
|
||||
return { accessToken, refreshToken };
|
||||
}
|
||||
|
||||
refreshTokens(refreshToken: string) {
|
||||
try {
|
||||
const payload = this.jwtService.verify(refreshToken);
|
||||
return this.generateTokens(payload);
|
||||
} catch (e) {
|
||||
throw new UnauthorizedException('Invalid Refresh Token');
|
||||
}
|
||||
}
|
||||
}
|
||||
5
src/middleware/auth/jwt.guard.ts
Normal file
5
src/middleware/auth/jwt.guard.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
@Injectable()
|
||||
export class JwtGuard extends AuthGuard('jwt') {}
|
||||
27
src/middleware/auth/jwt.strategy.ts
Normal file
27
src/middleware/auth/jwt.strategy.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import { AccountRepo } from 'src/modules/account/account.repo';
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(
|
||||
private readonly accountRepo: AccountRepo
|
||||
, private readonly configService: ConfigService
|
||||
, private readonly jwtService: JwtService
|
||||
) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: configService.get<string>('JWT_SECRET')!
|
||||
});
|
||||
}
|
||||
|
||||
async validate(payload: any) {
|
||||
const account = await this.accountRepo.findById(payload.id);
|
||||
if (!account || account.length < 1) throw new UnauthorizedException();
|
||||
return account[0];
|
||||
}
|
||||
}
|
||||
@@ -36,8 +36,14 @@ export class AccountController {
|
||||
}
|
||||
|
||||
@Post('signup')
|
||||
async signup(@Body() body: SignupRequest): Promise<LoginResponse> {
|
||||
async signup(@Body() body: SignupRequest): Promise<SignupResponse> {
|
||||
const result = await this.accountService.signup(body);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Post('login')
|
||||
async login(@Body() body: LoginRequest): Promise<LoginResponse> {
|
||||
const result = await this.accountService.login(body);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { forwardRef, Module } from "@nestjs/common";
|
||||
import { AccountController } from "./account.controller";
|
||||
import { AccountRepo } from "./account.repo";
|
||||
import { AccountService } from "./account.service";
|
||||
|
||||
import { AuthModule } from 'src/middleware/auth/auth.module';
|
||||
@Module({
|
||||
imports: [forwardRef(() => AuthModule)],
|
||||
controllers: [AccountController],
|
||||
providers: [AccountService, AccountRepo],
|
||||
exports: [AccountService, AccountRepo]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Inject, Injectable } from "@nestjs/common";
|
||||
import * as schema from "drizzle/schema";
|
||||
import { countDistinct, and, eq } from 'drizzle-orm';
|
||||
import { countDistinct, and, eq, not } from 'drizzle-orm';
|
||||
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
||||
|
||||
@Injectable()
|
||||
@@ -38,5 +38,36 @@ export class AccountRepo {
|
||||
});
|
||||
}
|
||||
|
||||
async
|
||||
async login(
|
||||
type: 'email' | 'accountId'
|
||||
, id: string
|
||||
) {
|
||||
const condition = type === 'email'
|
||||
? eq(schema.account.email, id)
|
||||
: eq(schema.account.accountId, id);
|
||||
return this
|
||||
.db
|
||||
.select()
|
||||
.from(schema.account)
|
||||
.where(
|
||||
and(
|
||||
condition,
|
||||
eq(schema.account.isDeleted, false),
|
||||
eq(schema.account.status, 'active')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async findById(id: string) {
|
||||
return await this
|
||||
.db
|
||||
.select()
|
||||
.from(schema.account)
|
||||
.where(
|
||||
and(
|
||||
eq(schema.account.id, id),
|
||||
eq(schema.account.isDeleted, false)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,14 @@ import { MailerService } from "src/util/mailer/mailer.service";
|
||||
import { Generator } from "src/util/generator";
|
||||
import Redis from "ioredis";
|
||||
import { Converter } from "src/util/converter";
|
||||
import { AuthService } from "src/middleware/auth/auth.service";
|
||||
|
||||
@Injectable()
|
||||
export class AccountService {
|
||||
constructor(
|
||||
private readonly accountRepo: AccountRepo
|
||||
, private readonly mailerService: MailerService
|
||||
, private readonly authService: AuthService
|
||||
, @Inject("REDIS") private readonly redis: Redis
|
||||
) {}
|
||||
|
||||
@@ -70,4 +72,41 @@ export class AccountService {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async login(data: DTO.LoginRequest): Promise<DTO.LoginResponse> {
|
||||
const { type, id, password } = data;
|
||||
const queryResult = await this.accountRepo.login(type, id);
|
||||
const typeValue = type === 'email' ? '이메일' : '아이디';
|
||||
console.log(queryResult);
|
||||
if (!queryResult || (queryResult.length < 1)) {
|
||||
return {
|
||||
success: false,
|
||||
message: `존재하지 않는 ${typeValue} 입니다.`
|
||||
};
|
||||
}
|
||||
const hashedPassword = queryResult[0].password;
|
||||
const isPasswordMatch = Converter.comparePassword(password, hashedPassword);
|
||||
if (!isPasswordMatch) {
|
||||
return {
|
||||
success: false,
|
||||
message: `비밀번호가 맞지 않습니다.`
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
const { id, accountId, name, nickname, email, status, isDeleted, birthday } = queryResult[0];
|
||||
|
||||
const payload = {
|
||||
id, accountId, name, nickname, email, status, isDeleted, birthday
|
||||
};
|
||||
|
||||
const { accessToken, refreshToken } = this.authService.generateTokens(payload);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
accessToken: accessToken,
|
||||
refreshToken: refreshToken
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
export class LoginResponseDto {
|
||||
success: boolean;
|
||||
accessToken?: string;
|
||||
refreshToken?: string;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user