Skip to content

Commit

Permalink
[BE#155] 아이폰용 로그인
Browse files Browse the repository at this point in the history
- fetch 요청을 통해 구글 서버에 access_token 보내기
- 새로운 사용자는 is_member false 반환
- 사용자는 base64 닉네임으로 미리 생성 후 유저정보 변경하도록 유도
  • Loading branch information
yeongbinim authored Nov 23, 2023
1 parent b20252e commit bcaed23
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 53 deletions.
2 changes: 2 additions & 0 deletions BE/src/app.controller.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import { ApiExcludeEndpoint } from '@nestjs/swagger';

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}

@Get('')
@ApiExcludeEndpoint()
getHello(): string {
return this.appService.getHello();
}
Expand Down
44 changes: 32 additions & 12 deletions BE/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,67 @@ import {
Res,
Post,
Body,
Patch,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Response } from 'express';
import { AccessTokenGuard } from './guard/bearer-token.guard';
import { User } from 'src/users/decorator/user.decorator';
import {
ApiBearerAuth,
ApiExcludeEndpoint,
ApiOperation,
ApiResponse,
ApiTags,
} from '@nestjs/swagger';
import { UsersService } from 'src/users/users.service';
import { UpdateUserDto } from 'src/users/dto/update-user.dto';
import { UsersModel } from 'src/users/entity/users.entity';

@ApiTags('auth')
@ApiTags('로그인 페이지')
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
constructor(
private readonly authService: AuthService,
private readonly usersService: UsersService,
) {}

@Get('google')
@UseGuards(AuthGuard('google'))
@ApiOperation({ summary: 'Google 테스트용 로그인' })
@ApiResponse({ status: 200, description: '로그인 성공' })
@ApiResponse({ status: 401, description: '인증 실패' })
@ApiExcludeEndpoint()
googleAuth(@Req() req) {
const user = req.user;
return this.authService.loginWithGoogle(user);
}

@Post('google/app')
@ApiOperation({ summary: 'Google 아이폰용 로그인' })
googleAppAuth(@Body() body) {
return body;
@ApiOperation({ summary: 'Google 아이폰용 로그인 (완)' })
@ApiResponse({ status: 201, description: '인증 성공' })
@ApiResponse({ status: 401, description: '인증 실패' })
googleAppAuth(@Body('access_token') accessToken: string) {
const email = this.authService.getUserInfo(accessToken);
return this.authService.loginWithGoogle({ email });
}

@Get('logout')
@UseGuards(AccessTokenGuard)
@ApiBearerAuth()
@ApiOperation({ summary: '로그아웃' })
@ApiResponse({ status: 200, description: '로그아웃 성공' })
@ApiResponse({ status: 401, description: '인증 실패' })
@ApiExcludeEndpoint()
logout(@User('id') userId: number, @Res() res: Response) {
console.log(`${userId}를 로그아웃 시키는 로직`);
res.redirect('/');
}

@Patch('info')
@UseGuards(AccessTokenGuard)
@ApiOperation({ summary: '유저 정보 설정 (완)' })
@ApiResponse({ status: 200, description: '프로필 변경 성공' })
@ApiResponse({ status: 400, description: '잘못된 요청' })
@ApiResponse({ status: 401, description: '인증 실패' })
@ApiBearerAuth()
patchUser(
@User('id') user_id: number,
@Body() user: UpdateUserDto,
): Promise<UsersModel> {
return this.usersService.updateUser(user_id, user);
}
}
37 changes: 33 additions & 4 deletions BE/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,49 @@ export class AuthService {
email: user.email,
nickname: user.nickname,
type: 'access',
auth_type: 'google',
};
return this.jwtService.sign(payload);
}

public async loginWithGoogle(user) {
let prevUser = await this.usersService.findUserByEmail(user.email);
const prevUser = await this.usersService.findUserByEmail(user.email);
if (!prevUser) {
const id = user.email.split('@')[0];
const userEntity = {
nickname: user.email.split('@')[0],
nickname:
id + Buffer.from(user.email + user.auth_type).toString('base64'),
email: user.email,
image_url: '',
};
prevUser = await this.usersService.createUser(userEntity);
const newUser = await this.usersService.createUser(userEntity);
return {
access_token: this.signToken(newUser),
is_member: false,
};
}
return {
access_token: this.signToken(prevUser),
is_member: true,
};
}

public async getUserInfo(accessToken: string): Promise<string> {
const url = 'https://www.googleapis.com/oauth2/v2/userinfo';
try {
const response = await fetch(url, {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
if (!response.ok) {
throw new UnauthorizedException('유효하지 않은 토큰입니다.');
}
const { email } = await response.json();
return email;
} catch (error) {
throw error;
}
return this.signToken(prevUser);
}
}
2 changes: 1 addition & 1 deletion BE/src/auth/google.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
email: any,
done: VerifyCallback,
): Promise<any> {
console.log(accessToken);
const user = {
email: email.emails[0].value,
accessToken,
};
done(null, user);
}
Expand Down
11 changes: 6 additions & 5 deletions BE/src/categories/categories.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ApiBadRequestResponse,
ApiBearerAuth,
ApiCreatedResponse,
ApiOkResponse,
ApiOperation,
ApiParam,
ApiTags,
Expand All @@ -23,15 +24,15 @@ import { CategoryDto } from './dto/response/category.dto';
import { AccessTokenGuard } from 'src/auth/guard/bearer-token.guard';
import { User } from 'src/users/decorator/user.decorator';

@ApiTags('Categories')
@ApiTags('카테고리 페이지')
@Controller('categories')
export class CategoriesController {
constructor(private readonly categoriesService: CategoriesService) {}

@Get()
@UseGuards(AccessTokenGuard)
@ApiBearerAuth()
@ApiOperation({ summary: '카테고리 조회' })
@ApiOperation({ summary: '카테고리 조회 (완)' })
@ApiCreatedResponse({
type: [CategoryDto],
description: '카테고리 조회 성공',
Expand All @@ -46,7 +47,7 @@ export class CategoriesController {
@Post()
@UseGuards(AccessTokenGuard)
@ApiBearerAuth()
@ApiOperation({ summary: '카테고리 생성' })
@ApiOperation({ summary: '카테고리 생성 (완)' })
@ApiCreatedResponse({
type: CategoryDto,
description: '카테고리 생성 성공',
Expand All @@ -64,14 +65,14 @@ export class CategoriesController {
@Patch(':category_id')
@UseGuards(AccessTokenGuard)
@ApiBearerAuth()
@ApiOperation({ summary: '카테고리 수정' })
@ApiOperation({ summary: '카테고리 수정 (완)' })
@ApiParam({
name: 'category_id',
description: '카테고리 id',
type: Number,
required: true,
})
@ApiCreatedResponse({
@ApiOkResponse({
type: CategoryDto,
description: '카테고리 수정 성공',
})
Expand Down
2 changes: 1 addition & 1 deletion BE/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async function bootstrap() {
const config = new DocumentBuilder()
.setTitle('StudyLog API')
.setDescription('StudyLog 애플리케이션 API 문서')
.setVersion('1.0')
.setVersion('2.0')
.addBearerAuth()
.build();

Expand Down
5 changes: 1 addition & 4 deletions BE/src/mates/mates.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,26 @@ import { Controller, Delete, Get, Param, Post } from '@nestjs/common';
import { ApiCreatedResponse, ApiOperation, ApiTags } from '@nestjs/swagger';

@Controller('mates')
@ApiTags('소셜 페이지')
export class MatesController {
@Get()
@ApiTags('Social')
@ApiOperation({ summary: '모든 친구들 조회하기' })
getMates() {}

@Get('/:mate_id/stats')
@ApiTags('Mate Stats')
@ApiOperation({ summary: '특정 친구의 통계 조회하기' })
getMateStats(@Param('mate_id') id: string) {
id;
}

@Post()
@ApiTags('Social')
@ApiCreatedResponse({
description: '친구가 성공적으로 구독되었습니다.',
})
@ApiOperation({ summary: '친구 구독하기' })
crateMate() {}

@Delete('/:mate_id')
@ApiTags('Social')
@ApiOperation({ summary: '구독한 친구 구독 취소하기' })
deleteMate(@Param('mate_id') id: string) {
id;
Expand Down
18 changes: 4 additions & 14 deletions BE/src/study-logs/study-logs.controller.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
import {
Body,
Controller,
Get,
Post,
Query,
Delete,
UseGuards,
} from '@nestjs/common';
import { Body, Controller, Get, Post, Query, UseGuards } from '@nestjs/common';
import { StudyLogsService } from './study-logs.service';
import { StudyLogs } from './study-logs.entity';
import {
Expand All @@ -21,14 +13,14 @@ import { StudyLogsDto } from './dto/response/study-logs.dto';
import { AccessTokenGuard } from 'src/auth/guard/bearer-token.guard';
import { User } from 'src/users/decorator/user.decorator';

@ApiTags('Timer')
@ApiTags('타이머 페이지')
@Controller('study-logs')
export class StudyLogsController {
constructor(private readonly studyLogsService: StudyLogsService) {}

@Post()
@UseGuards(AccessTokenGuard)
@ApiOperation({ summary: '학습시간 생성 및 종료' })
@ApiOperation({ summary: '학습시간 생성 및 종료 (완)' })
@ApiCreatedResponse({
type: StudyLogsCreateDto,
description: '학습 기록이 성공적으로 생성되었습니다.',
Expand Down Expand Up @@ -60,10 +52,10 @@ export class StudyLogsController {
}
}

@ApiTags('통계 페이지')
@Controller('/study-logs/stats')
export class StatsController {
@Get()
@ApiTags('Stats')
@ApiOperation({ summary: '일간 통계 조회하기' })
getStats(
@Query('year') year: number,
Expand All @@ -74,12 +66,10 @@ export class StatsController {
}

@Get('/weekly')
@ApiTags('Stats')
@ApiOperation({ summary: '주간 통계 조회하기' })
getWeeklyStats() {}

@Get('/monthly')
@ApiTags('Stats')
@ApiOperation({ summary: '주간 통계 조회하기' })
getMonthlyStats() {}
}
24 changes: 23 additions & 1 deletion BE/src/users/dto/create-user.dto.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
import { PickType } from '@nestjs/mapped-types';
import { UsersModel } from '../entity/users.entity';
import { ApiProperty } from '@nestjs/swagger';

export class CreateUserDto extends PickType(UsersModel, [
'nickname',
'email',
'image_url',
]) {}
]) {
@ApiProperty({
type: 'string',
example: '어린콩',
description: '닉네임',
})
nickname: string;

@ApiProperty({
type: 'string',
example: 'https://sldkjfds/dsflkdsjf.png',
description: '이미지 링크',
})
image_url: string;

@ApiProperty({
type: 'string',
example: '[email protected]',
description: '이메일',
})
email: string;
}
22 changes: 22 additions & 0 deletions BE/src/users/dto/update-user.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { PartialType } from '@nestjs/mapped-types';
import { UsersModel } from '../entity/users.entity';
import { ApiProperty } from '@nestjs/swagger';
import { IsOptional } from 'class-validator';

export class UpdateUserDto extends PartialType(UsersModel) {
@ApiProperty({
type: 'string',
example: '어린콩',
description: '닉네임',
})
@IsOptional()
nickname?: string;

@ApiProperty({
type: 'string',
example: 'https://sldkjfds/dsflkdsjf.png',
description: '이미지 링크',
})
@IsOptional()
image_url?: string;
}
4 changes: 2 additions & 2 deletions BE/src/users/entity/users.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ export class UsersModel {
description: '유저 닉네임',
})
@Column({
length: 20,
length: 84,
unique: true,
})
@IsString()
@Length(1, 20)
@Length(1, 84)
nickname: string;

@ApiProperty({
Expand Down
Loading

0 comments on commit bcaed23

Please sign in to comment.