Skip to content

Commit

Permalink
Merge pull request #250 from boostcampwm2023/feat/verify_kakao_token
Browse files Browse the repository at this point in the history
카카오 IdToken 검증
  • Loading branch information
msjang4 authored Dec 11, 2023
2 parents dfdc17e + 64f2a8b commit e547dab
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 3 deletions.
10 changes: 8 additions & 2 deletions server/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { ProfileUploadRequiredException } from 'src/exceptions/profile-upload-re
import { PresignedUrlResponseDto } from 'src/presigned-url/dto/presigned-url-response.dto';
import { PresignedUrlService } from 'src/presigned-url/presigned-url.service';
import { SignupProfilePresignedUrlRequestDto } from 'src/presigned-url/dto/signup-profile-presigned-url-request.dto';
import { InvalidKakaoIdTokenException } from 'src/exceptions/invalid-kakao-idtoken.exception';
import { InconsistentKakaoUuidException } from 'src/exceptions/inconsistent-kakao-uuid.exception';
import { InvalidGoogldIdTokenException } from 'src/exceptions/invalid-google-idToken.exception';
import { InconsistentGoogldUuidException } from 'src/exceptions/inconsistent-google-uuid.exception';
import { AuthService } from './auth.service';
Expand All @@ -32,12 +34,14 @@ export class AuthController {
*/
@Post('signup')
@ApiSuccessResponse(201, '회원가입 성공', SignupResponseDto)
@ApiFailResponse('업로드 필요', [ProfileUploadRequiredException])
@ApiFailResponse('회원가입 실패', [UserConflictException])
@ApiFailResponse('인증 실패', [
InvalidKakaoIdTokenException,
InconsistentKakaoUuidException,
InvalidGoogldIdTokenException,
InconsistentGoogldUuidException,
])
@ApiFailResponse('업로드 필요', [ProfileUploadRequiredException])
@ApiFailResponse('회원가입 실패', [UserConflictException])
signUp(
@Body() signupRequestDto: SignupRequestDto,
): Promise<SignupResponseDto> {
Expand All @@ -51,6 +55,8 @@ export class AuthController {
@ApiSuccessResponse(201, '로그인 성공', SigninResponseDto)
@ApiFailResponse('인증 실패', [
LoginFailException,
InvalidKakaoIdTokenException,
InconsistentKakaoUuidException,
InvalidGoogldIdTokenException,
InconsistentGoogldUuidException,
])
Expand Down
59 changes: 59 additions & 0 deletions server/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import { InvalidRefreshTokenException } from 'src/exceptions/invalid-refresh-tok
import { UserInfoDto } from 'src/user/dto/user-info.dto';
import { checkUpload } from 'src/ncpAPI/listObjects';
import { ProfileUploadRequiredException } from 'src/exceptions/profile-upload-required-exception';
import * as assert from 'assert';
import axios from 'axios';
import { InvalidKakaoIdTokenException } from 'src/exceptions/invalid-kakao-idtoken.exception';
import { InconsistentKakaoUuidException } from 'src/exceptions/inconsistent-kakao-uuid.exception';
import { createPublicKey } from 'crypto';
import { PlatformEnum, SignupRequestDto } from './dto/signup-request.dto';
import { OAuth2Client } from 'google-auth-library';
import { InvalidGoogldIdTokenException } from 'src/exceptions/invalid-google-idToken.exception';
import { InconsistentGoogldUuidException } from 'src/exceptions/inconsistent-google-uuid.exception';
Expand Down Expand Up @@ -97,6 +103,59 @@ export class AuthService {
return { jwt, profile };
}

async verifyKakaoIdToken(idToken: string) {
try {
const tokens = idToken.split('.');
assert(tokens.length === 3);
const [header, payload] = tokens
.slice(0, 2)
.map((token) =>
JSON.parse(Buffer.from(token, 'base64').toString('utf-8')),
);
assert(
payload.iss === process.env.KAKAO_ISS &&
payload.aud === process.env.KAKAO_APP_KEY &&
payload.exp > Date.now() / 1000,
);
const jwks = (await axios.get(process.env.KAKAO_KEY_URL)).data.keys;

const key = jwks.find((jwk) => jwk.kid === header.kid);
assert(key);
const keyObject = createPublicKey({
key,
format: 'jwk',
});
const secret = keyObject.export({ type: 'pkcs1', format: 'pem' });
await this.jwtService.verifyAsync(idToken, {
algorithms: [key.alg],
secret,
});
const id = Number(payload.sub);

return this.formatAsUUID(id, id);
} catch (e) {
throw new InvalidKakaoIdTokenException();
}
}

formatAsUUID(mostSigBits: number, leastSigBits: number) {
const most = mostSigBits.toString(16).padStart(16, '0');
const least = leastSigBits.toString(16).padStart(16, '0');
return `${most.substring(0, 8)}-${most.substring(8, 12)}-${most.substring(
12,
)}-${least.substring(0, 4)}-${least.substring(4)}`;
}

async verifyUuid(platform: PlatformEnum, idToken: string, uuid: string) {
switch (platform) {
case PlatformEnum.KAKAO:
if (uuid !== (await this.verifyKakaoIdToken(idToken)))
throw new InconsistentKakaoUuidException();
break;
default:
}
}

async signin(signinRequestDto: SigninRequestDto): Promise<SigninResponseDto> {
const { uuid, platform, idToken } = signinRequestDto;
await this.verifyUuid(platform, idToken, uuid);
Expand Down
2 changes: 1 addition & 1 deletion server/src/auth/dto/signup-request.dto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IsEnum, IsNotEmpty } from 'class-validator';
import { UserDto } from 'src/user/dto/user.dto';

enum PlatformEnum {
export enum PlatformEnum {
GOOGLE = 'GOOGLE',
KAKAO = 'KAKAO',
}
Expand Down
4 changes: 4 additions & 0 deletions server/src/exceptions/enum/exception.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ enum ErrorCode {
InvalidToken = 1002,
BadTokenFormat = 1003,
InvalidRefreshToken = 1005,
InvalidKakaoIdToken = 1007,
InconsistentKakaoUuid = 1017,
InvalidGoogleIdToken = 1006,
InconsistentGoogleUuid = 1016,
VideoNotFound = 4000,
Expand Down Expand Up @@ -49,6 +51,8 @@ const ErrorMessage = {
[ErrorCode.EncodingActionFail]: '인코딩 액션 실패',
[ErrorCode.GreenEyeApiFail]: 'greeneye api 요청 실패',
[ErrorCode.GreenEyeActionFail]: 'greeneye 액션 실패',
[ErrorCode.InvalidKakaoIdToken]: '유효하지 않은 카카오 idToken',
[ErrorCode.InconsistentKakaoUuid]: '카카오 idToken과 uuid가 일치하지 않음',
};

export { ErrorCode, ErrorMessage };
9 changes: 9 additions & 0 deletions server/src/exceptions/inconsistent-kakao-uuid.exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { HttpStatus } from '@nestjs/common';
import { ErrorCode } from 'src/exceptions/enum/exception.enum';
import { BaseException } from './base.exception';

export class InconsistentKakaoUuidException extends BaseException {
constructor() {
super(ErrorCode.InconsistentKakaoUuid, HttpStatus.UNAUTHORIZED);
}
}
9 changes: 9 additions & 0 deletions server/src/exceptions/invalid-kakao-idtoken.exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ErrorCode } from 'src/exceptions/enum/exception.enum';
import { HttpStatus } from '@nestjs/common';
import { BaseException } from './base.exception';

export class InvalidKakaoIdTokenException extends BaseException {
constructor() {
super(ErrorCode.InvalidKakaoIdToken, HttpStatus.UNAUTHORIZED);
}
}

0 comments on commit e547dab

Please sign in to comment.