본문 바로가기

TOPIC

Node.js : Oauth 로그인 연동 (카카오 로그인, 구글 로그인)

반응형

Oauth

KAKAO 로그인, 구글 로그인 같은 경우는 Oauth 플로우를 따르고 있습니다. 따라서 먼저 Ouath에 대해서 학습합니다.
지금 소개에서는 Oauth를 다 다루지 않고 이전 포스팅으로 대체하겠습니다.

Ouath 는 모르겠고 구현만 필요해도 학습하는것을 추천합니다. 해당 플로우를 알아야 구현에 많은 도움이 됩니다.

https://redbinalgorithm.tistory.com/443

 

OAuth, OAuth2

생각해보기 웹 사이트를 이용할 때 "네이버로 로그인" 같이 별도의 회원가입 없이 로그인을 제공하는 서비스를 이용해 본적이 있다. 이때 해당 플랫폼의 아이디가 있다면 외부 서비스에서도 인

redbinalgorithm.tistory.com


간단하게 Oauth는 내가만드는 클라이언트 서버에서 다른 서버의 정보(KAKAO) 를 가지고 오고 싶을때 안전하게 가지고 오는 플로우를 제시합니다. 보통은 구글,페이스북에서 많이 사용하는 Authorization Code Grant 방식을 자주 사용합니다.

Oauth 플로우에 익숙해 지셨다면 다음으로는 시크릿 코드, Redirect URL을 카카오 디벨럽 먼트에 등록하고 최종 어세스 토근까 발급받도록 하겠습니다.

KAKAO 어플리케이션 추가


https://developers.kakao.com/console/app

 

카카오계정 로그인

여기를 눌러 링크를 확인하세요.

accounts.kakao.com


다음 화면에서 어플리케이션을 추가합니다.

그럼 다음과 같은 키 값들을 받을 수가 있습니다. 이 키값들을 이용해서 Assess token으로 최종적으로 발급받습니다.

본격적으로 시작

https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#before-you-begin

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

<a href={"https://kauth.kakao.com/oauth/authorize?response_type=code&client_id={Client_ID}&redirect_uri={Redirect_URI}"} className={style.kakao}>카카오 로그인</a>

다음과 같은 a 태그를 만듭니다. 그러면 화면이 카카오톡 인증 창으로 이동하게됩니다.

임시 로그인 화면

 

인증화면

동의하고 계속하기를 누르면 우리가 카카오 디벨롭퍼에서 Redirect_URL로 설정했던 페이지로 code값과 함게 이동하게됩니다.

Oauth인증 인가 플로우를 이해했으면 다시한번 이 CODE값과 시크릿 코드 클라이언트 아이디, Redirect_URI를 다시한번 전송하게되면 JSON형태의 어세스 토근을 발급받게 됩니다. 우리는 어세스토큰을 클라이언트 쿠키에다가 저장해도되고 세션에다가 저장해도 됩니다.

유효기간은 12시간정도이기 때문에 한번받은 어세스토큰은 계속 유효시간동안 계속사요할 수 있습니다. (탈취위험이 있음)
이러한 토큰을 관리하는 방법에 따라서 보안이 달라지는데 방법에 대해서는 이전 포스팅으로 대체하겠습니다.
https://redbinalgorithm.tistory.com/420?category=1012437

 

Access Token 클라이언트 보안전략

1. Access Token 만 사용 # 사용자가 로그인 할 때 클라이언트에게 AccessToken을 발급한다. 이때 AccessToken은 서버에서 관리할 필요가 없고 메모리상에서 미리 정의 된 비밀키를 이용해서 AccessToeken의 유

redbinalgorithm.tistory.com

 

Node.js로 돌아가서

자 이제 위와 같은 URI로 오게된다면 서버에서 받아서 처리해야합니다.

다음과 같은 코드로 Oauth 종류별로 로그인 기능을 받아옵니다. 현재는 구현단계라서 KaKao 케이스만 봐주시면 되겠습니다.

const express = require('express'); //express를 설치했기 때문에 가져올 수 있다.
const app = express();
const winston = require('winston');
const logger = winston.createLogger();
const qs = require('qs');
const fetch = require('node-fetch');

class Kakao {
    constructor(code) {
        this.url = 'https://kauth.kakao.com/oauth/token';
        this.clientID = '';
        this.clientSecret = '';
        this.redirectUri = 'http://localhost:8081/oauth/kakao';
        this.code = code;
    }
}

app.get(`/oauth/:coperation`, async (req, res) => {
    const coperation = req.params.coperation;
    const code = req.param('code');
    let options;

    // TODO : 이 부분 향후 모듈화 패턴으로 의존성 줄일수 있을것 같음.
    switch(coperation){
        case 'kakao':
            options = new Kakao(code);
        break;
        case 'google':
        break;
        case 'naver':
        break;
    }

    const token = await getAccessToken(options);

    res.send("HI " + qs.stringify(token));
})


Kakao 클래스안에는 우리가 카카오 디벨롭퍼에서 발급받은 값을 설정하시면 됩니다.
그러면 최종적으로 options에는 kakao oauth의 어세스토큰을 발급받기 위한 준비과정이 끝납니다.

나도 node.js를 공부하고 있는 입장으로 javascript 내장함수인 fetch가 왜안되지 하고 있었는데 requre를 통해서 추가해줘야한다.
fetch함수로 만든 비동기식 assess_token을 발급받는 함수이다.

const getAccessToken = async (options) => {
    try {
            return await fetch(options.url, {
                method: 'POST',
                headers: {
                    'content-type':'application/x-www-form-urlencoded;charset=utf-8'
                },
                body: qs.stringify({
                    grant_type: 'authorization_code',//특정 스트링
                    client_id: options.clientID,
                    client_secret: options.clientSecret,
                    redirectUri: options.redirectUri,
                    code: options.code,
                }),
            }).then(res => res.json());
    }catch(e) {
        logger.info("error", e);
    }
};

options값은 switch case 문을 통해서 발급받았습니다. 향후 리팩토링을 통해서 좀더 깔끔한 코드로 보여주겠습니다.

    const token = await getAccessToken(options);

    res.send("HI " + qs.stringify(token));


Oauth를 공부했으면 assess_token은 일종의 클럽의 티켓이라고 보면 무방합니다.
- 클럽의 티켓을 발급받을 때는 주민등록증을 제시하고 신분을 조회한 후 입장합니다. (이 과정이 로그인 과정)
- 티켓을 발급받은 후 클럽에 입장할 때는 또 신분증을 보여줄 필요는 없다. 티켓을 보여주면 된다. (assess_token 이다. 이게)

자이제 이 assess_token을 가지고 kakao 정보를 얻어보겠습니다.

카카오 디벨로퍼에서 사용자정보를 조회하는 API는 다음과 같이 공개한다.


getUserInfo 라는 함수를 통해서 user정보를 JSON형태로 가져오겠습니다.

const getUserInfo = async (url, access_token) => {
    try {
        return await fetch(optinos.url, {
            method: 'GET',
            url: url,
            headers: {
                Authorization: `Bearer ${access_token}`
            }
        });
    }catch(e) {
        logger.info("error", e);
    }
};


다음과 같이 간단하게 assessToken을 이용하여 REST API를 요청합니다.

    const token = await getAccessToken(options);

    const userInfo = await getUserInfo('kapi.kakao.com', token.access_token);

    res.send("HI " + qs.stringify(userInfo));

요쳥결과 화면

다음 사이트에서 정렬된 JSON파일을 확인해봅니다.
https://jsonlint.com/

 

The JSON Validator

JSONLint is the free online validator and reformatter tool for JSON, a lightweight data-interchange format.

jsonlint.com

하하하


자 이제 이값을 front 상단으로 다시 Redirect 하면서 cookie, local, session 등을 이용해서 login을 유지해주면 됩니다.

추가적으로 리플레시토큰이라는 값이 있습니다. 리플레시 토큰같은 경우는 어세스토큰 보단 유효기간이 긴편입니다. 이러한 토큰을 데이터베이스에서 관리하면서 유저가 다시들어왓을 경우 추가적인 로그인을 하지않아도 우리 웹사이트를 이용하기 쉽게 해주는거죠 ㅎㅎ

이상으로 Oauth 로그인을 마치도록 하겠습니다.

다음에는 Node.js로 Oauth 서버자체를 구현해보는 것도 포스팅해도 좋을거 같네요

전체소스 (리팩토링 후 소스입니다. 깔끔한게 최고!!!! , 구글, 네이버 로그인도 카카오랑 똑같은 방법이니 한번 시도해보세요~)
요즘 리팩토링2라는 js기반 책을 읽고 있는데 좀더 깔끔한 코드로 짜는걸 연습하면 좋을것 같네요.


전체소스

const express = require('express'); //express를 설치했기 때문에 가져올 수 있다.
const app = express();
const winston = require('winston');
const logger = winston.createLogger();
const qs = require('qs');
const fetch = require('node-fetch');

class Kakao {
    constructor(code) {
        this.url = 'https://kauth.kakao.com/oauth/token';
        this.clientID = '{발급받은 REST API}';
        this.clientSecret = '{보안안에 있는 SecretCode}';
        this.redirectUri = 'http://localhost:8081/oauth/kakao';
        this.code = code;

        // userInfo
        this.userInfoUrl = 'https://kapi.kakao.com/v2/user/me';
    }
}

//  TODO Naver

//  TODO Google

const getAccessToken = async (options) => {
    try {
            return await fetch(options.url, {
                method: 'POST',
                headers: {
                    'content-type':'application/x-www-form-urlencoded;charset=utf-8'
                },
                body: qs.stringify({
                    grant_type: 'authorization_code',//특정 스트링
                    client_id: options.clientID,
                    client_secret: options.clientSecret,
                    redirectUri: options.redirectUri,
                    code: options.code,
                }),
            }).then(res => res.json());
    }catch(e) {
        logger.info("error", e);
    }
};

const getUserInfo = async (url, access_token) => {
    try {
        return await fetch(url, {
            method: 'POST',
            headers: {
                'Content-type': 'application/x-www-form-urlencoded;charset=utf-8',
                'Authorization': `Bearer ${access_token}`
            }
        }).then(res => res.json());
    }catch(e) {
        logger.info("error", e);
    }
};

const getOption = (coperation, code)=> {
    switch(coperation){
        case 'kakao':
            return new Kakao(code);
        break;
        case 'google':
            // return new Google(code);
        break;
        case 'naver':
            // return new Naver(code);
        break;
    }
}

app.get(`/oauth/:coperation`, async (req, res) => {
    const coperation = req.params.coperation;
    const code = req.param('code');
    const options = getOption(coperation, code);
    const token = await getAccessToken(options);
    const userInfo = await getUserInfo(options.userInfoUrl, token.access_token);

    // TODO Redirect Frot Server (쿠키, 세션, local_store 중에 로그인을 유지한다.)
    // TODO Data Base or 쿠키 reflesh Token 저장 방법 모색
    res.send(userInfo);
})

app.listen(8081);
반응형

'TOPIC' 카테고리의 다른 글

Node.js 서버에서 Jwt 토큰 사용하기  (1) 2021.08.05
React 프로젝트에 Redux 적용하기 : Redux란??  (0) 2021.08.02
SVG  (0) 2021.07.29
접근성 : ARIA-TAB  (0) 2021.07.21
JavaScript 모든 this의 바인딩 상황  (1) 2021.07.18