/ OAUTH

OAuth2.0에 대해 알아보기


OAuth란

인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹 사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는, 접근 위임을 위한 개방형 표준이다. 즉 웹,앱 서비스에서 제한적으로 권한을 요청해 사용할 수 있는 키를 발급해주는 것을 말한다

쉽게 말해 사이트의 회원가입 없이 이미 회원가입 되어있는 다른 사이트로 로그인 하는 것이다. 아래의 그림처럼 OAuth인증은 3가지에 대한 용어를 알아야 한다.

이해가 가지 않아도 일단 OAuth는 resource owner와 client, resource server 사이에서 정보의 요청/응답/인증이 이루어진다고 생각하자

oauth

  • Resource Owner : 서비스를 이용하려는 사용자
  • Client : 소셜 서비스와 연동시키려는 웹 서비스
  • Resource Server (+ Authorization Server) : 연동하고자 하는 소셜 서비스의 서버(access token, refresh token등을 발급/재발급 해주는 역할)
  • Access Token : 인증 후에 사용자가 서비스 제공자가 아닌 소비자를 통해서 보호된 자원에 접근하기 위한 키를 포함한 값(Authorization Server로부터 발급받은 인증 토큰으로 Resource Server에 전달하여 서비스를 제공받을 수 있다)

OAuth를 쓰는 이유

일반적으로 생각해 보았을때 우리는 보통 어떠한 사이트에 회원가입할때 소셜로 로그인 할 수 있으면 소셜을 보통 사용하지 않는가? 내 정보를 일일히 적는거 너무 귀찮다.. 가끔은 비밀번호를 까먹기도 한다. 이러한 이유 때문에 OAuth를 쓰는것이다.

정리를 하면 애플리케이션에서 일일히 계정을 만들어 사용하게 되면 id/pwd 관리가 어렵고 개인정보 유출등 보안적으로 취약하기 때문에 이미 보안이 검증된 사이트의 API를 이용해 인증을 받아 보안적으로 안정을 유지하기 위해 사용한다


OAuth의 과정

처음 과정에 대해서 보게 되면 굉장히 복잡해 보인다. :-) 하지만 겁먹을 필요가 없다. 눈에 익지 않아서 그렇지 여러번 보면 패턴이 정해져 있기 때문에 수월하게 공부할 수 있을 것이다. (코드로 작성하는 방식은 비슷비슷 하기 때문에 이론적인 부분을 제대로 공부하자)

페이코 사이트에서 프로세스 과정을 캡쳐해 왔다. 아래의 1 - 6 과정 옆에 과정의 단계를 써놓았다.

payco

1. client가 resource server에 oauth 사용 요청을 한다

2. resource server가 client id와 client secret정보를 제공한다

(여기까지는 kakao developer 사이트에 들어가 직접 설정해주면 된다. 이때 리다이렉트 uri와 유저의 어떤 정보를 받을지에 대한 범위를 정해준다)

3. resource owner 가 client에서 소셜 로그인 버튼을 누르면 resource server의 로그인 창으로 리다이렉트되고 client에서 어떤 정보가 필요하고 이를 승인하는지에 대해 묻는다 (1,2,3,4)

4. 동의 버튼을 누르면 resource server는 미리 지정해놓았던 client uri로 리다이렉트 되고 code를 제공한다(5,6)

5. client는 code값과 client id, client secret값을 다시 resource server로 보내어 인증된 client가 맞는지 확인한다(7)

6. 확인되면 resource server는 access token을 client에게 발급하고 client는 해당 토큰을 이용해 resource owner의 정보를 가져와 사용할 수 있다(8)


서비스 등록

위의 과정 1,2번에 대한 설명을 잠깐 하고 넘어가겠다

먼저 OAuth 인증을 하기 위해서는 연동하고자 하는 소셜 서비스에 OAuth인증을 사용할 애플리케이션을 등록해야 한다 예를 들어 https://neonews.site url을 가지는 웹 사이트에서 카카오톡 OAuth인증을 이용해 회원가입/로그인을 하려고 할때 카카오톡 api 사이트에 들어가 사용할 웹사이트를 등록해주어야 한다.

이때 카카오톡 사이트에 미리 등록해야 할 url이 있다. 바로 리다이렉션 url 이다. 사용자가 카카오톡 인증 서버에 정보를 입력하면 카카오톡에서 정보에 대한 유효성을 검증한 후 유효한 정보라면 유저의 정보를 애플리케이션에 전송하기 위해 (애플리케이션에 유저의 정보를 전송해야 현재 로그인한 유저가 누구인지 식별이 가능하다) 미리 카카오톡 사이트에 등록한 리다이렉션 url로 데이터를 전송한다

이때 알고있어야 하는 것은 OAuth은 단순히 정보가 유효한지에 대한 인증을 해주며 유저의 정보는 애플리케이션에서 관리한다 !

Access Token

Access Token에 대해 조금 더 자세히 알아보자

유저의 정보를 리다이렉션 url을 통해 애플리케이션에 보내기 전 보안을 위해 OAuth에서 리다이렉션 url에 code라는 매개변수 정보를 추가하여 보낸다.

애플리케이션은 해당 Code를 얻은 후 해당 Code를 통해 유저의 카카오톡 계정 정보에 접근할 수 있는 액세스 토큰을 얻게 된다. 이때 해당 액세스 토큰을 얻기 위해 카카오톡 oauth url에 code 정보를 포함하여 요청을 보낸다. (code정보 , 카카오톡에 웹 서비스 등록시 발급받은 클라이언트 id, 클라이언트 secret 정보, 동일한 리다이렉션url 정보를 포함한다)

"https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id={client_id}&redirect_uri={REDIRECT_URI}&code={code}&client_secret={client_secret}"

client가 resource server에게 사용자 정보를 요청하기 위한 입장권과 같은 것 입장권에는 유효기간이 존재한다. 각 provider마다 다르다. 해당 유효기간이 지나면 더이상 해당 토큰을 사용할 수 없다

Refresh Token

위 access token이 유효기간이 만료되면, 새로운 access token을 발급받기 위해 필요한 토큰이다 이 토큰에도 유효기간이 있다. 각 provider마다 다르다. Access token보다는 유효기간이 훨씬 길다



실습을 진행해보자

장고를 통해 kakao 소셜 로그인을 구현하였다.

먼저 kakao site에서 발급받은 kakao id와 secret_key를 settings.py에 입력한다. 이때 이 key들은 외부에
보여지면 안되기 때문에 env파일에 넣어 불러와 사용하였다

# settings.py

KAKAO_ID=env('KAKAO_ID')
SECRET_KEY = env('SECRET_KEY')


resource server와 owner가 리다이렉트 할 url을 작성한다

# urls.py

path('signin/kakao/', kakao_login, name='kakao_login'),
  path('signin/kakao/callback/', kakao_login_callback, name='kakao-callback'),


except KakaoException()를 사용하기 위해 해당 class 를 import해서 사용했다(꼭 이 방법이 아니어도 된다 !!)

# exception.py

class SocialLoginException(Exception):
    pass
class KakaoException(Exception):
    pass


# signin.html


<a class="login-kakao" href="{% url 'user:kakao_login' %}">카카오톡으로 로그인하기</a>


카카오 로그인 버튼을 누르면 카카오 로그인 창으로 리다이렉트 시켜주기 위해 redirect_uri에 미리 정해놓았던 uri를 입력한다.(미리 카카오 서버와 약속한 uri) 카카오 서버에서의 인증이 성공하면 kakao api가 유저를 설정해놓은 redirect_uri로 보내서 다시 kakao_login_callback로 돌아오게 한다

# views.py

def kakao_login(request):
    client_id = os.environ.get("KAKAO_ID")
    redirect_uri = "http://127.0.0.1:8000/user/signin/kakao/callback"
    return redirect(f"https://kauth.kakao.com/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code")


카카오톡 로그인창에 입력한 유저의 정보를 웹사이트에 보내기전 보안을 위해 OAauth에서 리다이렉션 url에 code라는 매개변수 정보를 추가하여 보낸다. 웹 사이트는 해당 code를 얻은 후 이 code를 통해 유저의 카카오톡 계정 정보에 접근할 수 있는 Access_token을 얻게 된다.

def kakao_login_callback(request):
    code = request.GET.get('code', None)
    if code is None:
        KakaoException("Can't get code")
    client_id = os.environ.get("KAKAO_ID")
    redirect_uri = "http://127.0.0.1:8000/user/signin/kakao/callback"
    client_secret = os.environ.get('SECRET_KEY')
    request_access_token = requests.post(
            f"https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id={client_id}&redirect_uri={redirect_uri}&code={code}&client_secret={client_secret}",
            headers={"Accept": "application/json"},
        )
    token_info_json = request_access_token.json()
    error = token_info_json.get("error", None)
    if error is not None:
        raise KakaoException()
    access_token = token_info_json.get("access_token")


Access Token을 통해 사용자 정보를 요청한다. 사용자 정보 요청은 사용자의 id나 카카오 계정 이메일등 상세 정보를 얻어 올 수 있는 기능이다. 이를 위해서 Bearer {access_token} 헤더를 담은 요청을 kakao 서버에 보낸다. 요청을 받은 서버에서는 유효성 검증 후 정보를 보낸다

    profile_request = requests.get(
        "https://kapi.kakao.com/v2/user/me",
        headers={"Authorization": f"Bearer {access_token}"},
    )


받아온 정보를 get을 통해 얻어올 수 있다. 이후 서비스의 로그인 시스템에 받아온 데이터를 가지고 적용시켜주면 된다 (user DB등에 등록)

    profile_json = profile_request.json()
    kakao_account = profile_json.get("kakao_account")
    nickname = kakao_account.get("nickname", None)
    email = kakao_account.get("email", None)
    user, created = User.objects.get_or_create(email=email)
    if created:
        user.set_password(None)
        user.nickname = nickname
        user.is_active = True
        user.save()
        auth.login(request, user)
        return redirect("/")
    auth.login(request, user)
    return redirect("/")




reference

https://velog.io/@piecemaker/OAuth2-%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90