Files
document-haness/docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/reference/reference-authorization-code-endpoints.md
T
DongHyeonkaandClaude Fable 5.1 4d50bb939a docs(keycloak): adopt the decomposition contract, fix the redirect URI, strip evaluative prose
- 계약 채택 — 독자 질문, 후보 29건(PROMOTE 24 · MERGE_INTO 4 · KEEP_IN_SSOT 1). 게시 중
  17건은 전부 유지. 저장소 keycloak-pattern 은 패턴 넷이 브랜치로 갈라져 있어 revisions 로
  tip 넷을 적었다. keycloak-session-store 는 같은 저장소 @ cdac9b8
- 게시된 기록의 redirect_uri 가 SSOT·코드와 달랐다 — OAuth2callback.html → callback.html
  (frontend/src/app.js 에서 확인). 계약 title 이 기록과 다른 7건도 기록 쪽으로 맞췄다
- 미작성 1건 작성 — 패턴 검증을 실제로 돌릴 때의 안전한 순서(Reference)
- 리뷰 100건 반영 — 설명 뒤에 붙은 평가·차례 예고·독자 오해 가정·작성 지시를 지웠다.
  삭제가 남긴 조각 4건을 고치고, 원래부터 잘려 있던 로컬 미리보기 라벨 1건도 닫았다

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 12:39:20 +09:00

7.8 KiB

id, kind, slug, title, topic, topicName, project, status, version, verifiedOn, studio, public, sourceRevision, source
id kind slug title topic topicName project status version verifiedOn studio public sourceRevision source
39fdf472-82c4-43ed-abec-73de672f08ae REFERENCE authorization-code-endpoint-credential-movement Authorization Code Flow의 Endpoint와 Credential 이동 기준 oauth-oidc-auth-boundary OAuth/OIDC 인증 경계 KeyCloak Patterns 게시 중 34 2026-08-25 https://hyeonworks.com/studio/documents/39fdf472-82c4-43ed-abec-73de672f08ae/edit https://hyeonworks.com/references/authorization-code-endpoint-credential-movement keycloak-patterns-lab@2026-08
final/document.md#문제를-어렵게-만든-제약-로그인-흐름과-api-흐름
final/document.md#선택이-코드와-흐름에-반영되는-방식-추적-규칙

Authorization Code Flow의 Endpoint와 Credential 이동 기준

Authorization Endpoint에서 리다이렉트, Token Endpoint, JWK(JSON Web Key) 검증, Resource API까지 지점마다 무엇이 이동하고 무엇이 이동하지 않는지 확인한다. 먼저 client_secret이 가는 곳과 가지 않는 곳을 나눈다.

관계

  • SPA에서 토큰을 직접 관리하면서 드러난 Browser Credential 경계 브라우저가 code를 직접 교환하는 흐름에서 엔드포인트마다 무엇이 오가는지 확인했다.
  • Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출 confidential client가 Token Endpoint를 부를 때 자기 자신을 인증한다.
  • Public Client와 Confidential Client 구분 기준 클라이언트 종류가 정해져야 PKCE와 클라이언트 인증을 어디에 걸지 정해진다.

목적

Authorization Endpoint와 Token Endpoint는 하는 일도 다르고 요청이 나가는 방식도 다르다.

하나는 브라우저가 페이지째 넘어가는 full-page navigation이고, 다른 하나는 서버가 보낼 수도 있고 브라우저가 직접 보낼 수도 있는 호출이다.

Authorization Endpoint 요청은 브라우저 주소창을 지나기 때문에 URL이 히스토리와 서버 로그, referrer에 남고, 이 요청에서는 클라이언트를 인증하지 않는다. Token Endpoint 요청은 값을 요청 본문과 Authorization 헤더에 싣고, 보내는 쪽은 클라이언트 종류에 따라 서버이거나 브라우저이며, 클라이언트 인증을 여기서 한다.

규칙

1. Authorization Endpoint에는 client_secret을 보내지 않는다

이 요청은 브라우저 주소창을 통해 나가기 때문에 URL이 주소창과 브라우저 히스토리, 서버 접근 로그에 남고, 링크를 타고 온 경우에는 referrer에도 남는다. 여기 실리는 값은 client_id, redirect_uri, response_type, scope, state, code_challenge, code_challenge_method다. 클라이언트 시크릿이 필요한 인증은 아직 하지 않는다.

이 목록에 없는 값을 여기에 실으면 그 값도 같은 곳에 함께 남는다.

2. Token Endpoint에서 비로소 클라이언트를 인증한다

code를 액세스 토큰으로 바꾸는 요청은 자격 증명을 URL 쿼리 문자열이 아니라 요청 본문과 Authorization 헤더에 싣기 때문에, 클라이언트 인증도 이 요청에서 한다. confidential client는 client_secret_basic처럼 클라이언트 시크릿을 함께 보낸다.

주소창과 히스토리에 남지 않는다는 뜻이지 어디에도 기록되지 않는다는 뜻은 아니다. 애플리케이션 디버그 로그, 리버스 프록시 로그, 추적 도구와 APM(성능 모니터링 도구), 패킷 캡처에 남을 수 있어서 자격 증명을 가리는 마스킹을 따로 둔다.

이 요청을 누가 보내는지는 클라이언트 종류에 따라 갈린다. 서버가 보내면 서버끼리 주고받는 호출이고, 클라이언트 시크릿이 없는 SPA가 보내면 브라우저가 직접 보낸다. Token Endpoint를 서버 안에서만 부르게 하려면 클라이언트 종류부터 confidential client로 정해야 한다.

3. PKCE는 두 요청을 같은 주체에 묶는다

로그인을 시작하는 요청에 code_challenge를 담아 보내고, code를 토큰으로 바꿀 때 원본인 code_verifier를 보낸다. Authorization Server가 이 둘이 대응하는지 확인하고, 대응할 때만 토큰 교환이 끝난다.

그래서 code를 누가 훔쳐 가도 code_verifier가 없으면 토큰으로 바꾸지 못한다.

4. issuer 검증값과 JWK 조회 주소를 같은 값으로 맞추려 하지 않는다

issuer는 요청을 보내는 주소가 아니라 토큰을 발급한 주체를 가리키는 정해진 식별자다. 검증은 발급된 토큰의 iss 클레임이 그 값과 같은지를 본다.

JWK 조회 주소는 공개키를 실제로 가져오는 네트워크 경로다. 이 예제에서는 브라우저가 보는 주소와 컨테이너 안에서 닿는 주소가 다르다. 컨테이너 안에서는 자기 localhost가 그 서버가 아니어서 서비스 이름을 써야 하는데, 브라우저는 그 이름에 닿지 못한다.

issuer 검증값과 엔드포인트에 연결하는 주소는 따로 구성한다. 둘을 하나로 맞추려 하면 로그인 리다이렉트가 깨지거나 서버가 공개키를 가져오지 못한다.

5. Resource API는 서명만 보고 끝내지 않는다

서명이 맞다는 것은 그 IdP가 발급했다는 뜻일 뿐이다. 같은 IdP가 다른 API용으로 발급한 토큰도 서명은 맞다.

그래서 issuer와 유효 시간, 그리고 이 API를 위해 발급됐다는 audience를 함께 본다. audience 검증이 빠지면 옆 서비스에 발급된 토큰으로도 우리 API가 열린다.

6. redirect_uri는 정확히 일치하는 주소로 좁힌다

리다이렉트 허용 목록에 와일드카드를 두면 학습 환경에서는 편하다. 다만 허용 범위가 넓으면 같은 호스트의 다른 경로로도 code가 갈 수 있다.

실제로 쓰는 콜백 주소만 등록해 두면 code가 도착할 수 있는 곳이 그 하나로 줄어든다.

등록하지 않은 redirect_uri를 보냈을 때 거부하는지 확인하는 검사도 따로 둔다.

7. 로그인 구간과 API 호출 구간은 따로 따라간다

로그인 구간은 authorization request에서 시작해 콜백과 code 교환을 지나 로그인 상태를 만드는 데까지다. API 호출 구간은 브라우저 입력, 중간 계층의 자격 증명 변환, 보호 자원의 검증, 최종 응답이다.

로그인 버튼을 누른 뒤 JSON이 돌아올 때까지 code와 토큰, 쿠키가 차례로 바뀐다.

두 구간을 한 줄로 이어 그리면 누가 code를 바꾸고 누가 API를 부르는지가 겹쳐 보인다. 네 가지 구조는 여기서 갈리므로 나눠서 그린다.

적용 조건

  • Authorization Code Flow를 쓰는 클라이언트를 설정하거나 문서로 설명할 때
  • 브라우저 요청과 서버끼리 주고받는 요청이 한 흐름에 섞여 있을 때
  • 엔드포인트마다 무엇이 드러나는지 나눠야 할 때
  • PKCE와 클라이언트 인증을 어디에 걸지 정할 때

예외

  • Client Credentials처럼 사용자 없이 토큰을 받는 흐름은 Authorization Endpoint를 지나지 않는다.
  • Device Authorization Grant는 브라우저 리다이렉트 대신 별도의 사용자 code 단계를 쓰기 때문에 redirect_uri 항목이 그대로 적용되지 않는다.

예시

  • authorization request에는 code_challenge_method=S256이 있고 클라이언트 시크릿은 없다
  • token request에는 code_verifier가 있다. 시크릿을 가진 클라이언트는 이 요청에서 자기를 인증한다
  • issuer 검증값은 http://localhost:8080/realms/keycloak-patterns 이고 JWK 조회는 컨테이너 네트워크 주소를 쓴다
  • audience에 keycloak-pattern-api 가 없으면 invalid_token 결과가 되어 401이 된다
  • 리다이렉트 허용 목록에 와일드카드가 있으면 등록한 호스트의 다른 경로로도 code가 갈 수 있다