Skip to content

Commit

Permalink
[Refactor]: add improved error handling (#118)
Browse files Browse the repository at this point in the history
* [fix]: fix error handling logic and add swagger examples

* [fix]: add Date object transform logic

* [refactor]: add testing fixture

* [fix]: fix to return dtos

* [fix]: fix to not check user email duplicate in guard

* [fix]: add unauthorized error when getting kakao/goolge userinfo

* [refactor]: add controller tests and refactor user modify dto

* [refactor]: add domain tests
  • Loading branch information
Istiopaxx authored Jan 14, 2024
1 parent 87ab950 commit 6a03f42
Show file tree
Hide file tree
Showing 56 changed files with 1,907 additions and 429 deletions.
9 changes: 5 additions & 4 deletions api/libs/auth/src/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import {
ApiGet,
ApiPostOk,
} from '@app/common/decorators/http-method.decorator';
import { User } from '@app/user/domain/mongo.user.entity';
import { ReqUser } from '@app/common/decorators/req-user.decorator';
import { CreateUserApiDto, CreateUserDto } from '@app/user/dto/modify-user.dto';
import { CreateUserApiDto, CreateUserDto } from '@app/user/dto/create-user.dto';
import { Auth, RegisterAuth } from '@app/common/decorators/auth.decorator';
import {
LogoutResponseDto,
Expand All @@ -25,6 +24,8 @@ import {
} from './dto/auth-response.dto';
import { FindOneUserResponseDto } from '@app/user/dto/user-response.dto';
import { UserInfo } from './types/user-info.type';
import { UserDto } from '@app/user/dto/user.dto';
import { UserEntity } from '@app/user/domain/user.entity';

@ApiTags('Auth')
@Controller('auth')
Expand All @@ -41,8 +42,8 @@ export class AuthController {
@ApiGet(FindOneUserResponseDto)
@Auth()
@Get('me')
async me(@ReqUser() user: User) {
return user;
async me(@ReqUser() user: UserEntity) {
return UserDto.fromEntity(user);
}

/**
Expand Down
51 changes: 35 additions & 16 deletions api/libs/auth/src/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { UserService } from '@app/user/user.service';
import { Injectable, NotFoundException } from '@nestjs/common';
import {
Injectable,
NotFoundException,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { AuthRepository } from './repositories/auth.repository';
import { CreateUserDto } from '@app/user/dto/modify-user.dto';
import { CreateUserDto } from '@app/user/dto/create-user.dto';
import { HttpService } from '@nestjs/axios';
import {
GoogleLoginDto,
Expand Down Expand Up @@ -81,15 +85,21 @@ export class AuthService {
googleLoginDto: GoogleLoginDto,
): Promise<OAuthLoginSessionDto> {
const { accessToken } = googleLoginDto;
const { data }: { data: GoogleApiResponseType } =
await this.httpService.axiosRef.get(
'https://www.googleapis.com/oauth2/v2/userinfo',
{
headers: {
Authorization: `Bearer ${accessToken}`,
let data: GoogleApiResponseType;
try {
data = (
await this.httpService.axiosRef.get(
'https://www.googleapis.com/oauth2/v2/userinfo',
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
},
);
)
).data;
} catch (err) {
throw new UnauthorizedException();
}
const { email, name: username } = data;
return await this.OAuthLoginByEmail({ email, username } as UserInfo);
}
Expand All @@ -99,12 +109,21 @@ export class AuthService {
kakaoLoginDto: KakaoLoginDto,
): Promise<OAuthLoginSessionDto> {
const { accessToken } = kakaoLoginDto;
const { data }: { data: KakaoApiResponseType } =
await this.httpService.axiosRef.get('https://kapi.kakao.com/v2/user/me', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
let data: KakaoApiResponseType;
try {
data = (
await this.httpService.axiosRef.get(
'https://kapi.kakao.com/v2/user/me',
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
).data;
} catch (err) {
throw new UnauthorizedException();
}
const { email } = data.kakao_account;
const { nickname: username } = data.kakao_account.profile;
return await this.OAuthLoginByEmail({ email, username } as UserInfo);
Expand Down
43 changes: 29 additions & 14 deletions api/libs/auth/src/dto/oauth.dto.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,53 @@
import { IsNotEmpty, IsString } from 'class-validator';
import { Expose } from 'class-transformer';
import { ApiProperty } from '@nestjs/swagger';
import { UserDto } from '@app/user/dto/user.dto';
import { ApiExpose } from '@app/common/decorators/api-expose.decorator';
import { Type } from 'class-transformer';

export class GoogleLoginDto {
@ApiProperty({ name: 'access_token' })
@Expose({ name: 'access_token' })
@ApiExpose({ name: 'access_token' })
@IsString()
@IsNotEmpty()
readonly accessToken: string;

constructor(accessToken: string) {
this.accessToken = accessToken;
}
}

export class KakaoLoginDto {
@ApiProperty({ name: 'access_token' })
@Expose({ name: 'access_token' })
@ApiExpose({ name: 'access_token' })
@IsString()
@IsNotEmpty()
readonly accessToken: string;

constructor(accessToken: string) {
this.accessToken = accessToken;
}
}

export class OAuthLoginSessionDto {
@ApiProperty({ name: 'is_exist' })
@Expose({ name: 'is_exist' })
@ApiExpose({ name: 'is_exist' })
readonly isExist: boolean;

@ApiProperty({ name: 'session_token' })
@Expose({ name: 'session_token' })
@ApiExpose({ name: 'session_token' })
readonly sessionToken?: string;

@ApiProperty({ name: 'user' })
@Expose({ name: 'user' })
@Type(() => UserDto)
@ApiExpose({ name: 'user' })
readonly user?: UserDto;

@ApiProperty({ name: 'register_token' })
@Expose({ name: 'register_token' })
@ApiExpose({ name: 'register_token' })
readonly registerToken?: string;

constructor(props: {
isExist: boolean;
sessionToken?: string;
user?: UserDto;
registerToken?: string;
}) {
this.isExist = props.isExist;
this.sessionToken = props.sessionToken;
this.user = props.user;
this.registerToken = props.registerToken;
}
}
10 changes: 9 additions & 1 deletion api/libs/auth/src/dto/session.dto.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import { Expose } from 'class-transformer';
import { Expose, Transform } from 'class-transformer';
import { ApiProperty } from '@nestjs/swagger';
import { SessionEntity } from '@app/auth/domain/session.entity';

export class SessionDto {
@ApiProperty({ name: 'id' })
@Expose({ name: 'id' })
readonly id: number;

@ApiProperty({ name: 'session_token' })
@Expose({ name: 'session_token' })
readonly sessionToken: string;

@ApiProperty({ name: 'user_id' })
@Expose({ name: 'user_id' })
readonly userId: number;

@Transform(({ value }) => new Date(value), { toClassOnly: true })
@Transform(({ value }) => value.toISOString(), { toPlainOnly: true })
@ApiProperty({ name: 'created_at' })
@Expose({ name: 'created_at' })
readonly createdAt: Date;

@Transform(({ value }) => new Date(value), { toClassOnly: true })
@Transform(({ value }) => value.toISOString(), { toPlainOnly: true })
@ApiProperty({ name: 'updated_at' })
@Expose({ name: 'updated_at' })
readonly updatedAt: Date;
Expand Down
14 changes: 2 additions & 12 deletions api/libs/auth/src/guards/register-auth.guard.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import {
BadRequestException,
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { Request } from 'express';
import { AuthService } from '../auth.service';
import { UserService } from '@app/user/user.service';

@Injectable()
export class RegisterAuthGuard implements CanActivate {
constructor(
private readonly authService: AuthService,
private readonly userService: UserService,
) {}
constructor(private readonly authService: AuthService) {}

async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
Expand All @@ -23,14 +18,9 @@ export class RegisterAuthGuard implements CanActivate {
throw new UnauthorizedException();
}
try {
const userInfo = await this.authService.verifyRegisterToken(
request['user'] = await this.authService.verifyRegisterToken(
registerToken,
);
const user = await this.userService.findByEmail(userInfo.email);
if (user) {
throw new BadRequestException();
}
request['user'] = userInfo;
} catch {
throw new UnauthorizedException();
}
Expand Down
39 changes: 39 additions & 0 deletions api/libs/auth/test/fixture/auth.fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { SessionEntity } from '@app/auth/domain/session.entity';
import { UserInfo } from '@app/auth/types/user-info.type';
import { LoginSessionDto } from '@app/auth/dto/token.dto';
import {
GoogleLoginDto,
KakaoLoginDto,
OAuthLoginSessionDto,
} from '@app/auth/dto/oauth.dto';
import { userDto, userEntity } from '../../../user/test/fixture/user.fixture';

export const sessionEntity: SessionEntity = {
id: 1,
sessionToken: 'token',
userId: 1,
user: userEntity,
createdAt: new Date(),
updatedAt: new Date(),
};

export const userInfo: UserInfo = {
email: '[email protected]',
username: 'test',
};

export const loginSessionDto: LoginSessionDto = {
sessionToken: 'sessToken',
user: userDto,
};

export const googleLoginDto: GoogleLoginDto = new GoogleLoginDto('accessToken');

export const kakaoLoginDto: KakaoLoginDto = new KakaoLoginDto('accessToken');

export const oAuthLoginSessionDto: OAuthLoginSessionDto =
new OAuthLoginSessionDto({
isExist: true,
sessionToken: loginSessionDto.sessionToken,
user: loginSessionDto.user,
});
31 changes: 31 additions & 0 deletions api/libs/auth/test/fixture/mocked-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { AuthService } from '@app/auth/auth.service';
import { SessionAuthGuard } from '@app/auth/guards/session-auth.guard';
import { RegisterAuthGuard } from '@app/auth/guards/register-auth.guard';

export const MockedAuthService = {
provide: AuthService,
useValue: {
register: jest.fn(),
verifyRegisterToken: jest.fn(),
findBySessionToken: jest.fn(),
login: jest.fn(),
OAuthLoginByEmail: jest.fn(),
googleLogin: jest.fn(),
kakaoLogin: jest.fn(),
logout: jest.fn(),
},
};

export const MockedSessionAuthGuard = {
provide: SessionAuthGuard,
useValue: {
canActivate: jest.fn(),
},
};

export const MockedRegisterAuthGuard = {
provide: RegisterAuthGuard,
useValue: {
canActivate: jest.fn(),
},
};
26 changes: 0 additions & 26 deletions api/libs/auth/test/integration/auth.controller.spec.ts

This file was deleted.

Loading

0 comments on commit 6a03f42

Please sign in to comment.