FastAPI OAuth2 인증 구현 가이드

인증 메커니즘 비교

웹 애플리케이션에서 사용자 식별은 필수적인 요소입니다. 대표적인 방식으로 Session 기반Token 기반이 있으며, 현대 서비스에서는 주로 후자를 채택합니다.

Session 방식의 한계

서버 측에 세션 저장소를 두고 클라이언트의 session_id와 사용자 정보를 매핑하는 방식입니다. 다중 서버 환경에서 세션 동기화 문제가 발생하며, 서버 자원도 추가로 소모됩니다.

Token 방식의 장점

서버가 토큰을 발급만 하고 실제 검증은 클라이언트가 전달한 토큰의 서명을 통해 수행합니다. 이로 인해 상태를 서버에 저장할 필요가 없어져 확장성이 크게 향상됩니다.

OAuth2와 Bearer 토큰

OAuth2는 토큰 기반 인증의 표준 스펙을 정의합니다. 특히 Bearer Token 방식은 HTTP 요청 헤더에 다음과 같이 포함하여 전송합니다:

Authorization: Bearer <token_value>

이 형식에서 AuthorizationBearer는 표준에 정의된 고정 값이며 변경할 수 없습니다.

FastAPI에서 OAuth2 구현하기

의존성 설정

FastAPI는 OAuth2PasswordBearer 클래스를 제공하여 Bearer 토큰 추출을 자동화합니다.

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import jwt, JWTError
from datetime import datetime, timedelta
from typing import Optional

app = FastAPI()

# 토큰 발급 엔드포인트 경로 지정
token_extractor = OAuth2PasswordBearer(tokenUrl="auth/issue")

JWT 토 생성 및 검증

토큰의 암호화와 복호화는 OAuth2 범위 밖이므로 별도 구현이 필요합니다. 여기서는 python-jose 라이브러리를 활용합니다.

# 보안 설정
SIGNING_KEY = "a8f3e2b1c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1"
TOKEN_ALGORITHM = "HS256"
VALIDITY_PERIOD_MINUTES = 30

def issue_access_token(payload_data: dict, lifetime: Optional[timedelta] = None):
    """사용자 데이터를 JWT 토큰으로 변환"""
    expiration = datetime.utcnow() + (
        lifetime if lifetime else timedelta(minutes=VALIDITY_PERIOD_MINUTES)
    )
    
    payload_data["iat"] = datetime.utcnow()  # 발급 시각
    payload_data["exp"] = expiration           # 만료 시각
    
    return jwt.encode(payload_data, SIGNING_KEY, algorithm=TOKEN_ALGORITHM)

def parse_access_token(token_string: str) -> dict:
    """JWT 토큰을 디코딩하여 원본 데이터 반환"""
    try:
        decoded = jwt.decode(token_string, SIGNING_KEY, algorithms=[TOKEN_ALGORITHM])
        return decoded
    except JWTError:
        raise ValueError("유효하지 않은 큰입니다")

로그인 엔드포인트 구현

클라이언트가 자격 증명을 제출하면 검증 후 토큰을 발급합니다. OAuth2 표준에 따르면 응답은 특정 형식을 따라야 합니다.

@app.post("/auth/issue")
async def authenticate_user(
    identity: str = Body(...), 
    secret: str = Body(...)
):
    # 실제 환경에서는 데이터베이스 조회 및 비밀번호 해시 비교 수행
    verified_account = verify_credentials(identity, secret)
    
    if not verified_account:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="잘못된 자격 증명입니다"
        )
    
    token_content = {"sub": verified_account["uid"], "role": verified_account["role"]}
    access_token = issue_access_token(token_content)
    
    return {
        "access_token": access_token,
        "token_type": "bearer"
    }

보호된 리소스 엔드포인트

의존성 주입을 통해 토큰 검증을 재사용 가능한 형태로 분리합니다.

async def get_current_user(bearer_token: str = Depends(token_extractor)):
    """의존성: 요청에서 사용자 정보 추출"""
    auth_failure = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="인증이 필요합니다",
        headers={"WWW-Authenticate": "Bearer"},
    )
    
    try:
        token_payload = parse_access_token(bearer_token)
        user_identifier = token_payload.get("sub")
        
        if user_identifier is None:
            raise auth_failure
            
    except (ValueError, JWTError):
        raise auth_failure
    
    return user_identifier

@app.get("/profile")
async def fetch_user_profile(active_user: str = Depends(get_current_user)):
    """토큰 검증 완료 후 실행되는 보호된 엔드포인트"""
    # active_user를 통해 데이터베이스에서 상세 정보 조회
    return {
        "message": "접근 성공",
        "user_id": active_user
    }

API 문서 연동

FastAPI의 자동 문서화(/docs)에서 OAuth2PasswordBearer를 사용하면 인터페이스 우측에 자물쇠 아이콘이 표시됩니다. 이를 클릭해 토큰을 입력하면 이후 모든 요청에 자동으로 Authorization 헤더가 포함됩니다.

테스트 검증

다음 curl 명령으로 전체 흐름을 확인할 수 있습니다:

# 1. 토큰 발급 요청
curl -X POST "http://localhost:8000/auth/issue" \
  -H "Content-Type: application/json" \
  -d '{"identity":"user01","secret":"mypassword"}'

# 2. 보호된 리소스 접근 (응답에서 받은 토큰 사용)
curl -X GET "http://localhost:8000/profile" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

추가 고려사항

  • Refresh Token: Access Token의 만료 기간을 짧게 설정하고 별도의 Refresh Token으로 재발급
  • 비밀번호 해싱: 평문 저장 금지, bcrypt 등의 알고리즘 활용
  • HTTPS 적용: 운영 환경에서 토큰 탈취 방지를 위한 필수 설정
  • 토큰 블랙리스트: 로그아웃 시 즉시 무효화가 필요한 경우 Redis 등에 저장

태그: FastAPI oauth2 jwt python-jose Bearer Token

8월 3일 20:02에 게시됨