Skip to content

Commit

Permalink
Merge pull request #69 from tscenping/yubin
Browse files Browse the repository at this point in the history
feat: fetchSockets를 이용해 socket instance 가져오기, JwtAuthGuard에서 AuthGuard('access')로 변경
  • Loading branch information
cjho0316 authored Dec 11, 2023
2 parents 2eb6907 + f439a1f commit 4bce1f0
Show file tree
Hide file tree
Showing 17 changed files with 262 additions and 121 deletions.
13 changes: 6 additions & 7 deletions database/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,6 @@ INSERT INTO public.block (id, "createdAt", "updatedAt", "deletedAt", "fromUserId
INSERT INTO public.channel (id, "createdAt", "updatedAt", "deletedAt", name, "channelType", password, "ownerId") VALUES (1, '2023-12-02 03:20:33.185711+00', '2023-12-02 03:20:33.185711+00', NULL, 'publicch1', 'PUBLIC', NULL, 5);
INSERT INTO public.channel (id, "createdAt", "updatedAt", "deletedAt", name, "channelType", password, "ownerId") VALUES (2, '2023-12-02 05:12:25.658717+00', '2023-12-02 05:12:25.658717+00', NULL, 'protected', 'PROTECTED', '$2b$10$CCF.sHJSpvI79KR3UNus2eC4U2izenVra3c7KjnGA2UFKxwjt.ZCy', 1);
INSERT INTO public.channel (id, "createdAt", "updatedAt", "deletedAt", name, "channelType", password, "ownerId") VALUES (3, '2023-12-02 05:28:14.898349+00', '2023-12-02 05:28:14.898349+00', NULL, '상예키와디엠', 'DM', NULL, 4);
INSERT INTO public.channel (id, "createdAt", "updatedAt", "deletedAt", name, "channelType", password, "ownerId") VALUES (4, '2023-12-02 06:28:49.86125+00', '2023-12-02 06:28:49.86125+00', NULL, '윱최와디엠', 'DM', NULL, 4);


--
Expand Down Expand Up @@ -478,11 +477,11 @@ INSERT INTO public.friend (id, "createdAt", "updatedAt", "deletedAt", "fromUserI
-- Data for Name: user; Type: TABLE DATA; Schema: public; Owner: jiyun
--

INSERT INTO public."user" (id, "createdAt", "updatedAt", "deletedAt", nickname, avatar, email, "isMfaEnabled", "ladderScore", "ladderMaxScore", "winCount", "loseCount", "refreshToken", "gameSocketId", "channelSocketId", "statusMessage", status) VALUES (1, '2023-12-02 03:19:34.302102+00', '2023-12-02 08:34:39.769368+00', NULL, 'yubchoi', NULL, '[email protected]', false, 1200, 1200, 0, 0, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNzAxNDk0NjIwLCJleHAiOjE3MDIwOTk0MjB9.oKRM-lR_0G1oB6rFt8UslxwVjaYxNP7kotXgNdqks_g', NULL, NULL, NULL, 'OFFLINE');
INSERT INTO public."user" (id, "createdAt", "updatedAt", "deletedAt", nickname, avatar, email, "isMfaEnabled", "ladderScore", "ladderMaxScore", "winCount", "loseCount", "refreshToken", "gameSocketId", "channelSocketId", "statusMessage", status) VALUES (2, '2023-12-02 03:19:48.160854+00', '2023-12-02 08:34:39.769368+00', NULL, 'jiyun', NULL, '[email protected]', false, 1200, 1200, 0, 0, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiaWF0IjoxNzAxNDk0NjIzLCJleHAiOjE3MDIwOTk0MjN9.Q6YigxUyghkzZZWLVnJmXN6nvdY8HPi1aLqGcrH1Qos', NULL, NULL, NULL, 'OFFLINE');
INSERT INTO public."user" (id, "createdAt", "updatedAt", "deletedAt", nickname, avatar, email, "isMfaEnabled", "ladderScore", "ladderMaxScore", "winCount", "loseCount", "refreshToken", "gameSocketId", "channelSocketId", "statusMessage", status) VALUES (5, '2023-12-02 03:20:03.833044+00', '2023-12-02 08:34:39.769368+00', NULL, 'sangyeki', NULL, '[email protected]', false, 1200, 1200, 0, 0, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NSwiaWF0IjoxNzAxNDk0NjMzLCJleHAiOjE3MDIwOTk0MzN9.vRz9CsHVydWjeIa6Lne7SECZuDOztadWVBugepxYpIQ', NULL, NULL, NULL, 'OFFLINE');
INSERT INTO public."user" (id, "createdAt", "updatedAt", "deletedAt", nickname, avatar, email, "isMfaEnabled", "ladderScore", "ladderMaxScore", "winCount", "loseCount", "refreshToken", "gameSocketId", "channelSocketId", "statusMessage", status) VALUES (3, '2023-12-02 03:19:53.482907+00', '2023-12-02 08:34:39.769368+00', NULL, 'jang-cho', NULL, '[email protected]', false, 1200, 1200, 0, 0, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MywiaWF0IjoxNzAxNDk0NjM5LCJleHAiOjE3MDIwOTk0Mzl9._d_WJ7XL8l8vdZFOlb7lCc4MRQkVREZm0eqEYtawM_E', NULL, NULL, NULL, 'OFFLINE');
INSERT INTO public."user" (id, "createdAt", "updatedAt", "deletedAt", nickname, avatar, email, "isMfaEnabled", "ladderScore", "ladderMaxScore", "winCount", "loseCount", "refreshToken", "gameSocketId", "channelSocketId", "statusMessage", status) VALUES (4, '2023-12-02 03:19:59.864485+00', '2023-12-02 08:34:41.718081+00', NULL, 'him', NULL, '[email protected]', false, 1200, 1200, 0, 0, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NCwiaWF0IjoxNzAxNDk0NzY4LCJleHAiOjE3MDIwOTk1Njh9.nNd3mlwIO4BORIM8fbn4PtnpAs84rr9WM_rU-Ee8Sk0', NULL, NULL, NULL, 'OFFLINE');
INSERT INTO public."user" (id, "createdAt", "updatedAt", "deletedAt", nickname, avatar, email, "isMfaEnabled", "ladderScore", "ladderMaxScore", "winCount", "loseCount", "refreshToken", "gameSocketId", "channelSocketId", "statusMessage", status) VALUES (1, '2023-12-02 03:19:34.302102+00', '2023-12-09 14:14:20.691057+00', NULL, 'yubchoi', NULL, '[email protected]', false, 1200, 1200, 0, 0, '$2b$10$IdThmWc09Pals6rEY4bPBuI4f1dkjCcxxURSt67pbz3PL9uD0fwnG', NULL, NULL, NULL, 'OFFLINE');
INSERT INTO public."user" (id, "createdAt", "updatedAt", "deletedAt", nickname, avatar, email, "isMfaEnabled", "ladderScore", "ladderMaxScore", "winCount", "loseCount", "refreshToken", "gameSocketId", "channelSocketId", "statusMessage", status) VALUES (2, '2023-12-02 03:19:48.160854+00', '2023-12-09 14:14:25.68037+00', NULL, 'jiyun', NULL, '[email protected]', false, 1200, 1200, 0, 0, '$2b$10$D8tF4ELtxQ9MOHGguWi91OQpAuEmcxUkf9LITy6Esl69RwemTX5mC', NULL, NULL, NULL, 'OFFLINE');
INSERT INTO public."user" (id, "createdAt", "updatedAt", "deletedAt", nickname, avatar, email, "isMfaEnabled", "ladderScore", "ladderMaxScore", "winCount", "loseCount", "refreshToken", "gameSocketId", "channelSocketId", "statusMessage", status) VALUES (5, '2023-12-02 03:20:03.833044+00', '2023-12-09 14:14:58.478118+00', NULL, 'sangyeki', NULL, '[email protected]', false, 1200, 1200, 0, 0, '$2b$10$izrDKTQY5PSOhf8cQG3B/eZMXgYkUSOmpFQ5REuHfwjyUxhrY6H0S', NULL, NULL, NULL, 'OFFLINE');
INSERT INTO public."user" (id, "createdAt", "updatedAt", "deletedAt", nickname, avatar, email, "isMfaEnabled", "ladderScore", "ladderMaxScore", "winCount", "loseCount", "refreshToken", "gameSocketId", "channelSocketId", "statusMessage", status) VALUES (3, '2023-12-02 03:19:53.482907+00', '2023-12-09 14:15:02.136635+00', NULL, 'jang-cho', NULL, '[email protected]', false, 1200, 1200, 0, 0, '$2b$10$Qjdw7pPwL8o4HeNTslTzXOswxEUmU0prpxpybk2AWcm6ZCsAgRCzG', NULL, NULL, NULL, 'OFFLINE');
INSERT INTO public."user" (id, "createdAt", "updatedAt", "deletedAt", nickname, avatar, email, "isMfaEnabled", "ladderScore", "ladderMaxScore", "winCount", "loseCount", "refreshToken", "gameSocketId", "channelSocketId", "statusMessage", status) VALUES (4, '2023-12-02 03:19:59.864485+00', '2023-12-09 14:14:38.660236+00', NULL, 'him', NULL, '[email protected]', false, 1200, 1200, 0, 0, '$2b$10$3GZbxg4I1INf8fh/6I6tfOSnYXFHo8A18ExPm2olC7Xz5n6m8J11K', NULL, NULL, NULL, 'OFFLINE');


--
Expand Down Expand Up @@ -538,7 +537,7 @@ SELECT pg_catalog.setval('public.game_invitation_id_seq', 1, false);
-- Name: user_id_seq; Type: SEQUENCE SET; Schema: public; Owner: jiyun
--

SELECT pg_catalog.setval('public.user_id_seq', 5, true);
SELECT pg_catalog.setval('public.user_id_seq', 6, true);


--
Expand Down
35 changes: 25 additions & 10 deletions src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { AppService } from './../app.service';
import {
Body,
Controller,
Expand All @@ -8,18 +7,17 @@ import {
Res,
UseGuards,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
import { Response } from 'express';
import { User } from 'src/users/entities/user.entity';
import { SignupRequestDto } from '../users/dto/signup-request.dto';
import { UsersService } from '../users/users.service';
import { AppService } from './../app.service';
import { AuthService } from './auth.service';
import { UserSigninResponseDto } from './dto/user-signin-response.dto';
import { FtAuthService } from './ft-auth.service';
import { GetUser } from './get-user.decorator';
import { JwtAuthGuard } from './guards/jwt-auth.guard';
import { ChannelsGateway } from '../channels/channels.gateway';
import { UserStatus } from '../common/enum';

@Controller('auth')
@ApiTags('auth')
Expand Down Expand Up @@ -83,7 +81,7 @@ export class AuthController {
description:
'이미 존재하는 유저인지 체크하고, 유저상태(status)를 online으로 업데이트 합니다',
})
@UseGuards(JwtAuthGuard)
@UseGuards(AuthGuard('access'))
async signup(
@GetUser() user: User,
@Body() signupRequestDto: SignupRequestDto,
Expand Down Expand Up @@ -155,7 +153,7 @@ export class AuthController {
httpOnly: true,
secure: true,
sameSite: 'none',
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 1),
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 14), // 14일
});

const userSigninResponseDto: UserSigninResponseDto = {
Expand All @@ -167,7 +165,7 @@ export class AuthController {
Logger.log(
`updateRanking(ladderScore, id): ${user.ladderScore}, ${user.id}`,
);
await this.AppService.updateRanking(user.ladderScore, user.id);
await this.AppService.updateRanking(user.ladderScore, user.id); // TODO: 필요한 부분인가?

return res.send(userSigninResponseDto);
}
Expand All @@ -178,16 +176,33 @@ export class AuthController {
description:
'200 OK 만 반환한다. 쿠키에 있는 access,refresh token를 지운다.',
})
@UseGuards(JwtAuthGuard)
@UseGuards(AuthGuard('access'))
async signout(@GetUser() user: User, @Res() res: Response) {
await this.usersService.signout(user.id);

// TODO: 해당 유저의 상태 변경을 알리는 소켓 이벤트 전송

res.clearCookie('accessToken');
res.clearCookie('refreshToken');

// logout 시에는 프론트가 소켓을 disconnect 해준다.
return res.send();
}

@Patch('/refresh')
@ApiOperation({
summary: 'jwt access 토큰 재발급',
description: `jwt access 토큰 재발급.
refresh token이 유효하지 않으면 401 Unauthorized`,
})
@UseGuards(AuthGuard('refresh'))
async refresh(@GetUser() user: User, @Res() res: Response) {
const jwtAccessToken = await this.authService.generateAccessToken(user);
res.cookie('accessToken', jwtAccessToken, {
// httpOnly: true,
secure: true,
sameSite: 'none',
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 1), // 1일
});

return res.send();
}
}
29 changes: 16 additions & 13 deletions src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,27 @@ import { ConfigType } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppService } from 'src/app.service';
import { Game } from 'src/game/entities/game.entity';
import { GameRepository } from 'src/game/game.repository';
import { BlocksRepository } from 'src/users/blocks.repository';
import { Block } from 'src/users/entities/block.entity';
import { Friend } from 'src/users/entities/friend.entity';
import { FriendsRepository } from 'src/users/friends.repository';
import { ChannelUsersRepository } from '../channels/channel-users.repository';
import { ChannelsGateway } from '../channels/channels.gateway';
import { ChannelUser } from '../channels/entities/channel-user.entity';
import jwtConfig from '../config/jwt.config';
import { GameInvitation } from '../game/entities/game-invitation.entity';
import { GameInvitationRepository } from '../game/game-invitation.repository';
import { User } from '../users/entities/user.entity';
import { UsersRepository } from '../users/users.repository';
import { UsersService } from '../users/users.service';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { FtAuthService } from './ft-auth.service';
import { JwtAccessStrategy } from './jwt-access.strategy';
import { GameRepository } from 'src/game/game.repository';
import { Game } from 'src/game/entities/game.entity';
import { Friend } from 'src/users/entities/friend.entity';
import { Block } from 'src/users/entities/block.entity';
import { FriendsRepository } from 'src/users/friends.repository';
import { BlocksRepository } from 'src/users/blocks.repository';
import { AppService } from 'src/app.service';
import { GameInvitationRepository } from '../game/game-invitation.repository';
import { GameInvitation } from '../game/entities/game-invitation.entity';
import { ChannelsGateway } from '../channels/channels.gateway';
import { ChannelUsersRepository } from '../channels/channel-users.repository';
import { ChannelUser } from '../channels/entities/channel-user.entity';
import { JwtRefreshStrategy } from './jwt-refresh.strategy';

@Module({
imports: [
Expand All @@ -45,7 +46,6 @@ import { ChannelUser } from '../channels/entities/channel-user.entity';
providers: [
AuthService,
FtAuthService,
JwtAccessStrategy,
UsersRepository,
UsersService,
GameRepository,
Expand All @@ -56,6 +56,9 @@ import { ChannelUser } from '../channels/entities/channel-user.entity';
ChannelUsersRepository,
FriendsRepository,
AppService,
JwtAccessStrategy,
JwtRefreshStrategy,
],
exports: [JwtAccessStrategy, JwtRefreshStrategy],
})
export class AuthModule {}
33 changes: 24 additions & 9 deletions src/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ConflictException, Inject, Injectable, Logger } from '@nestjs/common';
import { ConfigType } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import { Socket } from 'socket.io';
import jwtConfig from 'src/config/jwt.config';
import { User } from 'src/users/entities/user.entity';
Expand All @@ -11,7 +12,7 @@ import { UserFindReturnDto } from './dto/user-find-return.dto';
@Injectable()
export class AuthService {
constructor(
private readonly userRepository: UsersRepository,
private readonly usersRepository: UsersRepository,
private readonly jwtService: JwtService,
@Inject(jwtConfig.KEY)
private readonly jwtConfigure: ConfigType<typeof jwtConfig>,
Expand All @@ -26,13 +27,13 @@ export class AuthService {
async findOrCreateUser(
userData: FtUserParamDto,
): Promise<UserFindReturnDto> {
const user = await this.userRepository.findOne({
const user = await this.usersRepository.findOne({
where: { email: userData.email },
});

if (!user) {
const user = this.userRepository.create(userData);
await this.userRepository.save(user);
const user = this.usersRepository.create(userData);
await this.usersRepository.save(user);

return { user };
}
Expand All @@ -46,7 +47,7 @@ export class AuthService {
}

async generateJwtToken(user: User) {
const payload = { id: user.id };
const payload = { id: user.id, email: user.email };
// accessToken 생성
const accessToken = await this.jwtService.signAsync(payload);

Expand All @@ -55,14 +56,21 @@ export class AuthService {
id: payload.id,
});

// refreshToken을 DB에 저장한다.
await this.userRepository.update(user.id, { refreshToken });
// 암호화된 refreshToken을 DB에 저장한다.
const saltOrRounds = 10;
const hashedRefreshToken = await bcrypt.hash(
refreshToken,
saltOrRounds,
);
await this.usersRepository.update(user.id, {
refreshToken: hashedRefreshToken,
});

return { jwtAccessToken: accessToken, jwtRefreshToken: refreshToken };
}

async validateNickname(nickname: string) {
const user = await this.userRepository.findUserByNickname(nickname);
const user = await this.usersRepository.findUserByNickname(nickname);
if (user) {
throw new ConflictException('이미 존재하는 닉네임입니다.');
}
Expand Down Expand Up @@ -90,7 +98,7 @@ export class AuthService {
}
this.logger.log(`payload: ${JSON.stringify(payload)}`);

const user = await this.userRepository.findOne({
const user = await this.usersRepository.findOne({
where: { id: payload.id },
});
if (!user) {
Expand All @@ -100,4 +108,11 @@ export class AuthService {

return user;
}

async generateAccessToken(user: User) {
const payload = { id: user.id, email: user.email };
const accessToken = await this.jwtService.signAsync(payload);

return accessToken;
}
}
4 changes: 4 additions & 0 deletions src/auth/dto/jwt-access-payload.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type JwtAccessPayloadDto = {
id: number;
email: string;
};
3 changes: 3 additions & 0 deletions src/auth/dto/jwt-refresh-payload.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type JwtRefreshPayloadDto = {
id: number;
};
45 changes: 37 additions & 8 deletions src/auth/guards/jwt-auth.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,41 @@ import { Observable } from 'rxjs';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
// console.log('----------------JwtAuthGuard----------------');
// // console.log('context:', context);
// console.log(context.switchToHttp().getRequest());
return super.canActivate(context);
}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
// console.log('----------------JwtAuthGuard----------------');
// // console.log('context:', context);
// console.log(context.switchToHttp().getRequest());
return super.canActivate(context);
}
}

// @Injectable()
// export class JwtAuthGuard extends PassportStrategy(Strategy, 'access') {
// constructor(
// @InjectRepository(UsersRepository)
// private readonly userRepository: UsersRepository,
// @Inject(jwtConfig.KEY)
// private readonly jwtConfigure: ConfigType<typeof jwtConfig>,
// ) {
// super({
// secretOrKey: jwtConfigure.secret,
// jwtFromRequest: ExtractJwt.fromExtractors([
// (request) => request.cookies?.accessToken,
// ]),
// // ignoreExpiration: true,
// });
// }

// async validate({ id }: JwtAccessPayloadDto): Promise<User> {
// console.log('jwt-auth.guard.ts: validate: id: ', id);
// const user = await this.userRepository.findOneBy({ id });

// if (!user) {
// throw new UnauthorizedException();
// }

// return user;
// }
// }
14 changes: 6 additions & 8 deletions src/auth/jwt-access.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ import { ExtractJwt, Strategy } from 'passport-jwt';
import jwtConfig from 'src/config/jwt.config';
import { User } from 'src/users/entities/user.entity';
import { UsersRepository } from 'src/users/users.repository';

type JwtPayload = {
id: number;
};
import { JwtAccessPayloadDto } from './dto/jwt-access-payload.dto';

@Injectable()
export class JwtAccessStrategy extends PassportStrategy(Strategy) {
export class JwtAccessStrategy extends PassportStrategy(Strategy, 'access') {
constructor(
@InjectRepository(UsersRepository)
private readonly userRepository: UsersRepository,
Expand All @@ -24,13 +21,14 @@ export class JwtAccessStrategy extends PassportStrategy(Strategy) {
jwtFromRequest: ExtractJwt.fromExtractors([
(request) => request.cookies?.accessToken,
]),
// jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: true, // 토큰 만료 여부를 검사하지 않는다.
});
}

async validate({ id }: JwtPayload): Promise<User> {
const user = await this.userRepository.findOneBy({ id });
async validate({ id }: JwtAccessPayloadDto): Promise<User> {
const user = await this.userRepository.findOne({
where: { id },
});

if (!user) {
throw new UnauthorizedException();
Expand Down
Loading

0 comments on commit 4bce1f0

Please sign in to comment.