토요일.

지금만들고 있는 Toy Project는 Admin Site이기 때문에, 불특정 다수를 대상으로 하는 것도 아니고 통상적인 자그마한 수준의 트랜잭션을 감당 할 예정이라 모든 요청에 인증 및 권한체크를 하는 것으로 구상을 하고 있다.

인증 후 Token의 만료시간을 30분으로 잡고 있는데, 이 경우 계속 사용을 하면(===요청을 하면), 그 만료시간을 자동으로 늘려주려주는 방식의 구조를 만들려고 한다.

알아야 하는 사항

  • login을 제외한 모든 요청은 인증체크와 권한체크가 되어야 한다는 전제이다.
  • 모든 요청에 인증체크가 자동으로 되기 위해서 주입(injection)을 사용한다.
  • 체크를 위한 값과 체크의 결과를 응답에 섞어서 받거나 내릴수 없기 때문에 request.header와 response.header를 사용한다.
  • 해당 Request, Response 객체는 fastapi가 아니고 starlette에 있다.
  • dependencies 선언에 파라메터를 넣을 수 없어서, 중간 함수가 하나 필요하다.

이 정도만 있으면, 구글링을 통해서 자신이 원하는 구조를 만들 수 있겠으나 코드를 좀 살펴보면.

from pydantic import BaseModel
from fastapi import APIRouter, Depends
from starlette.requests import Request
from starlette.responses import Response

# (1)
def check_auth_pass_response(request: Request, response: Response):
    check_auth_by_header(request, response)

# (2)
routerDependencies = APIRouter(
    prefix="/auth",
    tags=["auth"],
    dependencies=[Depends(check_auth_pass_response)]
)

# (3)
@routerDependencies.get("/checkAuth")
def check_for_autorization_and_authentication():
    return None

(1)은 (2)에 의해서 선언된 Depends이다. (3)에서 바로 (1)로 갈 수도 있겠으나, 우리는 Request, Response 파라메터가 필요하기 때문에 중간 함수가 하나 필요하다.

(2)의 tags는 FastAPI docs에서 보이는 라우팅 경로들을 구분해주는 tag이고, perfix는 말 그대로 접두사니, 실제 호출은 /auth/checkAuth가 된다.

(3)이 실제 표출되는 라우팅인데, 함수 상단에 @routerDependencies.이라고 붙어있으니, (2)에 영향을 받는다는 것을 알 수 있다. 그리고 응답값이 None인데, 실제 Refresh Token은 생성해서 response.header에 달아 줄 것이라, Body는 필요로 하지 않는다.
왜 이런 구조가 되냐하면, 나중에 실제 응답 Body가 필요한 다른 요청에서도 저 주입[Depends(check_auth_pass_response)]을 동일하게 사용 할 수 있어야 하기 때문이다.

이것은 200이 아니면 모든 응답이 그 상황에 따라 매우 적절하게 Http Status Code로 보내줘야 한다는 것을 의미한다.

def check_auth_by_header(request: Request, response: Response):
    # request.header에서 토큰을 꺼내서 검증하다가 뭔가 마음에 안들면 여기서 Http Exception을 raise하면 된다.

    refresh_token = '' # 갱신된 Token을 넣어준다.
    response.headers.setdefault('refreshtoken', refresh_token)
    return None

ResponseHeader
‘refreshtoken’이라는 Key는 나중에 Client에서 Response.header에서 확인 할 수 있는 Key이고, Client는 적당한 부분에서 Client Token을 갱신 해 주면 되겠다.

주의 Axios에서는 Header가 모두 소문자로 바뀌는 현상이 있었다.


app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
    expose_headers=["refreshtoken"]
)

여기까지만 하고, 브라우져 개발자 Network Tab에서 헤더가 찍히는것을 보고 코드에서 값에 접근 할 수 있다면 좋았겠지만.. 아니다.
Access-Control-Expose-Headers라는것이 있는데, 요약하면 서버에서 보내 줄 때 별다른 표시를 안해주면, 브라우져가 알아서 잡다한것은 빼고 꼭 필요한 것만 전달해준다. 보안을 위한 브라우져의 양심이랄까?

FastAPI를 사용한다면 Fast CORS부분을 보고 설정하면 되고, Node등 다른 것이라면 구글링을 해서 설정하면 된다.

Read Count