From e55cadd6feb1d00b8cd2473112a3bfec9a509c9c Mon Sep 17 00:00:00 2001 From: kimminsu Date: Sat, 23 Nov 2024 21:46:14 +0900 Subject: [PATCH 1/9] =?UTF-8?q?=E2=9C=A8=20feat:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=84=B0=20=EC=9C=A0=EC=A0=80=20=EC=83=9D=EC=84=B1=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/user/user.service.ts | 72 +++++++++++++++++++---- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/user/user.service.ts b/packages/backend/src/user/user.service.ts index c1eebd52..8d2a5d23 100644 --- a/packages/backend/src/user/user.service.ts +++ b/packages/backend/src/user/user.service.ts @@ -27,22 +27,25 @@ export class UserService { }); } + async registerTester() { + return await this.dataSource.transaction(async (manager) => { + return await manager.save(User, { + nickname: this.generateRandomNickname(), + email: 'tester@nav', + type: OauthType.LOCAL, + oauthId: String( + (await this.getMaxOauthId(OauthType.LOCAL, manager)) + 1, + ), + }); + }); + } + async findUserByOauthIdAndType(oauthId: string, type: OauthType) { return await this.dataSource.manager.findOne(User, { where: { oauthId, type }, }); } - private async validateUserExists( - type: OauthType, - oauthId: string, - manager: EntityManager, - ) { - if (await manager.exists(User, { where: { oauthId, type } })) { - throw new BadRequestException('user already exists'); - } - } - async updateUserTheme(userId: number, isLight?: boolean): Promise { return await this.dataSource.transaction(async (manager) => { if (isLight === undefined) { @@ -72,4 +75,53 @@ export class UserService { return user.isLight; } + + private generateRandomNickname(): string { + const adjectives = [ + '강력한', + '지혜로운', + '소중한', + '빛나는', + '고요한', + '용감한', + '행운의', + '신비로운', + ]; + const animals = [ + '호랑이', + '독수리', + '용', + '사슴', + '백호', + '하늘새', + '백두산 호랑이', + '붉은 여우', + ]; + + const randomAdjective = + adjectives[Math.floor(Math.random() * adjectives.length)]; + const randomAnimal = animals[Math.floor(Math.random() * animals.length)]; + + return `${randomAdjective} ${randomAnimal}`; + } + + private async getMaxOauthId(oauthType: OauthType, manager: EntityManager) { + const result = await manager + .createQueryBuilder(User, 'user') + .select('MAX(user.oauthId)', 'max') + .where('user.type = :oauthType', { oauthType }) + .getRawOne(); + + return result ? Number(result.max) : 1; + } + + private async validateUserExists( + type: OauthType, + oauthId: string, + manager: EntityManager, + ) { + if (await manager.exists(User, { where: { oauthId, type } })) { + throw new BadRequestException('user already exists'); + } + } } From fc9adbd336bd87efdcb2a1eb72a90b6986d73f9e Mon Sep 17 00:00:00 2001 From: kimminsu Date: Sat, 23 Nov 2024 21:51:45 +0900 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=9A=9A=20chore:=20passport=20local=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/package.json | 2 ++ yarn.lock | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/packages/backend/package.json b/packages/backend/package.json index c41b6046..fd70879e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -39,6 +39,7 @@ "nest-winston": "^1.9.7", "passport": "^0.7.0", "passport-google-oauth20": "^2.0.0", + "passport-local": "^1.0.0", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "socket.io": "^4.8.1", @@ -56,6 +57,7 @@ "@types/jest": "^29.5.2", "@types/node": "^20.3.1", "@types/passport-google-oauth20": "^2.0.16", + "@types/passport-local": "^1.0.38", "@types/supertest": "^6.0.0", "@types/unzipper": "^0.10.10", "@typescript-eslint/eslint-plugin": "^8.0.0", diff --git a/yarn.lock b/yarn.lock index b8b27206..1d75dcf2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2156,6 +2156,15 @@ "@types/passport" "*" "@types/passport-oauth2" "*" +"@types/passport-local@^1.0.38": + version "1.0.38" + resolved "https://registry.yarnpkg.com/@types/passport-local/-/passport-local-1.0.38.tgz#8073758188645dde3515808999b1c218a6fe7141" + integrity sha512-nsrW4A963lYE7lNTv9cr5WmiUD1ibYJvWrpE13oxApFsRt77b0RdtZvKbCdNIY4v/QZ6TRQWaDDEwV1kCTmcXg== + dependencies: + "@types/express" "*" + "@types/passport" "*" + "@types/passport-strategy" "*" + "@types/passport-oauth2@*": version "1.4.17" resolved "https://registry.yarnpkg.com/@types/passport-oauth2/-/passport-oauth2-1.4.17.tgz#d5d54339d44f6883d03e69dc0cc0e2114067abb4" @@ -2165,6 +2174,14 @@ "@types/oauth" "*" "@types/passport" "*" +"@types/passport-strategy@*": + version "0.2.38" + resolved "https://registry.yarnpkg.com/@types/passport-strategy/-/passport-strategy-0.2.38.tgz#482abba0b165cd4553ec8b748f30b022bd6c04d3" + integrity sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA== + dependencies: + "@types/express" "*" + "@types/passport" "*" + "@types/passport@*": version "1.0.17" resolved "https://registry.yarnpkg.com/@types/passport/-/passport-1.0.17.tgz#718a8d1f7000ebcf6bbc0853da1bc8c4bc7ea5e6" @@ -7039,6 +7056,13 @@ passport-google-oauth20@^2.0.0: dependencies: passport-oauth2 "1.x.x" +passport-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee" + integrity sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow== + dependencies: + passport-strategy "1.x.x" + passport-oauth2@1.x.x: version "1.8.0" resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.8.0.tgz#55725771d160f09bbb191828d5e3d559eee079c8" From 07d2a8234cf575ae1cac9a290f44ef66ff11b225 Mon Sep 17 00:00:00 2001 From: kimminsu Date: Sat, 23 Nov 2024 21:54:03 +0900 Subject: [PATCH 3/9] =?UTF-8?q?=E2=9C=A8=20feat:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=84=B0=20=EC=9C=A0=EC=A0=80=20service=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/auth/tester/testerAuth.service.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 packages/backend/src/auth/tester/testerAuth.service.ts diff --git a/packages/backend/src/auth/tester/testerAuth.service.ts b/packages/backend/src/auth/tester/testerAuth.service.ts new file mode 100644 index 00000000..0982c4b0 --- /dev/null +++ b/packages/backend/src/auth/tester/testerAuth.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { UserService } from '@/user/user.service'; + +@Injectable() +export class TesterAuthService { + constructor(private readonly userService: UserService) {} + + async attemptAuthentication() { + return await this.userService.registerTester(); + } +} From 2bc72478280d6c43b8b01e6e931438005830b3cc Mon Sep 17 00:00:00 2001 From: kimminsu Date: Sat, 23 Nov 2024 21:54:30 +0900 Subject: [PATCH 4/9] =?UTF-8?q?=E2=9C=A8=20feat:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=84=B0=20=EC=9C=A0=EC=A0=80=20strategy=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/auth/tester/strategy/tester.strategy.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 packages/backend/src/auth/tester/strategy/tester.strategy.ts diff --git a/packages/backend/src/auth/tester/strategy/tester.strategy.ts b/packages/backend/src/auth/tester/strategy/tester.strategy.ts new file mode 100644 index 00000000..2c6939ca --- /dev/null +++ b/packages/backend/src/auth/tester/strategy/tester.strategy.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { Strategy } from 'passport-local'; +import { TesterAuthService } from '@/auth/tester/testerAuth.service'; + +@Injectable() +export class TesterStrategy extends PassportStrategy(Strategy) { + constructor(private readonly testerAuthService: TesterAuthService) { + super(); + } + + async validate(username: string, password: string, done: CallableFunction) { + const user = await this.testerAuthService.attemptAuthentication(); + done(null, user); + } +} From 1de6bc2f450faf504f6c20246599a5ccec8cc64d Mon Sep 17 00:00:00 2001 From: kimminsu Date: Sat, 23 Nov 2024 21:54:45 +0900 Subject: [PATCH 5/9] =?UTF-8?q?=E2=9C=A8=20feat:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=84=B0=20=EC=9C=A0=EC=A0=80=20guard=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/auth/tester/guard/tester.guard.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 packages/backend/src/auth/tester/guard/tester.guard.ts diff --git a/packages/backend/src/auth/tester/guard/tester.guard.ts b/packages/backend/src/auth/tester/guard/tester.guard.ts new file mode 100644 index 00000000..46f461d3 --- /dev/null +++ b/packages/backend/src/auth/tester/guard/tester.guard.ts @@ -0,0 +1,16 @@ +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +@Injectable() +export class TestAuthGuard extends AuthGuard('local') { + constructor() { + super(); + } + + async canActivate(context: ExecutionContext) { + const isActivate = (await super.canActivate(context)) as boolean; + const request = context.switchToHttp().getRequest(); + await super.logIn(request); + return isActivate; + } +} From 258044beeb15da43e3c68a0c48e653bb9e4fbce0 Mon Sep 17 00:00:00 2001 From: kimminsu Date: Sat, 23 Nov 2024 21:55:04 +0900 Subject: [PATCH 6/9] =?UTF-8?q?=E2=9C=A8=20feat:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=84=B0=20=EC=9C=A0=EC=A0=80=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/auth/auth.module.ts | 16 +++++++-- .../src/auth/tester/testerAuth.controller.ts | 36 +++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 packages/backend/src/auth/tester/testerAuth.controller.ts diff --git a/packages/backend/src/auth/auth.module.ts b/packages/backend/src/auth/auth.module.ts index f513d613..c32261b7 100644 --- a/packages/backend/src/auth/auth.module.ts +++ b/packages/backend/src/auth/auth.module.ts @@ -1,13 +1,23 @@ import { Module } from '@nestjs/common'; +import { PassportModule } from '@nestjs/passport'; import { GoogleAuthController } from '@/auth/google/googleAuth.controller'; import { GoogleAuthService } from '@/auth/google/googleAuth.service'; import { GoogleStrategy } from '@/auth/google/strategy/google.strategy'; import { SessionSerializer } from '@/auth/session/session.serializer'; +import { TesterStrategy } from '@/auth/tester/strategy/tester.strategy'; +import { TesterAuthController } from '@/auth/tester/testerAuth.controller'; +import { TesterAuthService } from '@/auth/tester/testerAuth.service'; import { UserModule } from '@/user/user.module'; @Module({ - imports: [UserModule], - controllers: [GoogleAuthController], - providers: [GoogleStrategy, GoogleAuthService, SessionSerializer], + imports: [UserModule, PassportModule.register({ session: true })], + controllers: [GoogleAuthController, TesterAuthController], + providers: [ + GoogleStrategy, + GoogleAuthService, + SessionSerializer, + TesterAuthService, + TesterStrategy, + ], }) export class AuthModule {} diff --git a/packages/backend/src/auth/tester/testerAuth.controller.ts b/packages/backend/src/auth/tester/testerAuth.controller.ts new file mode 100644 index 00000000..d4381db9 --- /dev/null +++ b/packages/backend/src/auth/tester/testerAuth.controller.ts @@ -0,0 +1,36 @@ +import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { Request, Response } from 'express'; +import { TestAuthGuard } from '@/auth/tester/guard/tester.guard'; + +@ApiTags('Auth') +@Controller('auth/tester') +export class TesterAuthController { + constructor() {} + + @ApiOperation({ + summary: '테스터 로그인 api', + description: '테스터로 로그인합니다.', + }) + @Get('/login') + @UseGuards(TestAuthGuard) + async handleLogin(@Res() response: Response) { + response.redirect('/'); + } + + @ApiOperation({ + summary: '로그인 상태 확인', + description: '로그인 상태를 확인합니다.', + }) + @ApiOkResponse({ + description: '로그인된 상태', + example: { message: 'Authenticated' }, + }) + @Get('/status') + async user(@Req() request: Request) { + if (request.user) { + return { message: 'Authenticated' }; + } + return { message: 'Not Authenticated' }; + } +} From afb3bf339b857948441ce825eae8e75e03971ce8 Mon Sep 17 00:00:00 2001 From: kimminsu Date: Sat, 23 Nov 2024 21:55:59 +0900 Subject: [PATCH 7/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20google=20s?= =?UTF-8?q?trategy=20=EB=8D=94=EC=9D=B4=EC=83=81=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/auth/google/strategy/google.strategy.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/auth/google/strategy/google.strategy.ts b/packages/backend/src/auth/google/strategy/google.strategy.ts index b87b7945..28295a2d 100644 --- a/packages/backend/src/auth/google/strategy/google.strategy.ts +++ b/packages/backend/src/auth/google/strategy/google.strategy.ts @@ -1,9 +1,8 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Profile, Strategy, VerifyCallback } from 'passport-google-oauth20'; import { GoogleAuthService } from '@/auth/google/googleAuth.service'; import { OauthType } from '@/user/domain/ouathType'; -import { Logger } from 'winston'; export interface OauthUserInfo { type: OauthType; @@ -15,7 +14,7 @@ export interface OauthUserInfo { @Injectable() export class GoogleStrategy extends PassportStrategy(Strategy) { - constructor(private readonly googleAuthService: GoogleAuthService, @Inject('winston') private readonly logger: Logger) { + constructor(private readonly googleAuthService: GoogleAuthService) { super({ clientID: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, From b71839a219595cee9e6116d28d9dd36d69ab0517 Mon Sep 17 00:00:00 2001 From: kimminsu Date: Mon, 25 Nov 2024 11:37:06 +0900 Subject: [PATCH 8/9] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=EB=9E=9C=EB=8D=A4=20=EB=8B=89=EB=84=A4=EC=9E=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/user/user.service.ts | 32 ++++------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/packages/backend/src/user/user.service.ts b/packages/backend/src/user/user.service.ts index 8d2a5d23..dbef92dd 100644 --- a/packages/backend/src/user/user.service.ts +++ b/packages/backend/src/user/user.service.ts @@ -6,6 +6,7 @@ import { import { DataSource, EntityManager } from 'typeorm'; import { OauthType } from './domain/ouathType'; import { User } from './domain/user.entity'; +import { status, subject } from '@/user/constants/randomNickname'; type RegisterRequest = Required< Pick @@ -76,33 +77,10 @@ export class UserService { return user.isLight; } - private generateRandomNickname(): string { - const adjectives = [ - '강력한', - '지혜로운', - '소중한', - '빛나는', - '고요한', - '용감한', - '행운의', - '신비로운', - ]; - const animals = [ - '호랑이', - '독수리', - '용', - '사슴', - '백호', - '하늘새', - '백두산 호랑이', - '붉은 여우', - ]; - - const randomAdjective = - adjectives[Math.floor(Math.random() * adjectives.length)]; - const randomAnimal = animals[Math.floor(Math.random() * animals.length)]; - - return `${randomAdjective} ${randomAnimal}`; + private generateRandomNickname() { + const statusName = status[Math.floor(Math.random() * status.length)]; + const subjectName = subject[Math.floor(Math.random() * subject.length)]; + return `${statusName}${subjectName}`; } private async getMaxOauthId(oauthType: OauthType, manager: EntityManager) { From 22d60c65f57beda606df335a92c84e8bb220b9b0 Mon Sep 17 00:00:00 2001 From: kimminsu Date: Mon, 25 Nov 2024 11:40:48 +0900 Subject: [PATCH 9/9] =?UTF-8?q?=E2=9C=A8=20feat:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=84=B0=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=97=94=EB=93=9C?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=20swagger=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/auth/tester/testerAuth.controller.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/auth/tester/testerAuth.controller.ts b/packages/backend/src/auth/tester/testerAuth.controller.ts index d4381db9..4ee02553 100644 --- a/packages/backend/src/auth/tester/testerAuth.controller.ts +++ b/packages/backend/src/auth/tester/testerAuth.controller.ts @@ -1,5 +1,10 @@ import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common'; -import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { + ApiOkResponse, + ApiOperation, + ApiQuery, + ApiTags, +} from '@nestjs/swagger'; import { Request, Response } from 'express'; import { TestAuthGuard } from '@/auth/tester/guard/tester.guard'; @@ -12,6 +17,16 @@ export class TesterAuthController { summary: '테스터 로그인 api', description: '테스터로 로그인합니다.', }) + @ApiQuery({ + name: 'username', + required: true, + description: '테스터 아이디(값만 넣으면 됨)', + }) + @ApiQuery({ + name: 'password', + required: true, + description: '테스터 비밀번호(값만 넣으면 됨)', + }) @Get('/login') @UseGuards(TestAuthGuard) async handleLogin(@Res() response: Response) {