더 나은 서비스를 위해 의견을 들려주세요! 설문조사 링크
comjeonggosi

인증과 인가: OAuth 2.0과 JWT


인증과 인가 완벽 가이드

1. 인증 vs 인가

인증 (Authentication): 너는 누구니? 인가 (Authorization): 무엇을 할 수 있니?

2. OAuth 2.0

Authorization Code Flow

1. 사용자 → 클라이언트: 구글 로그인 클릭
2. 클라이언트 → 구글: 인증 요청
3. 사용자 → 구글: 로그인 + 권한 승인
4. 구글 → 클라이언트: Authorization Code
5. 클라이언트 → 구글: Code + Client Secret
6. 구글 → 클라이언트: Access Token
7. 클라이언트 → API: Access Token으로 요청

구현

from flask import redirect, request
import requests

CLIENT_ID = 'your_client_id'
CLIENT_SECRET = 'your_client_secret'
REDIRECT_URI = 'http://localhost:5000/callback'

@app.route('/login')
def login():
    return redirect(
        f'https://accounts.google.com/o/oauth2/v2/auth?'
        f'client_id={CLIENT_ID}&'
        f'redirect_uri={REDIRECT_URI}&'
        f'response_type=code&'
        f'scope=openid email profile'
    )

@app.route('/callback')
def callback():
    code = request.args.get('code')
    
    # Access Token 요청
    response = requests.post('https://oauth2.googleapis.com/token', data={
        'code': code,
        'client_id': CLIENT_ID,
        'client_secret': CLIENT_SECRET,
        'redirect_uri': REDIRECT_URI,
        'grant_type': 'authorization_code'
    })
    
    access_token = response.json()['access_token']
    
    # 사용자 정보 요청
    user_info = requests.get(
        'https://www.googleapis.com/oauth2/v1/userinfo',
        headers={'Authorization': f'Bearer {access_token}'}
    ).json()
    
    return f"Welcome, {user_info['email']}!"

3. JWT (JSON Web Token)

구조

Header.Payload.Signature

Header: {"alg": "HS256", "typ": "JWT"}
Payload: {"user_id": 123, "exp": 1234567890}
Signature: HMACSHA256(base64(Header) + "." + base64(Payload), secret)

구현

import jwt
from datetime import datetime, timedelta

SECRET_KEY = 'your-secret-key'

# 토큰 생성
def create_token(user_id):
    payload = {
        'user_id': user_id,
        'exp': datetime.utcnow() + timedelta(hours=24)
    }
    return jwt.encode(payload, SECRET_KEY, algorithm='HS256')

# 토큰 검증
def verify_token(token):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        return payload['user_id']
    except jwt.ExpiredSignatureError:
        return None  # 만료됨
    except jwt.InvalidTokenError:
        return None  # 유효하지 않음

4. Refresh Token

@app.route('/login', methods=['POST'])
def login():
    # 인증 성공
    access_token = create_token(user_id, expires_in=15*60)  # 15분
    refresh_token = create_refresh_token(user_id, expires_in=30*24*60*60)  # 30일
    
    return {
        'access_token': access_token,
        'refresh_token': refresh_token
    }

@app.route('/refresh', methods=['POST'])
def refresh():
    refresh_token = request.json['refresh_token']
    user_id = verify_refresh_token(refresh_token)
    
    if user_id:
        new_access_token = create_token(user_id)
        return {'access_token': new_access_token}
    else:
        return {'error': 'Invalid refresh token'}, 401

결론

인증 방식 선택:

  • 세션 기반: 전통적, 서버 상태 관리
  • JWT: Stateless, 분산 시스템 적합
  • OAuth 2.0: 소셜 로그인, 제3자 인증