- Published on
Keycloak OAuth2 로그인 무한 리다이렉트 해결 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버는 정상인데 브라우저만 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-ProtoX-Forwarded-HostX-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이 다음과 같이 미묘하게 달라지면 실패합니다.
httpvshttps- 포트 포함 여부
- 트레일링 슬래시 유무
- 경로 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 조합에서 자주 보는 루프는 다음 시나리오입니다.
- Keycloak이
code를 앱의 콜백으로 전달 - 앱이 code를 토큰으로 교환
- 그런데 앱이 인증 세션을 저장하지 못함(세션 저장소 문제, 쿠키 문제)
- 다음 요청에서 인증이 없다고 판단해 다시
/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가지 체크리스트
- Keycloak과 앱의 외부 URL 기준이 동일한가
- issuer가
https://auth.example.com/realms/{realm}인지 - 앱이 redirect URI를 동일 스킴/호스트로 만드는지
- 프록시 헤더가 정확히 전달되는가
X-Forwarded-Proto누락이 가장 흔합니다.
- 쿠키가 브라우저에 저장되는가
- 저장은 되는데 전송이 안 되면 SameSite/Secure를 의심
- 콜백 경로가 중간에서 리라이트/차단되지 않는가
- Ingress rewrite 규칙, API Gateway 라우팅을 점검
- 로드밸런싱 환경에서 세션이 유지되는가
- 세션 공유 없이 라운드로빈이면 로그인 직후 다른 인스턴스로 가며 루프가 날 수 있습니다.
9) 재현 가능한 최소 구성으로 좁혀보기(권장 절차)
문제가 복잡하게 얽혀 있을 때는 “구성을 최소화”하면 원인이 빨리 드러납니다.
- 프록시를 우회해 Keycloak에 직접 붙여보기(가능하면)
- 앱도 직접 붙여보기
- 이 상태에서 정상이라면 프록시 헤더/리라이트/HTTPS 종료가 원인
- 직접 붙여도 루프라면 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 배포 권한 오류 해결도 함께 참고하면 인증 흐름을 더 입체적으로 이해하는 데 도움이 됩니다.