한양과학기술고등학교

에듀테크 프로젝트 대시보드

OAuth2 password flow 기초

OAuth2PasswordBearer OAuth2PasswordRequestForm /token Bearer

FastAPI 내장 유틸로 '토큰 발급 → 헤더로 토큰 전달 → 현재 사용자 확인' 흐름을 만듭니다.

1. 스킴 선언

from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
#  -> /docs 에 Authorize 버튼이 생김
#  -> 요청의 Authorization: Bearer <token> 헤더에서 토큰을 추출

2. 토큰 발급 엔드포인트

from typing import Annotated
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordRequestForm

@app.post("/token")
def login(form: Annotated[OAuth2PasswordRequestForm, Depends()]):
    user = authenticate(form.username, form.password)
    if not user:
        raise HTTPException(400, "아이디 또는 비밀번호가 올바르지 않습니다")
    token = create_access_token({"sub": user.username})
    return {"access_token": token, "token_type": "bearer"}

OAuth2PasswordRequestForm 은 username·password 를 폼 데이터로 받습니다 (python-multipart 필요).

3. 현재 사용자 의존성

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]) -> User:
    username = verify_token(token)          # 실패 시 None
    if username is None:
        raise HTTPException(
            status_code=401,
            detail="인증이 필요합니다",
            headers={"WWW-Authenticate": "Bearer"},
        )
    user = get_user(username)
    if user is None:
        raise HTTPException(401)
    return user

CurrentUser = Annotated[User, Depends(get_current_user)]

4. 보호된 라우트

@app.get("/users/me")
def read_me(user: CurrentUser):
    return user

@app.get("/admin")
def admin(user: CurrentUser):
    if not user.is_admin:
        raise HTTPException(403, "권한이 없습니다")
    return {"ok": True}

5. 스코프 / 역할

간단한 앱은 user.role 체크로 충분합니다. 세분화가 필요하면 Security(get_current_user, scopes=["items:write"]) 와 SecurityScopes 를 사용.

"타입 힌트 하나로 검증·직렬화·문서화가 동시에 이뤄진다"