Skip to content

32. Security - First Steps


1. Security - First Steps

  • 일부 도메인에 백엔드 API가 있고, 다른 도메인이나 동일한 도메인의 다른 path에 프론트엔드가 있으며, 사용자 이름과 비밀번호를 사용하여 프론트엔드가 백엔드로 인증을 받을 수 있도록 하는 예제를 살펴보자.
  • OAuth2를 사용하여 FastAPI로 빌드할 수 있으며, 필요한 정보를 찾기 위해 긴 사양 전체를 읽는 시간을 절약할 수 있게 된다.
  • FastAPI에서 제공하는 도구를 사용하여 보안을 처리해 보자.


2. Create main.py

  • 다음과 같이 main.py를 작성한다.


from fastapi import FastAPI, Depends
from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
    return {"token": token}


3. Run it

  • 다음과 같이 실행한다.


uvicorn main:app --reload


4. Check it

  • http://127.0.0.1:8000/docs로 이동하면 다음과 같이 확인된다.


001


  • Authorize 버튼을 클릭하면 다음과 같이 usernamepassword 등을 입력할 수 있는 인증 양식을 확인할 수 있다.


002


  • 물론 최종 사용자를 위한 프론트엔드는 아니지만, 모든 API를 대화식으로 문서화하는 자동 도구이다.
  • 프론트엔드 팀에서 사용할 수 있으며, 동일한 응용 프로그램을 디버그, 확인 및 테스트하기 위해 직접 사용할 수도 있다.


5. The password flow

  • password "flow"는 보안 및 인증을 처리하기 위해 OAuth2에서 정의된 "flows" 중 하나이다.
  • OAuth2는 백엔드 또는 API가 사용자를 인증하는 서버와 독립적일 수 있도록 설계되었다.
  • 물론 위의 예제에서는 동일한 FastAPI 애플리케이션이 API와 인증을 같이 처리한다.
  • 위의 예제를 다음과 같이 검토해 보자.


1] 사용자는 프론트엔드에 usernamepassword를 입력하고 Enter를 누른다.

2] 프론트엔드는 해당 usernamepasswordtokenUrl="token"으로 선언된 API의 특정 URL로 보낸다.

3] 아직 구현하지는 않았지만 API는 usernamepassword를 확인하고 "token"으로 응답한다.

  • "token"은 나중에 이 사용자를 확인하는 데 사용할 수 있는 일부 콘텐츠가 포함된 문자열이다.
  • 일반적으로 token은 일정 시간이 지나면 만료되도록 설정된다.
  • 따라서 사용자는 나중에 다시 로그인해야 한다.
  • 그러므로 영원히 작동하는 영구키와 다르게 token을 도난당하는 것이 더 낫다.

4] 프론트엔드는 해당 token을 임시로 어딘가에 저장한다.

5] 사용자가 프론트엔드를 클릭하여 프론트엔드 웹 앱의 다른 섹션으로 이동한다.

6] 프론트엔드는 API에서 더 많은 데이터를 가져와야 한다.

  • 그러나 특정 endpoint에 대한 인증이 필요하다.
  • 따라서 API로 인증하기 위해 Bearer 값에 token을 더한 Authorization header를 보낸다.
  • token에 foobar가 포함된 경우 Authorization header의 내용은 Bearer foobar가 된다.


6. FastAPI's OAuth2PasswordBearer

  • FastAPI는 보안 기능을 구현하기 위해 다양한 추상화 수준에서 여러 도구를 제공한다.
  • 이 예제에서는 Bearer token을 사용하여 Password flow와 함께 OAuth2를 사용할 것이며, OAuth2PasswordBearer 클래스를 사용하여 이를 수행한다.


Note

  • "bearer" token만 사용할 수 있는 것은 아니다.
  • 예제에 가장 적합하기 때문에 사용하는 것이다.


  • 다음과 같이 OAuth2PasswordBearer 클래스의 인스턴스를 생성할 때 tokenUrl 매개변수를 전달하며, 이 매개변수에는 클라이언트가 token을 얻기 위해 usernamepassword를 보내는 데 사용할 URL이 포함된다.


from fastapi import FastAPI, Depends
from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
    return {"token": token}


Note

  • 위의 예제의 tokenUrl="token"은 아직 생성하지 않은 relative URL token을 나타낸다.
  • relative URL이므로 ./token과 동일하다고 볼 수 있다.
  • 만약 API가 https://example.com/에 있는 경우 realtive URL을 사용하기 때문에 https://example.com/token을 참조한다.
  • 하지만 API가 https://example.com/api/v1/에 있는 경우 https://example.com/api/v1/token을 참조한다.


  • 이 매개변수는 endpoint / path operation을 생성하지 않지만, URL /token이 클라이언트로 하여금 token을 가져오는 데 사용해야 하는 URL이 될 것이라고 선언한다.
  • 해당 정보는 OpenAPI에서 사용된 후 대화형 API 문서 시스템에서 사용된다.
  • 위의 예제에서 oauth2_scheme 변수는 OAuth2PasswordBearer의 인스턴스이지만, "callable"이기도 하기 때문에 Depends를 사용할 수 있다.


7. Use it

  • Depends를 사용하여 dependency oauth2_scheme을 전달할 수 있다.


from fastapi import FastAPI, Depends
from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
    return {"token": token}


  • 이 dependency는 path operation function의 매개변수 token에 할당된 str 타입을 제공한다.
  • 그 후 FastAPI는 이 dependency를 사용하여 OpenAPI 스키마에서 "security scheme"을 정의할 수 있음을 알게 된다.


8. What it does

  • 해당 Authorization header에 대한 request를 살펴보고 값이 Bearer token인지 확인하고 token을 str 타입으로 반환한다.
  • 만약 Authorization header가 표시되지 않거나 값에 Bearer token이 없으면, 401 상태 코드 에러(UNAUTHORIZED)로 응답한다.

References