Skip to content

Commit

Permalink
✨ feat : Token Refresh 요청 API 구현(#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
jinddings committed Nov 7, 2024
1 parent 4357a54 commit a9c95d7
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 7 deletions.
33 changes: 33 additions & 0 deletions BE/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion BE/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@
"@nestjs/schedule": "^4.1.1",
"@nestjs/swagger": "^8.0.1",
"@nestjs/typeorm": "^10.0.2",
"@types/passport-jwt": "^4.0.1",
"@nestjs/websockets": "^10.4.7",
"@types/cookie-parser": "^1.4.7",
"@types/passport-jwt": "^4.0.1",
"axios": "^1.7.7",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"cookie-parser": "^1.4.7",
"cross-env": "^7.0.3",
"docker": "^1.0.0",
"dotenv": "^16.4.5",
Expand Down
45 changes: 41 additions & 4 deletions BE/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,24 @@ import {
Post,
Get,
Body,
Req,
ValidationPipe,
UseGuards,
Req,
Res,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiOperation } from '@nestjs/swagger';
import { AuthService } from './auth.service';
import { AuthCredentialsDto } from './dto/authCredentials.dto';
import { Request } from 'express';
import { Request, Response } from 'express';
import { ConfigService } from '@nestjs/config';

@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
constructor(
private authService: AuthService,
private configService: ConfigService,
) {}

@ApiOperation({ summary: '회원 가입 API' })
@Post('/signup')
Expand All @@ -24,7 +29,7 @@ export class AuthController {
}

@ApiOperation({ summary: '로그인 API' })
@Get('/login')
@Post('/login')
loginWithCredentials(
@Body(ValidationPipe) authCredentialsDto: AuthCredentialsDto,
) {
Expand All @@ -37,4 +42,36 @@ export class AuthController {
test(@Req() req: Request) {
return req;
}

@ApiOperation({ summary: 'Kakao 로그인 API' })
@Get('/kakao')
async kakaoLogin(
@Body() authCredentialsDto: AuthCredentialsDto,
@Res() res: Response,
) {
const { accessToken, refreshToken } =
await this.authService.kakaoLoginUser(authCredentialsDto);

res.cookie('refreshToken', refreshToken, { httpOnly: true });
res.cookie('accessToken', accessToken, { httpOnly: true });
res.cookie('isRefreshToken', true, { httpOnly: true });
return res.redirect(this.configService.get<string>('CLIENT_URL'));
}

@ApiOperation({ summary: 'Refresh Token 요청 API' })
@Get('/refresh')
async refresh(@Req() req: Request, @Res() res: Response) {
const refreshToken = req.cookies['refreshToken'];
const accessToken = req.cookies['accessToken'];

if (!refreshToken || !accessToken) {
return res.status(401).send();
}

const newAccessToken = await this.authService.refreshToken(refreshToken);

res.cookie('accessToken', newAccessToken, { httpOnly: true });
res.cookie('isRefreshToken', true, { httpOnly: true });
return res.status(200).send();
}
}
39 changes: 37 additions & 2 deletions BE/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class AuthService {

async loginUser(
authCredentialsDto: AuthCredentialsDto,
): Promise<{ accessToken: string }> {
): Promise<{ accessToken: string; refreshToken: string }> {
const { email, password } = authCredentialsDto;
const user = await this.userRepository.findOne({ where: { email } });

Expand All @@ -31,11 +31,16 @@ export class AuthService {

this.setCurrentRefreshToken(refreshToken, user.id);

return { accessToken };
return { accessToken, refreshToken };
}
throw new UnauthorizedException('Please check your login credentials');
}

async kakaoLoginUser(
authCredentialsDto: AuthCredentialsDto,
): Promise<{ accessToken: string; refreshToken: string }> {
return await this.getJWTToken(authCredentialsDto);
}
async getJWTToken(authCredentialsDto: AuthCredentialsDto) {
const accessToken = await this.generateAccessToken(authCredentialsDto);
const refreshToken = await this.generateRefreshToken(authCredentialsDto);
Expand Down Expand Up @@ -90,4 +95,34 @@ export class AuthService {
currentRefreshTokenExpiresAt,
});
}

async refreshToken(refreshToken: string): Promise<string> {
try {
const decodedRefreshToken = this.jwtService.verify(refreshToken, {
secret: this.configService.get<string>('JWT_REFRESH_SECRET'),
});

const user = decodedRefreshToken.email
? await this.userRepository.findOne({
where: { email: decodedRefreshToken.email },
})
: await this.userRepository.findOne({
where: { kakaoId: decodedRefreshToken.kakaoId },
});

const isRefreshTokenMatching = await bcrypt.compare(
refreshToken,
user.currentRefreshToken,
);

if (!isRefreshTokenMatching) {
throw new UnauthorizedException('Invalid Token');
}

const accessToken = this.generateAccessToken(user.toAuthCredentialsDto());
return accessToken;
} catch (error) {
throw new UnauthorizedException('Invalid Token');
}
}
}
10 changes: 10 additions & 0 deletions BE/src/auth/user.entity.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { AuthCredentialsDto } from './dto/authCredentials.dto';

@Entity()
export class User extends BaseEntity {
Expand All @@ -22,4 +23,13 @@ export class User extends BaseEntity {

@Column({ type: 'datetime', nullable: true })
currentRefreshTokenExpiresAt: Date;

toAuthCredentialsDto(): AuthCredentialsDto {
if (this.kakaoId === -1) {
return {
email: this.email,
password: this.password,
};
}
}
}
2 changes: 2 additions & 0 deletions BE/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NestFactory } from '@nestjs/core';
import { Logger } from '@nestjs/common';
import { AppModule } from './app.module';
import { setupSwagger } from './util/swagger';
import * as cookieParser from 'cookie-parser';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
Expand All @@ -14,6 +15,7 @@ async function bootstrap() {
optionsSuccessStatus: 204,
});

app.use(cookieParser());
await app.listen(process.env.PORT ?? 3000);
}

Expand Down

0 comments on commit a9c95d7

Please sign in to comment.