Skip to content

Commit

Permalink
♻️ [Release] Release Local login/signup (#625)
Browse files Browse the repository at this point in the history
Co-authored-by: hannkim <skqkdldhf98@gmail.com>
Co-authored-by: Jisu Kim <hellojs242@gmail.com>
  • Loading branch information
3 people authored Aug 24, 2023
1 parent ac99f39 commit 0a49002
Show file tree
Hide file tree
Showing 44 changed files with 700 additions and 115 deletions.
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
**/.env*
**/.husky

./backend/seeding
./backend/db/seeding
./backend/yarn-error.log
./backend/test
./backend/.env.development
Expand Down
6 changes: 6 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_CALLBACK_URL=http://localhost:3000/api/v1/auth/callback/google

# Github OAuth
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_CALLBACK_URL=http://localhost:3000/api/v1/auth/callback/github


# JWT token secret 값
USER_JWT_SECRETKEY=
# JWT token secret 값
Expand Down
15 changes: 15 additions & 0 deletions backend/db/migrations/1692710671031-migration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class Migration1692710671031 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
// create column at auth table
await queryRunner.query(`ALTER TABLE "auth" ADD COLUMN password varchar(64)`);
await queryRunner.query(`ALTER TABLE "auth" ADD COLUMN account_id varchar(32) UNIQUE`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
// drop column from auth table
await queryRunner.query(`ALTER TABLE "auth" DROP COLUMN account_id`);
await queryRunner.query(`ALTER TABLE "auth" DROP COLUMN password`);
}
}
32 changes: 32 additions & 0 deletions backend/db/migrations/orm-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { config } from 'dotenv';
import { DataSource, DataSourceOptions } from 'typeorm';
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';

import { Auth } from '../../src/entity/auth.entity';

import { Migration1692710671031 } from './1692710671031-migration';

config({ path: '.env.development' });
if (process.env.NODE_ENV === 'production') {
config();
}

const auth1692710671031 = Migration1692710671031;

export const options: DataSourceOptions = {
type: 'postgres',
host: process.env.POSTGRES_HOST,
port: Number(process.env.POSTGRES_PORT),
database: process.env.POSTGRES_DB,
username: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
synchronize: false,
logging: true,
entities: [Auth],
namingStrategy: new SnakeNamingStrategy(),
migrations: [auth1692710671031],
};

const dataSource = new DataSource(options);

export default dataSource;
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { config } from 'dotenv';
import { DataSource, DataSourceOptions } from 'typeorm';
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';

import { Achievement } from '../src/entity/achievement.entity';
import { Auth } from '../src/entity/auth.entity';
import { BlockedUser } from '../src/entity/blocked-user.entity';
import { Friendship } from '../src/entity/friendship.entity';
import { GameHistory } from '../src/entity/game-history.entity';
import { MessageView } from '../src/entity/message-view.entity';
import { Message } from '../src/entity/message.entity';
import { UserRecord } from '../src/entity/user-record.entity';
import { User } from '../src/entity/user.entity';
import { Achievement } from '../../src/entity/achievement.entity';
import { Auth } from '../../src/entity/auth.entity';
import { BlockedUser } from '../../src/entity/blocked-user.entity';
import { Friendship } from '../../src/entity/friendship.entity';
import { GameHistory } from '../../src/entity/game-history.entity';
import { MessageView } from '../../src/entity/message-view.entity';
import { Message } from '../../src/entity/message.entity';
import { UserRecord } from '../../src/entity/user-record.entity';
import { User } from '../../src/entity/user.entity';

import seeder from './seeder/message.seeder';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { faker } from '@faker-js/faker';

import { AuthStatus } from '../../src/entity/auth.entity';
import { AuthStatus } from '../../../src/entity/auth.entity';

export default () => ({
//auth.id is auto generated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { faker } from '@faker-js/faker';

import { User } from '../../src/entity/user.entity';
import { User } from '../../../src/entity/user.entity';

export default (user1: User, user2: User, accept: boolean, lastMessageTime?: Date) => {
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// file name should be <entityName>.factory.ts
import { faker } from '@faker-js/faker';

import { User } from '../../src/entity/user.entity';
import { User } from '../../../src/entity/user.entity';

export default (user1: User, user2: User) => {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { faker } from '@faker-js/faker';

import { Friendship } from '../../src/entity/friendship.entity';
import { Friendship } from '../../../src/entity/friendship.entity';

export default (friendship: Friendship, prevDate?: Date) => {
prevDate?.setDate(prevDate?.getDate() + faker.datatype.number({ min: 1, max: 10 }));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { faker } from '@faker-js/faker';

import { Auth } from '../../src/entity/auth.entity';
import { Auth } from '../../../src/entity/auth.entity';

export default (auth: Auth) => {
return {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { faker } from '@faker-js/faker';
import { DataSource, Repository } from 'typeorm';

import { Auth, AuthStatus } from '../../src/entity/auth.entity';
import { Friendship } from '../../src/entity/friendship.entity';
import { GameHistory } from '../../src/entity/game-history.entity';
import { Message } from '../../src/entity/message.entity';
import { UserRecord } from '../../src/entity/user-record.entity';
import { User } from '../../src/entity/user.entity';
import { Auth, AuthStatus } from '../../../src/entity/auth.entity';
import { Friendship } from '../../../src/entity/friendship.entity';
import { GameHistory } from '../../../src/entity/game-history.entity';
import { Message } from '../../../src/entity/message.entity';
import { UserRecord } from '../../../src/entity/user-record.entity';
import { User } from '../../../src/entity/user.entity';
import authFactory from '../factory/auth.factory';
import frieindshipFactory from '../factory/frieindship.factory';
import gameHistoryFactory from '../factory/game-history.factory';
Expand Down
12 changes: 10 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"postinstall": "cd .. && husky install backend/.husky",
"seed": "npx ts-node ./seeding/data-source.ts",
"seed:reset": "./seeding/reset-db.sh"
"seed": "npx ts-node ./db/seeding/data-source.ts",
"seed:reset": "./db/seeding/reset-db.sh",
"migration:create": "npx typeorm-ts-node-commonjs migration:create ./db/migrations/migration",
"migration:generate": "npx typeorm-ts-node-commonjs migration:generate ./db/migrations/migration",
"migration:run": "npx typeorm-ts-node-commonjs migration:run -d ./db/migrations/orm-config.ts",
"migration:revert": "npx typeorm-ts-node-commonjs migration:revert -d ./db/migrations/orm-config.ts"
},
"dependencies": {
"@hapi/joi": "^17.1.1",
Expand All @@ -50,8 +54,10 @@
"nodemailer": "^6.9.1",
"passport": "^0.6.0",
"passport-42": "^1.2.6",
"passport-github2": "^0.1.12",
"passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"pg": "^8.10.0",
"postgresql": "^0.0.1",
"reflect-metadata": "^0.1.13",
Expand All @@ -74,8 +80,10 @@
"@types/multer": "^1.4.7",
"@types/node": "18.11.18",
"@types/passport": "^1.0.12",
"@types/passport-github2": "^1.2.5",
"@types/passport-google-oauth20": "^2.0.11",
"@types/passport-jwt": "^3.0.9",
"@types/passport-local": "^1.0.35",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
Expand Down
65 changes: 40 additions & 25 deletions backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Post, Res, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import {
ApiBadRequestResponse,
ApiConflictResponse,
Expand All @@ -19,13 +20,14 @@ import { AuthService } from './auth.service';
import { ExtractUser } from './decorator/extract-user.decorator';
import { SkipUserGuard } from './decorator/skip-user-guard.decorator';
import { CodeVerificationRequestDto } from './dto/request/code-verification-request.dto';
import { LocalLoginRequestDto } from './dto/request/local-login-request.dto';
import { LocalSignUpRequestDto } from './dto/request/local-signup-request.dto';
import { TwoFactorAuthRequestDto } from './dto/request/two-factor-auth-request.dto';
import { TwoFactorAuthResponseDto } from './dto/response/two-factor-auth-response.dto';
import { FtGuard } from './guard/ft.guard';
import { GoogleGuard } from './guard/google.guard';
import { SocialGuard } from './guard/social.guard';
import { TwoFaGuard } from './guard/two-fa.guard';
import { LoginInfo } from './type/login-info';
import { SocialResponseOptions } from './type/social-response-options';
import { LoginResponseOptions } from './type/login-response-options';

@ApiTags('auth')
@Controller('auth')
Expand All @@ -34,47 +36,60 @@ export class AuthController {

/**
* @summary 로그인
* @description GET /auth/login/ft
* @description GET /auth/login/ft, GET /auth/login/google, GET /auth/login/github
*/
@ApiOperation({ summary: '42 로그인' })
@ApiOperation({ summary: 'oauth 로그인' })
@SkipUserGuard()
@UseGuards(FtGuard)
@Get('login/ft')
login(): void {
@UseGuards(SocialGuard)
@Get('login/:provider')
socialLogin(): void {
return;
}

/**
* @summary 로그인 callback
* @description GET /auth/callback/ft
* @description GET /auth/callback/ft, GET /auth/callback/google, GET /auth/callback/github
*/
@ApiOperation({ summary: '42 로그인 callback' })
@ApiOperation({ summary: 'oauth 로그인 callback' })
@SkipUserGuard()
@UseGuards(FtGuard) // strategy.validate() -> return 값 기반으로 request 객체 담아줌
@Get('callback/ft')
async callbackLogin(@ExtractUser() user: LoginInfo, @Res() res: Response): Promise<void> {
const responseOptions: SocialResponseOptions = await this.authService.socialAuth(user);

@UseGuards(SocialGuard) // strategy.validate() -> return 값 기반으로 request 객체 담아줌
@Get('callback/:provider')
async callbackSocialLogin(@ExtractUser() user: LoginInfo, @Res() res: Response): Promise<void> {
const responseOptions: LoginResponseOptions = await this.authService.socialAuth(user);
if (responseOptions.cookieKey !== undefined) {
res.cookie(responseOptions.cookieKey, responseOptions.token, COOKIE_OPTIONS);
}
res.redirect(responseOptions.redirectUrl);
}

@ApiOperation({ summary: 'google 로그인' })
/**
* @summary Local 로그인
* @description POST /auth/login/local
*/
@ApiOperation({ summary: 'local 로그인' })
@ApiNotFoundResponse({ type: ErrorResponseDto, description: '이메일 없음' })
@ApiBadRequestResponse({ type: ErrorResponseDto, description: '잘못된 비밀번호' })
@SkipUserGuard()
@UseGuards(GoogleGuard)
@Get('login/google')
async googleLogin(): Promise<void> {
return;
@UseGuards(AuthGuard('local'))
@HttpCode(HttpStatus.OK)
@Post('login/local')
async localLogin(@ExtractUser() user: LocalLoginRequestDto, @Res() res: Response): Promise<void> {
const token = await this.authService.localLogin(user);
res.send({ token });
}

@ApiOperation({ summary: 'google 로그인 callback' })
/**
* @summary Local 회원가입
* @description POST /auth/signup/local
*/
@ApiOperation({ summary: 'local 회원가입' })
@SkipUserGuard()
@UseGuards(GoogleGuard)
@Get('callback/google')
async googleCallbackLogin(@ExtractUser() user: LoginInfo, @Res() res: Response): Promise<void> {
return this.callbackLogin(user, res);
@Post('signup/local')
async localSignUp(@Body() signUpInfo: LocalSignUpRequestDto): Promise<SuccessResponseDto> {
await this.authService.localSignUp(signUpInfo);
return {
message: '회원가입이 완료되었습니다.',
};
}

/**
Expand Down
9 changes: 7 additions & 2 deletions backend/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,30 @@ import { MailerModule } from '@nestjs-modules/mailer';
import { TWO_FA_EXPIRES_IN, TWO_FA_MAX } from '../common/constant';
import { AppConfigModule } from '../config/app/configuration.module';
import { FtAuthConfigModule } from '../config/auth/ft/configuration.module';
import { GithubAuthConfigModule } from '../config/auth/github/configuration.module';
import { GoogleAuthConfigModule } from '../config/auth/google/configuration.module';
import { JwtConfigModule } from '../config/auth/jwt/configuration.module';
import { MailerConfigModule } from '../config/auth/mailer/configuration.module';
import { MailerConfigService } from '../config/auth/mailer/configuration.service';
import { Auth } from '../entity/auth.entity';
import { User } from '../entity/user.entity';

import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { UserGuard } from './guard/user.guard';
import { FtStrategy } from './strategy/ft.strategy';
import { GithubStrategy } from './strategy/github.strategy';
import { GoogleStrategy } from './strategy/google.strategy';
import { LocalStrategy } from './strategy/local.strategy';
import { UserStrategy } from './strategy/user.strategy';

@Module({
imports: [
TypeOrmModule.forFeature([Auth]),
TypeOrmModule.forFeature([Auth, User]),
JwtModule.register({}),
FtAuthConfigModule,
GoogleAuthConfigModule,
GithubAuthConfigModule,
JwtConfigModule,
MailerConfigModule,
AppConfigModule,
Expand All @@ -40,7 +45,7 @@ import { UserStrategy } from './strategy/user.strategy';
inject: [MailerConfigService],
}),
],
providers: [AuthService, FtStrategy, UserStrategy, GoogleStrategy, UserGuard],
providers: [AuthService, FtStrategy, UserStrategy, GoogleStrategy, GithubStrategy, LocalStrategy, UserGuard],
controllers: [AuthController],
exports: [AuthService],
})
Expand Down
Loading

0 comments on commit 0a49002

Please sign in to comment.