seohl16 / kakao-oauth-study

Using Kakao OAuth with Nest.js
0 stars 0 forks source link

#2 Validating Access Token #2

Open seohl16 opened 2 years ago

seohl16 commented 2 years ago

Access Token 유효성 검사

Accesstoken의 유효성 검사를 하기 위해서 다양한 방법이 있는데 나느 이 블로그에서 추천하는 방법을 사용했다. kapi.kakao.com/v2/user/me 에 acesstoken을 보내서 유저 정보를 얻을 수 있는 지 확인하는 방법인데, 유효한 토큰이면 200 응답과 정보가 담긴 json을 돌려주고, 만료된 토큰이면 401 응답을 돌려준다.

이걸 검사하기 위해서 우리는 axios라는 라이브러리 및 함수를 사용하려고 한다.

필요한 모듈 설치

npm install axios @nestjs/axios --save

그 외 또 참고한 사이트

seohl16 commented 2 years ago

Axios 함수 만들기

auth.service.ts

import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common';
import axios from 'axios';

@Injectable()
export class AuthService {
    constructor(private readonly httpService: HttpService) {}

    async tokenValidation(accessToken: string) {
        await axios({
            method: 'get',
            url: 'https://kapi.kakao.com/v2/user/me',
            headers: {
                'Authorization': `Bearer ${accessToken}`,
                'Content-Type': 'application/json'
            }
        })
        .then((res) => {
            console.log(res.data);
        })
        .catch((err) => {
            console.log(err);
        })      
    }   
}

service.ts 파일에 tokenValidation이라는 함수를 만든다.

kakao.strategy.ts

import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { Strategy } from "passport-kakao";
import * as config from 'config';
import { AuthKakaoDto } from "./dto/auth.kakao-dto";
import { AuthService } from "./auth.service";

@Injectable()
export class StrategyKakao extends PassportStrategy(Strategy, 'kakao') {
    constructor(public readonly authService: AuthService) {
        super({
            clientID: config.get('kakao.clientId'),
            callbackURL: config.get('kakao.callbackURL'),
        });
    }
    async validate(accessToken: string, refreshToken: string, profile, done) {
        const data = await (this.authService.tokenValidation(accessToken));
        console.log("accesstoken", accessToken);
        const payload: AuthKakaoDto = {
            name: profile._json.kakao_account.profile.nickname,
            kakaoid: profile._json.id,
        };
        done(null, payload);
    }
}

strategy.ts의 validate 함수를 수정한다. -> await thisService.tokenValidation으로 accessToken을 넘겨서 함수를 수행한다.

결과

console.log(res.data)를 한 결과

{
  id: 24234252,
  connected_at: '2022-07-30T07:41:17Z',
  properties: { nickname: 'se' },
  kakao_account: {
    profile_nickname_needs_agreement: false,
    profile: { nickname: 'se' }
  }
}

이 나온다.

seohl16 commented 2 years ago

GetUserMe 함수로 바꿈

profile이용한 Payload가 아니라 유효한 토큰을 통해 검사한 api에서 얻은 정보를 돌려주기로 한다.

import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { Strategy } from "passport-kakao";
import * as config from 'config';
import { AuthKakaoDto } from "./dto/auth.kakao-dto";
import { AuthService } from "./auth.service";

@Injectable()
export class StrategyKakao extends PassportStrategy(Strategy, 'kakao') {
    constructor(public readonly authService: AuthService) {
        super({
            clientID: config.get('kakao.clientId'),
            callbackURL: config.get('kakao.callbackURL'),
        });
    }

    async validate(accessToken: string, refreshToken: string, profile, done) {
        const user: AuthKakaoDto = await (this.authService.getUserMe(accessToken));
        console.log(user);
        console.log("accesstoken", accessToken);
        done(null, user);
    }
}

auth.service.ts

import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common';
import axios from 'axios';
import { AuthKakaoDto } from './dto/auth.kakao-dto';

@Injectable()
export class AuthService {
    constructor(private readonly httpService: HttpService) {}

    async getUserMe(accessToken: string) : Promise < AuthKakaoDto > {
        let user: AuthKakaoDto;
        await axios({
            method: 'get',
            url: 'https://kapi.kakao.com/v2/user/me',
            headers: {
                'Authorization': `Bearer ${accessToken}`,
                'Content-Type': 'application/json'
            }
        })
        .then((res) => {
            // 유효한 토큰은 사용자 정보를 돌려준다. 
            console.log('good');
            user = {
                name: res.data.kakao_account.profile.nickname, 
                kakaoid: res.data.id, 
            };
            return user;
        })
        .catch((err) => {
            // 유효하지 않은 토큰은 401에러를 돌려준다. 
            console.log(err);
        })
        return user;
    }

}

payload 역할 검사

@Injectable()
export class StrategyKakao extends PassportStrategy(Strategy, 'kakao') {
    constructor(public readonly authService: AuthService) {
        super({
            clientID: config.get('kakao.clientId'),
            callbackURL: config.get('kakao.callbackURL'),
        });
    }

    async validate(accessToken: string, refreshToken: string, profile, done) {
        const user: AuthKakaoDto = await (this.authService.getUserMe(accessToken));
        console.log(user);
        console.log("accesstoken", accessToken);
        const payload = {}
        done(null, payload);
    }
}

이렇게 payload를 {} 빈 배열로 바꾸고 controller에서 req.user를 어떻게 받는지 확인해보니

{} {
  code: 'KW_DHNSbL7j0egnSFfJUP3xm1X_nWDzl0awqwHOPN1k_DYB4xtUPFgbpNYRqwdoAAAGCTl8dFQ'
} 
finish

오류는 없는데 user 정보가 하나도 없어진다. payload를 user로 인식하고 있던게 맞았나보다.