Published on

Keycloak OAuth2 로그인 무한 리다이렉트 해결 가이드

Authors

서버는 정상인데 브라우저만 302를 반복하며 로그인 화면으로 되돌아가거나, code를 받고도 다시 인증 요청으로 튕기는 “Keycloak OAuth2 로그인 무한 리다이렉트”는 현장에서 가장 시간을 잡아먹는 장애 중 하나입니다. 특히 리버스 프록시(Nginx, ALB, Ingress) 뒤에서 HTTPS를 종료하거나, 프런트엔드와 백엔드 도메인이 분리된 구조, 또는 Spring Security 같은 클라이언트 라이브러리의 기본 설정이 섞이면 원인이 겹쳐 보입니다.

이 글은 증상을 “어디서 루프가 도는지”로 먼저 분류하고, 그에 맞는 체크리스트와 설정 예시를 제공합니다. 목표는 딱 2가지를 확인하는 것입니다.

  • Keycloak이 생각하는 외부 URL(issuer, redirect)과 실제 브라우저 URL이 일치하는가
  • 세션/쿠키가 브라우저에 정상 저장되고 다음 요청에 다시 실려 가는가

1) 무한 리다이렉트의 전형적인 패턴 3가지

패턴 A: 로그인 성공 직후 다시 로그인 페이지로

  • Keycloak에서 ID/PW 입력 후 “로그인 성공”처럼 보이지만 곧바로 다시 Keycloak 로그인 페이지로 이동
  • 혹은 애플리케이션으로 돌아왔다가 즉시 다시 /oauth2/authorization/... 같은 엔드포인트로 이동

대부분 “세션 쿠키가 저장되지 않거나, 저장되었어도 다음 요청에 전송되지 않는” 문제입니다.

패턴 B: redirect_uri 불일치로 계속 인증 재시도

  • 브라우저 주소창에 error=invalid_redirect_uri가 보이거나
  • 애플리케이션 로그에 “redirect_uri mismatch” 류 메시지가 있고
  • 클라이언트가 다시 인증을 시도하면서 루프

원인은 Keycloak 클라이언트의 Valid Redirect URIs, Web Origins, 그리고 앱의 redirect URI 생성 로직 불일치입니다.

패턴 C: 프록시 뒤에서 HTTP/HTTPS 스킴이 꼬여 루프

  • 외부는 HTTPS인데 내부 통신은 HTTP
  • Keycloak이 issuer를 HTTP로 생성하거나, 앱이 redirect URI를 HTTP로 만들면서
  • 브라우저가 혼합된 스킴으로 계속 리다이렉트

이 경우 X-Forwarded-Proto, X-Forwarded-Host 처리가 핵심입니다.

2) 1분 진단: “루프가 Keycloak에서 도나, 앱에서 도나”

먼저 네트워크 탭에서 302 체인을 보고 어느 쪽이 반복되는지 확인합니다.

  • Keycloak 도메인에서 302가 반복되면: Keycloak 세션/쿠키, 프록시 헤더, hostname 설정을 의심
  • 애플리케이션 도메인에서 /login/oauth2/code/... 처리 후 다시 /oauth2/authorization/...로 가면: 앱의 세션 저장, Spring Security 설정, SameSite 쿠키, 콜백 처리 실패를 의심

추가로 Keycloak 이벤트 로그를 켜면 빠릅니다.

  • Realm Settings의 Events에서 Save Events 활성화
  • Admin Events도 필요 시 활성화

이때 “로그인 성공 이벤트는 찍히는데 클라이언트로 세션이 유지되지 않는다”면 쿠키/프록시 문제 가능성이 큽니다.

3) 가장 흔한 원인: 쿠키 SameSite/Secure 문제

최근 브라우저는 기본 쿠키 정책이 엄격합니다. 특히 다음 조합에서 루프가 자주 발생합니다.

  • Keycloak과 애플리케이션이 서로 다른 도메인/서브도메인
  • 프런트엔드에서 팝업/리다이렉트로 인증
  • HTTPS 환경인데 쿠키가 Secure가 아니거나
  • SameSite=None이 필요한데 Lax로 설정됨

해결 방향

  • 외부가 HTTPS라면 쿠키는 대체로 Secure가 필요합니다.
  • 크로스 사이트 리다이렉트/임베드가 걸리면 SameSite=None이 필요할 수 있습니다.

Keycloak은 버전/배포 방식에 따라 쿠키 정책이 다르게 적용될 수 있으니, “브라우저 개발자도구 Application 탭에서 쿠키가 생성되는지”를 먼저 확인하세요.

(참고) Spring Boot에서 세션 쿠키 설정 예시

아래는 애플리케이션 세션이 유지되지 않아 루프가 도는 케이스에서 도움이 되는 설정 예시입니다.

server:
  servlet:
    session:
      cookie:
        secure: true
        same-site: none

주의할 점은 same-site: none은 반드시 secure: true와 함께여야 최신 브라우저에서 쿠키가 차단되지 않는다는 것입니다.

4) 프록시/Ingress 뒤에서 발생하는 스킴 꼬임(HTTP/HTTPS) 문제

Keycloak과 앱이 모두 프록시 뒤에 있을 때 다음이 흔합니다.

  • 외부 요청: https://auth.example.com
  • 내부 Keycloak: http://keycloak:8080
  • 프록시가 X-Forwarded-Proto: https를 제대로 전달하지 않음
  • Keycloak이 자기 자신을 http://...로 인식하고 redirect/issuer를 HTTP로 생성

Keycloak(Quarkus 배포)에서 프록시 인식 설정

Keycloak 17+ (Quarkus 기반)에서는 보통 아래 설정이 필요합니다.

# 예시: 컨테이너 환경 변수
KC_PROXY=edge
KC_HOSTNAME=auth.example.com
KC_HTTP_ENABLED=true

환경에 따라 KC_PROXY 값은 edge 또는 reencrypt 등을 사용합니다. 핵심은 “외부에서 들어온 스킴/호스트를 신뢰하고, 그 기준으로 redirect를 생성”하도록 만드는 것입니다.

또한 프록시에서 아래 헤더가 정확히 전달되는지 확인하세요.

  • X-Forwarded-Proto
  • X-Forwarded-Host
  • X-Forwarded-Port

Nginx 리버스 프록시 헤더 예시

location / {
  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-Host $host;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-Forwarded-Port $server_port;
  proxy_pass http://keycloak:8080;
}

이 중 하나라도 누락되면 Keycloak이 redirect URI를 다르게 만들고, 클라이언트가 이를 받아 다시 인증을 시도하면서 루프가 됩니다.

5) redirect_uri 불일치: Keycloak 클라이언트 설정이 앱과 안 맞는 경우

OIDC에서 Keycloak은 “등록된 redirect URI만” 허용합니다. 앱이 생성하는 콜백 URL이 다음과 같이 미묘하게 달라지면 실패합니다.

  • http vs https
  • 포트 포함 여부
  • 트레일링 슬래시 유무
  • 경로 prefix(/api 같은) 포함 여부

Keycloak 클라이언트에서 확인할 항목

  • Valid Redirect URIs
  • Web Origins
  • Root URL, Home URL(사용 중인 경우)

예를 들어 Spring Security 기본 콜백은 보통 다음 형태입니다.

  • /login/oauth2/code/{registrationId}

따라서 Keycloak에는 아래처럼 등록합니다(예시).

  • https://app.example.com/login/oauth2/code/*

와일드카드는 편하지만 과도하게 열면 보안 리스크가 있으니 운영에서는 가능한 좁히는 것을 권장합니다.

6) Spring Security에서 “로그인 성공 후 다시 인증” 루프가 도는 이유

Spring Boot + Spring Security OAuth2 Client 조합에서 자주 보는 루프는 다음 시나리오입니다.

  1. Keycloak이 code를 앱의 콜백으로 전달
  2. 앱이 code를 토큰으로 교환
  3. 그런데 앱이 인증 세션을 저장하지 못함(세션 저장소 문제, 쿠키 문제)
  4. 다음 요청에서 인증이 없다고 판단해 다시 /oauth2/authorization/...로 이동

체크 포인트

  • 앱이 상태 저장 세션을 쓰는지(기본 JSESSIONID)
  • 로드밸런서 뒤라면 스티키 세션 또는 세션 공유가 필요한지
  • 콜백 처리 엔드포인트가 프록시에서 차단/리라이트되지 않는지

특히 Kubernetes Ingress에서 경로 리라이트를 쓸 때 /login/oauth2/code/...가 다른 경로로 바뀌면 콜백이 깨지고 루프가 날 수 있습니다.

7) 로그로 확정 진단하기: Keycloak과 앱에서 봐야 할 로그

Keycloak에서 확인

  • 이벤트 로그에서 LOGIN 성공 여부
  • invalid_redirect_uri 또는 클라이언트 설정 관련 오류
  • 프록시/hostname 관련 경고

애플리케이션에서 확인

  • Spring Security 디버그 로깅을 잠깐 켜서 “인증 객체가 세션에 저장되는지” 확인
logging:
  level:
    org.springframework.security: DEBUG

DEBUG를 켠 뒤, 콜백 요청에서 Authentication이 생성된 다음 요청에서도 유지되는지(세션에서 로드되는지)를 보면 쿠키/세션 문제를 빠르게 특정할 수 있습니다.

8) 운영에서 많이 놓치는 5가지 체크리스트

  1. Keycloak과 앱의 외부 URL 기준이 동일한가
  • issuer가 https://auth.example.com/realms/{realm}인지
  • 앱이 redirect URI를 동일 스킴/호스트로 만드는지
  1. 프록시 헤더가 정확히 전달되는가
  • X-Forwarded-Proto 누락이 가장 흔합니다.
  1. 쿠키가 브라우저에 저장되는가
  • 저장은 되는데 전송이 안 되면 SameSite/Secure를 의심
  1. 콜백 경로가 중간에서 리라이트/차단되지 않는가
  • Ingress rewrite 규칙, API Gateway 라우팅을 점검
  1. 로드밸런싱 환경에서 세션이 유지되는가
  • 세션 공유 없이 라운드로빈이면 로그인 직후 다른 인스턴스로 가며 루프가 날 수 있습니다.

9) 재현 가능한 최소 구성으로 좁혀보기(권장 절차)

문제가 복잡하게 얽혀 있을 때는 “구성을 최소화”하면 원인이 빨리 드러납니다.

  1. 프록시를 우회해 Keycloak에 직접 붙여보기(가능하면)
  2. 앱도 직접 붙여보기
  3. 이 상태에서 정상이라면 프록시 헤더/리라이트/HTTPS 종료가 원인
  4. 직접 붙여도 루프라면 redirect URI 또는 쿠키/세션 정책이 원인

Cloud Run이나 서버리스 환경처럼 프록시 계층이 숨겨진 경우도 있어, 네트워크/콜드스타트 이슈가 인증 플로우에 간접 영향을 주기도 합니다. 관련해서는 GCP Cloud Run 503·콜드스타트 지연 해결법처럼 “요청이 중간에서 끊기거나 지연되는 상황”도 함께 점검하면 좋습니다.

10) 결론: 무한 리다이렉트는 결국 URL 정합성과 쿠키/세션 문제다

Keycloak OAuth2/OIDC 로그인 루프는 원인이 다양해 보이지만, 실제로는 아래 두 축으로 거의 수렴합니다.

  • Keycloak이 생성하는 redirect/issuer의 “외부 기준 URL”이 실제와 일치하지 않음(프록시 헤더, hostname, HTTPS 종료)
  • 쿠키/세션이 저장 또는 전송되지 않음(SameSite, Secure, 도메인 분리, 로드밸런싱)

위 체크리스트대로 “루프 위치 확인 → 쿠키 저장/전송 확인 → 프록시 헤더/redirect URI 확인” 순서로 접근하면, 현장에서 대부분 30분 내에 원인을 특정할 수 있습니다.

추가로 배포 파이프라인에서 OIDC를 쓰는 경우(예: GitHub Actions OIDC)도 ‘issuer/redirect/신뢰 경로’가 핵심이라는 점에서 문제 구조가 유사합니다. 맥락이 맞는다면 GitHub Actions OIDC로 AWS 배포 권한 오류 해결도 함께 참고하면 인증 흐름을 더 입체적으로 이해하는 데 도움이 됩니다.