LabHub

Keycloak 과 기업 인증 · 애플리케이션 연동(리다이렉트·PKCE) · 실습

FastAPI 앱에 토큰 검증 미들웨어 붙이기

LabHub 에서 이어서 보기

목표

FastAPI 앱에 JWT 검증 미들웨어를 직접 붙여, 토큰 없음·만료·잘못된 대상·권한 부족을 각각 올바른 상태 코드로 구분해 처리한다.

왜 중요한가

토큰 검증 미들웨어는 대부분 라이브러리로 해결하지만, 옵션 하나를 잘못 주면 조용히 뚫립니다. 그리고 상태 코드 구분도 자주 틀립니다 — 토큰이 없거나 유효하지 않으면 401 이고, 토큰은 유효한데 권한이 부족하면 403 입니다. 이 둘을 뭉개면 클라이언트가 "다시 로그인해야 하는가"와 "권한을 요청해야 하는가"를 구별하지 못합니다. 3번 스텝의 JWKS 캐시도 실무에서 중요합니다. 매 요청 인증 서버에 공개키를 물으면 로컬 검증의 이점이 사라집니다. 하지만 무한정 캐시하면 키 회전 직후 모든 검증이 실패합니다. 캐시하되 kid 를 못 찾으면 한 번 갱신하는 것이 표준적 구현이고, 이 실습에서 그것을 직접 만듭니다.

단계

1. /root/app/main.py 를 127.0.0.1:8160 에 띄운다. 검증 대상은 사전 임포트된 렐름 labhub 이고 기대 audlab-api 이다. 사용자는 dev1(order-reader)과 admin1(order-admin)이며 비밀번호는 /opt/fixtures/kc/realm-info.env 에 있다. GET /public 이 인증 없이 200 을 준다.
2. GET /me 를 토큰 없이 호출하면 401 이고 응답에 WWW-Authenticate 헤더가 있어야 한다.
3. JWKS 를 메모리에 캐시한다. GET /_debug/jwks{"cached":true,"keys":<n>,"fetches":<n>} 를 주고, /me 를 5번 호출한 뒤에도 fetches 가 2 이하여야 한다. 이 스텝부터 dev1 의 액세스 토큰을 /root/app/token.txt 에, admin1 의 것을 /root/app/admin_token.txt 에 한 줄씩 저장해 둔다.
4. 유효한 토큰으로 GET /me 를 호출하면 200 과 {"sub":"<값>","username":"dev1"} 를 준다.
5. 만료된 토큰으로 호출하면 401 이고 본문에 expired 가 들어 있어야 한다. 만료 토큰은 /opt/fixtures/kc/expired.jwt 에 있다.
6. aud 가 다른 토큰으로 호출하면 401 이고 본문에 audience 가 들어 있어야 한다. 그런 토큰은 /opt/fixtures/kc/wrongaud.jwt 에 있다.

5·6번의 실패 케이스 토큰은 파드가 뜰 때 /opt/fixtures/kc/gen-tokens.py 가 만듭니다. 두 파일이 안 보이면 그 스크립트를 직접 한 번 실행하세요 — 픽스처 디렉터리가 읽기 전용이면 /tmp/lab-kc/ 아래에 만들어집니다.
7. GET /adminorder-admin 롤이 있어야 200 이고, 없으면 403 이다. 401 이 아니라 403 이어야 한다.
8. /root/app/e2e.sh 로 다섯 케이스(토큰 없음, 유효, 만료, 잘못된 aud, 권한 부족)를 순서대로 시험해 /root/app/e2e.outno_token=401 valid=200 expired=401 bad_aud=401 forbidden=403 을 적는다.

참고

단계 8개

  1. 앱 띄우고 공개 엔드포인트 확인하기
  2. 토큰 없으면 401 주기
  3. JWKS 캐시 구현하기
  4. 유효 토큰 통과시키기
  5. 만료 토큰 거부하기
  6. 잘못된 대상 토큰 거부하기
  7. 롤 기반 인가 붙이기
  8. 다섯 가지 케이스 한 번에 검증하기