flask 소셜 로그인 구현하기

Jang Seowoo·2022년 2월 5일

사전 개념

OAuth 소셜로그인

프로젝트에 소셜로그인 적용시키기

이번 프로젝트에서는 웹 서비스와 카카오 소셜로그인을 구현해볼것이기 때문에 카카오를 기준으로 다음 인증과정을 진행해보겠다.

Kakao Developers에서 어플리케이션 등록하고 API Key 발급받기

우선 kakao developers에서 회원가입, 로그인을 진행한 후 내 애플리케이션으로 들어간다.

내 애플리케이션 등록

  • 애플리케이션 추가하기
  • 앱 아이콘, 앱 이름, 사업자명을 작성하고 저장하기(여기서 입력된 정보는 사용자가 카카오 로그인을 할 때 표시된다고 한다.)
  • 생성된 애플리케이션 클릭하기
  • 앱키(유출되면 안됨) 확인 -> 이 중 REST API Key(CLIENT_ID)를 사용할 예정

내 애플리케이션 설정

  • 플랫폼 설정하기
  • Web 플랫폼 등록 > 사이트 도메인 http://localhost:5000 으로 작성
  • 카카오 로그인 사용 시 Redirect URI를 등록해야한다고 알려준다. 등록하러 가기 클릭
  • 카카오 로그인 활성화 설정 상태를 ON으로 바꿔주고 Redirect URIhttp://localhost:5000/oauth/kakao/callback 으로 등록해준다.

카카오 로그인 관련 추가 사항

  • 동의항목: 사용자 동의 항목을 세부적으로 설정할 수 있다.
  • 간편가입/카카오톡채널: 사업자 정보를 등록하여 비즈앱으로 전환하여야 사용가능하다.
  • 보안: 토큰 발급 시, 보안을 강화하기 위해 Client Secret을 사용가능하다. (REST API인 경우에 해당)
  • 고급: Logout Redirect URI를 등록할 수 있다. 서비스 로그아웃 시 카카오 계정과 함께 로그아웃을 위한 스펙으로 선택항목이다.

Flask 코드에 적용시키기

main.py

Flask 객체를 불러오는 메인 모듈이다.
kakao api blueprint를 등록시킨다.

key > kakao_client.py

위의 Kakao Developers에서 발급받은 정보를 저장한다.

CLIENT_ID = REST API Key
CLIENT_SECRET = 카카오 로그인 > 보안에서 발급받은 Client Secret
REDIRECT_URI = 카카오 로그인 기능을 위해 설정한 Redirect URI
SIGNOUT_REDIRECT_URI = 카카오 로그인 > 고급에서 발급받은 카카오 계정과 함께 로그아웃 기능을 위해 설정한 Logout Redirect URI

kakao_controller.py

Oauth 객체를 정의한다.

import requests
from key.kakao_client import CLIENT_ID, CLIENT_SECRET, REDIRECT_URI


class Oauth:

    def __init__(self):
        self.auth_server = "https://kauth.kakao.com%s"
        self.api_server = "https://kapi.kakao.com%s"
        self.default_header = {
            "Content-Type": "application/x-www-form-urlencoded",
            "Cache-Control": "no-cache",
        }

    def auth(self, code):
        return requests.post(
            url=self.auth_server % "/oauth/token",
            headers=self.default_header,
            data={
                "grant_type": "authorization_code",
                "client_id": CLIENT_ID,
                "client_secret": CLIENT_SECRET,
                "redirect_uri": REDIRECT_URI,
                "code": code,
            },
        ).json()
    
    def userinfo(self, bearer_token):
        return requests.post(
            url=self.api_server % "/v2/user/me",
            headers={
                **self.default_header,
                **{"Authorization": bearer_token}
            },
            # "property_keys":'["kakao_account.profile_image_url"]'
            data={}
        ).json()

oauth.py

카카오 로그인과 관련된 api를 작성한다.

GET oauth/kakao

  1. 카카오톡 로그인 버튼을 눌렀을 때 oauth/kakao api를 호출하면 앞에서 발급받은 토큰 값들을 넣어 kakao_oauth_url를 생성 후 redirect 한다.
  2. kakao_oauth_url은 카카오 자체의 로그인 화면과 동의 화면을 거친 후 내가 지정해준 REDIRECT_URI인 oauth/kakao/callback로 redirect한다.
  3. oauth/kakao/callback(REDIRECT_URI)에서는 request로 전달받은 authorization code를 통해서 access_token을 발급받는다.
  4. 에러가 발생하지 않으면 access_token을 사용하여 user의 정보를 받아온다.
  5. user가 기존에 없는 사람이라면 db에 추가하고 session에 저장하여 로그인 상태로 만들어준다.
  6. 홈페이지로 redirect 한다.
methodparametersreturn내용
GET-redirect to 홈페이지카카오 사용자 로그인
  • /oauth/kakao
@kakao.route('/')
def kakao_sign_in():
    # 카카오톡으로 로그인 버튼을 눌렀을 때
    kakao_oauth_url = f"https://kauth.kakao.com/oauth/authorize?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&response_type=code"
    return redirect(kakao_oauth_url)

  • /oauth/kakao/callback
@kakao.route('/callback')
def callback():
    code = request.args["code"]

    # 전달받은 authorization code를 통해서 access_token을 발급
    oauth = Oauth()
    auth_info = oauth.auth(code)

    # error 발생 시 로그인 페이지로 redirect
    if "error" in auth_info:
        print("에러가 발생했습니다.")
        return {'message': '인증 실패'}, 404

    # 아닐 시
    user = oauth.userinfo("Bearer " + auth_info['access_token'])

    print(user)
    kakao_account = user["kakao_account"]
    profile = kakao_account["profile"]
    name = profile["nickname"]
    if "email" in kakao_account.keys():
        email = kakao_account["email"]
    else:
        email = f"{name}@kakao.com"

    user = User.query.filter(User.name == name).first()

    if user is None:
        # 유저 테이블에 추가
        user = User(name, email, generate_password_hash(name))
        db.session.add(user)
        db.session.commit()

        # message = '회원가입이 완료되었습니다.'
        # value = {"status": 200, "result": "success", "msg": message}

    session['email'] = user.email
    session['isKakao'] = True
    # message = '로그인에 성공하였습니다.'
    # value = {"status": 200, "result": "success", "msg": message}

    return redirect("http://localhost")

GET oauth/kakao/signout

카카오톡 로그인 시 카카오 유저인지 정보를 세션에 담아뒀다가 카카오 로그인 사용자가 로그아웃을 할 경우 oauth/kakao/signout api를 호출하여 카카오 계정과 함께 로그아웃이 가능하도록 한다.

methodparametersreturn내용
GET-redirect to 홈페이지카카오 사용자 로그아웃
  • oauth/kakao/signout
@kakao.route('/signout')
def kakao_sign_out():
    # 카카오톡으로 로그아웃 버튼을 눌렀을 때
    kakao_oauth_url = f"https://kauth.kakao.com/oauth/logout?client_id={CLIENT_ID}&logout_redirect_uri={SIGNOUT_REDIRECT_URI}"

    if session.get('email'):
        session.clear()
        value = {"status": 200, "result": "success"}
    else:
        value = {"status": 404, "result": "fail"}
    print(value)
    return redirect(kakao_oauth_url)

주의 사항

REST API로 로그인 구현시 CORS 문제

로그인 버튼 클릭시 요청하는 api는 redirect를 시켜주는 역할을 하기 때문에 비동기 통신(axios) 방식으로 호출하면 안된다.
따라서 리다이렉트 받을 수 있도록 html 링크로 구현하거나, JavaScript SDK를 사용하여야 한다.

따라서 원래는 기존 홈페이지 기능에 있는 기본 로그인 기능 처럼 성공/실패 여부를 객체로 return 하려 했는데 그 대신 리턴값을 홈페이지로 redirect 하는 것으로 바꾸고 프론트단에서는 html 링크로 구현하는 방식으로 진행하였다.

로그인 버튼 디자인

표준 디자인 규격이 있으니 참고하자!
카카오 로그인 버튼 디자인 가이드

profile
https://devseowoo.notion.site/Seowoo-Portfolio-b21365c3477345818913e8d8fe2e3b90

0개의 댓글