詳細検索

OAuth 해소: 직관적인 OAuth 튜토리얼

아바타
글쓴이 Chen Ziyu

OAuth 해소: 직관적인 OAuth 튜토리얼
English에서 번역 • 원문 보기

OAuth(Open Authorization의 약자)를 애플리케이션에 통합하는 것은 많은 초급 소프트웨어 엔지니어들에게 다소 위압적이고 낙담하게 느껴질 수 있습니다. 결국, 올바른 구성으로 복잡한 인증 과정을 구현하는 데 몇 시간, 심지어 며칠을 투자해야 하지만, 결국 아주 작은 기능만 완성했다는 사실을 깨닫게 됩니다. 많은 노력이 따르지만, OAuth는 사용자가 웹사이트와 애플리케이션에 개인 정보를 안전하게 접근할 수 있는 효율적인 방법입니다. 따라서 OAuth가 어떻게 작동하는지 이해하는 데 노력할 가치가 있습니다. 이 글에서는 OAuth 프로세스에 포함된 모든 단계를 자세히 설명하여 여러분의 OAuth 여정을 시작할 수 있도록 도와드리겠습니다.

튜토리얼에 들어가기 전에 두 가지 주의할 점이 있습니다:

  1. 이 글에서 사용할 OAuth 버전은 현재 업계 표준인 OAuth 2.0입니다. 'https'(TLS)는 보안을 위해 필요합니다.
  2. PKCE는 선택 사항이지만, 전체 OAuth 과정을 더 안전하게 만들기 때문에 이 튜토리얼에 포함하기로 결정했습니다.

두 개의 애플리케이션이 있다고 가정해 봅시다: 앱 A(클라이언트 애플리케이션)와 앱 B(권한 부여 서버). 앱 A는 앱 B에 저장된 사용자 X의 정보에 접근하고자 합니다. 앱 A가 이 잠재적으로 민감한 정보를 안전하게 접근할 수 있도록 OAuth 프로세스를 구축해야 합니다.

1단계: 권한 부여 URL 생성

앱 A가 앱 B에서 사용자 X의 정보를 접근하려면, 앱 B는 먼저 사용자 X의 신원을 확인해야 합니다. 앱 B가 사용자 X의 신원을 확인하는 가장 간단한 방법은 사용자 X에게 앱 B에 로그인하도록 요청하는 것입니다. 하지만 사용자 X는 앱 A를 포함하지 않고 직접 앱 B에 로그인해서는 안 됩니다. 로그인 URL에는 앱 A의 정보가 포함되어야 하며, 앱 B가 사용자 X의 정보를 보내야 할 목적지, 즉 앱 A를 인지할 수 있어야 합니다. 여기서 권한 부여 URL이 중요한 역할을 합니다. 권한 부여 URL은 본질적으로 앱 A가 사용자 X의 정보를 접근하려는 시도를 앱 B에 알리는 URL입니다. 앱 B는 이후 사용자 X를 자신의 로그인 페이지로 리디렉션합니다. 이를 생성하려면 다음 정보가 필요합니다:

  1. 고객 ID
  2. 코드 검증기
  3. 코드 챌린지
  4. 코드 챌린지 방법
  5. URI 리디렉션

클라이언트 ID는 앱 B가 발행하는 앱 A의 공개 식별자입니다. 이는 앱 B가 앱 A의 신원을 확인하는 데 도움을 줍니다. 클라이언트 ID가 없으면 앱 B는 신뢰 부족으로 인해 앱 A에 민감한 정보를 제공하지 않을 것입니다.

코드 검증기와 코드 챌린지는 모두 권한 부여 코드를 더 안전하게 만드는 것을 목표로 하는 PKCE(Proof Key for Code Exchange의 약자) 프로세스의 일부입니다. 코드 검증기는 일부 요구사항을 충족하는 무작위 코드이고, 코드 챌린지는 코드 검증기를 변형한 것입니다. 1단계에서는 코드 도전만 필요합니다. 코드 검증기는 2단계까지 사용하지 않습니다.

다음 코드를 사용해 코드 검증기와 코드 챌린지를 만들 수 있습니다.

import re
import os
임포트 베이스64
Hashlib 가져오기

code_verifier = base64.urlsafe_b64encode(os.urandom(50)).decode("utf-8")
code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier)
code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest()
code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8")
code_challenge = code_challenge.replace("=", "")

코드 챌린지 메서드는 코드 검증기를 코드 챌린지로 변환하는 데 사용되는 인코딩 방법입니다. 일반적으로 OAuth 2.0은 위 코드 예제와 마찬가지로 SHA-256을 코드 챌린지 메서드로 사용합니다.

리디렉션 URI는 앱 B에서 사용자 X가 로그인하면 앱 A가 트리거하는 콜백 API입니다. 그 목적은 나중에 더 자세히 설명하겠습니다.

위의 모든 정보를 모으면, 인증 URL을 조립하기 시작할 수 있습니다. 파이썬에서는 requests_oauthlib라는 라이브러리를 사용해 이를 수행할 수 있습니다.

from requests_oauthlib import OAuth2Session

app_b = OAuth2Session(CLIENT_ID, redirect_uri=REDIRECT_URI)
authorization_url, _ = app_b.authorization_url(AUTH_URL, code_challenge=code_challenge, code_challenge_method="S256")

위 코드 예시에서 앱 A는 'OAuth2Session' 생성자를 사용하여 클라이언트 ID와 리디렉션 URI를 매개변수로 사용하여 'app_b' 인스턴스를 초기화합니다. 그 후 앱 A는 'authorization_url' 메서드를 사용하여 인증 URL을 생성합니다. 매개변수에는 앱 B의 권한 API, 코드 챌린지, 코드 챌린지 메서드의 기본 URL이 포함됩니다. 위 코드 예시에서 생성된 권한 인증(Authorization URL)은 다음과 같습니다:

https://www.example.com/get\_authorization\_token?response\_type=code&client\_id=&code\_challenge=&code\_challenge\_method=S256

인증 URL이 준비되면, 앱 A는 이를 사용자 X에게 보내 열도록 요청할 수 있습니다. 사용자 X가 권한 URL을 열면, 앱 B는 앱 A가 사용자 X의 정보 접근을 요청한 내용을 기록하고 사용자 X를 앱 B의 로그인 페이지로 리디렉션합니다.

2단계: 콜백 API에서 토큰 가져오기 및 정보 가져오기

사용자 X가 앱 B에 로그인했습니다. 이제 어떻게 해야 할까요? 앱 B는 사용자 X에게 리디렉션 응답을 보내, 사용자 X가 Redirect URI에 명시된 앱 A의 콜백 API로 리디렉션됩니다. 콜백 API의 목적은 두 가지입니다: 토큰을 가져오고 앱 B에서 사용자 X의 정보를 가져오는 것입니다.

먼저, 앱 A가 앱 B에서 가져와야 하는 두 개의 토큰, Access Token과 Refresh Token부터 살펴보겠습니다. Access Token은 보호된 자원에 접근하기 위해 필요합니다. 우리의 경우, App A는 App B가 발행한 Access Token을 소유하기 전까지는 App B에서 사용자 X의 정보를 접근할 수 없습니다. Access Token은 취소 불가능하므로, App B는 App A에 전송한 후 이를 취소할 수 없습니다. 따라서 Access Token은 동일한 토큰을 통해 강력하고 위험합니다(말장난 의도는 아닙니다). 잠재적인 데이터 유출을 방지하기 위해 Access Token은 보통 수명이 짧습니다.

Access Token을 단기간 유지하는 것은 데이터 보안을 보장하는 데 중요한 역할을 합니다. 하지만 사용자 경험 측면에서 다른 문제가 발생합니다: Access Token은 자주 만료되기 때문에 사용자가 새 Access Token을 받기 위해 자주 재인증을 해야 합니다. 다행히도 Refresh Token이 도움을 줍니다. Refresh Token은 현재 Access Token이 만료될 때 새로운 Access Token을 얻는 역할을 합니다. 예시에서 App B는 초기 Access Token과 함께 App A에 Refresh Token을 보냅니다. 현재 Access Token이 만료된 후 사용자 X에게 다시 App B에 로그인하라고 요청하는 대신, App A는 Refresh Token을 사용해 App B에서 새 Access Token을 가져올 수 있습니다.

초기 액세스 토큰과 새로고침 토큰을 가져오려면 다음 정보가 필요합니다:

  1. 토큰 URL
  2. 권한 코드
  3. 코드 검증기
  4. 코드 챌린지 방법
  5. 고객 ID

토큰 URL: 토큰 URL은 앱 B 측에서 토큰을 발행하고 갱신하는 API입니다.

권한 코드: 권한 코드는 앱 B에서 제공하는 코드입니다. 앱 B는 앱 A의 콜백 API 끝에 URL 매개변수로 부착합니다.

코드 검증기: 코드 검증기에 대해 제가 이전에 설명한 내용을 참고해 주세요.

코드 챌린지: 코드 챌린지 방법에 대해 제가 이전에 설명한 내용을 참고해 주세요.

클라이언트 ID: 이전 설명에서 클라이언트 ID에 대해 참고해 주세요.

다음 코드 예시는 API 호출을 통해 초기 Access Token, Refresh Token, 그리고 Access Token의 만료 시기에 대한 정보를 받습니다.

# requests_oauthlib 가져오기 OAuth2Session
# app_b = Auth2Session(CLIENT_ID, redirect_uri=REDIRECT_URI)

토큰 = app_b.fetch_token( 
    token_url=TOKEN_URL, 
    코드=auth_code, 
    code_verifier=code_verifier, 
    code_challenge_method='S256', 
    client_id=CLIENT_ID, 
)
access_token = 토큰["access_token"]
refresh_token = 토큰["refresh_token"]
expires_at = 토큰["expires_at"]
expires_in = 토큰["expires_in"]

액세스 토큰: 리프레시 토큰을 사용해 액세스 토큰을 새로고침하려면 다음 정보가 필요합니다:

  1. 토큰 URL
  2. 새로고침 토큰
  3. 고객 ID
  4. 클라이언트 비밀

토큰 URL: 이전에 설명한 토큰 URL을 참고해 주세요.

리프레시 토큰: 리프레시 토큰에 대해 제가 이전에 설명한 내용을 참고해 주세요.

클라이언트 ID: 이전 설명에서 클라이언트 ID에 대해 참고해 주세요.

클라이언트 시크릿은 OAuth 애플리케이션과 인증 서버만 아는 자격 증명입니다. 저희 경우, 클라이언트 시크릿은 앱 B가 앱 A의 신원을 검증하는 데 도움을 주는 비밀번호 역할을 합니다.

다음 코드 예시는 액세스 토큰을 새로고침하기 위해 앱 B에 API 호출을 합니다.

# requests_oauthlib 가져오기 OAuth2Session
# app_b = Auth2Session(CLIENT_ID, redirect_uri=REDIRECT_URI)

토큰 = app_b.refresh_token( 
    token_url=TOKEN_URL, 
    refresh_token=refresh_token, 
    client_id=CLIENT_ID, 
    client_secret=CLIENT_SECRET
)
access_token = 토큰["access_token"]
expires_at = 토큰["expires_at"]
expires_in = 토큰["expires_in"]

액세스 코드를 확보한 앱 A는 마침내 사용자 X를 대신해 앱 B로부터 사용자 X의 정보를 가져올 수 있습니다. 앱 A가 해야 할 일은 API 호출을 앱 B의 지정 API에 호출하여 외부 애플리케이션에 사용자 정보를 제공하는 것(보통 "/me"으로 끝나며)을 요청 헤더에 포함시키고, 액세스 토큰을 사용하는 것뿐입니다.

response = requests.request( 
    "받아라", 
    ME_URL, 
    headers={"Authorization": "Bearer {}".format(token["access_token"])}, 
)

3단계: 다음은 무엇일까?

앱 A의 사양에 따라 앱 A는 콜백 API에서 다음과 같은 작업을 할 수 있습니다:

  1. 앱 A는 사용자 X의 정보를 데이터베이스에 저장할 수 있습니다.
  2. 앱 A는 액세스 토큰과 새로고침 토큰을 세션에 저장하여 앱 B에서 사용자 X의 인증 상태를 추적할 수 있습니다. 새 침 토큰이 만료되면 앱 A는 사용자 X의 세션이 만료된 것으로 간주하여 사용자 X를 로그아웃할 수 있습니다.

면책 조항

이 글을 마치기 전에, 서로 다른 권한 부여 서버(앱 B)마다 약간 다른 구현체를 가질 수 있음을 말씀드리고 싶습니다. 따라서 이 글의 코드 예제는 권한 서버에 대한 API 호출에 어떤 매개변수를 포함할지 결정할 때 참고만 하시길 권장합니다. 클라이언트 애플리케이션(앱 A)을 구현하고 대상 권한 부여 서버(앱 B)와 어떻게 상호작용해야 하는지 설계할 때, 권한 부여 서버의 문서나 코드베이스를 참고하여 어떤 변수가 어떤 API 호출에 필요한지 이해하시기 바랍니다.

이 글에서는 클라이언트 애플리케이션에서 OAuth 프로세스를 설정하여 다른 애플리케이션에서 보호된 자원에 접근하는 방법을 시연했습니다. 또한 OAuth 프로세스의 각 단계에 필요한 변수와 그 이유를 설명했습니다. 이 글이 OAuth를 조금 더 잘 이해하는 데 도움이 되었기를 바랍니다.

Related Articles