- Published on
Keycloak OIDC 로그인 무한 리다이렉트 해결 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버는 정상인데 브라우저만 로그인 -> 콜백 -> 다시 로그인을 반복하는 OIDC 무한 리다이렉트는, 실제로는 “인증이 성공했지만 애플리케이션이 세션을 유지하지 못한다”는 신호인 경우가 많습니다. 특히 Keycloak을 리버스 프록시 뒤에 두거나, HTTPS 종단(terminate)을 프록시에서 처리하거나, SPA와 API를 다른 도메인으로 분리해 운영할 때 자주 터집니다.
이 글에서는 Keycloak OIDC에서 무한 리다이렉트가 발생하는 대표 원인을 증상별로 분류하고, 어디를 먼저 확인해야 하는지와 설정을 어떻게 바꿔야 하는지를 코드/설정 예제로 정리합니다.
관련해서 프록시, 캐시, 런타임 동작이 엮여 디버깅이 어려울 때는 아래 글도 같이 보면 도움이 됩니다.
무한 리다이렉트의 전형적인 흐름
대부분의 OIDC 로그인 흐름은 다음과 같습니다.
- 앱이 인증이 필요하다고 판단해 Keycloak의 authorization endpoint로 리다이렉트
- 사용자가 로그인
- Keycloak이
redirect_uri로 authorization code를 붙여 콜백 - 앱이 code를 token endpoint로 교환
- 앱이 세션(쿠키, 서버 세션, 토큰 저장 등)을 저장
- 앱이 원래 페이지로 리다이렉트
무한 리다이렉트는 보통 5번에서 실패합니다.
- 쿠키가 저장되지 않는다
- 저장되었지만 다음 요청에 포함되지 않는다
- 포함되었지만 서버가 다른 인스턴스라 세션을 못 찾는다
- 프록시 때문에
http로 인식되어Secure쿠키를 안 보내거나, 반대로Secure가 필요한데 빠진다 redirect_uri가 미세하게 달라 검증에 실패한다
1) Keycloak Client 설정: Redirect URI 미스매치
증상
- 로그인 성공 후 콜백으로 오지만 앱이 다시 Keycloak로 보냄
- 서버 로그에
invalid_redirect_uri또는Invalid parameter: redirect_uri유사 메시지
체크 포인트
Keycloak의 Client 설정에서 다음을 확인합니다.
Valid Redirect URIsValid Post Logout Redirect URIsWeb Origins
특히 로컬/스테이징/프로덕션이 섞이거나, 프록시가 경로를 붙이는 경우 redirect_uri가 예상과 다르게 만들어집니다.
권장 설정 예시
예를 들어 앱 콜백이 https://app.example.com/auth/callback이라면:
Valid Redirect URIs:https://app.example.com/auth/callback또는https://app.example.com/*Web Origins:https://app.example.com
와일드카드 *는 편하지만 범위를 과도하게 넓히면 보안적으로 불리합니다. 운영에서는 가능한 구체적으로 잡는 것이 안전합니다.
2) 프록시/로드밸런서 뒤에서 scheme/host 인식 문제
Keycloak이나 애플리케이션이 “자기가 http로 서비스되고 있다”고 착각하면, OIDC 리다이렉트 URL을 http로 만들거나 쿠키 정책이 꼬여서 무한 루프가 납니다.
증상
- 브라우저 주소창이
https인데 서버 로그에는http로 인식 Location헤더가http://...로 내려오며 다시https로 리다이렉트되는 반복- Keycloak이
redirect_uri를http로 만들어 검증 실패
해결: X-Forwarded-* 헤더와 Keycloak proxy 설정
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-For $proxy_add_x_forwarded_for;
proxy_pass http://keycloak:8080;
}
Keycloak 설정(컨테이너 환경 예시)
Keycloak 버전에 따라 옵션이 다를 수 있지만, 핵심은 “프록시 뒤에 있다”를 명시하는 것입니다.
# 예: Quarkus 기반 Keycloak에서 자주 쓰는 형태
KC_PROXY=edge
KC_HOSTNAME=auth.example.com
KC_HTTP_ENABLED=true
환경에 따라 KC_PROXY_HEADERS=xforwarded 같은 옵션을 쓰기도 합니다. 중요한 건 Keycloak이 X-Forwarded-Proto를 신뢰하고 외부 URL을 올바르게 구성하도록 만드는 것입니다.
3) SameSite / Secure 쿠키 때문에 세션이 유지되지 않는 문제
최근 브라우저 정책 때문에 OIDC에서 쿠키가 막히는 케이스가 매우 흔합니다.
증상
- 로그인 직후 콜백까지는 오는데, 앱이 “로그인 안 됨”으로 판단
- 개발자도구 Application 탭에서 세션 쿠키가 안 보이거나,
Set-Cookie는 내려오는데 다음 요청에 쿠키가 실리지 않음
원인 패턴
- HTTPS인데 쿠키에
Secure가 빠져서 차단 - 크로스 사이트 흐름에서
SameSite=Lax또는SameSite=Strict라서 콜백 요청에 쿠키가 안 붙음 - 반대로
SameSite=None인데Secure가 없어서 차단
해결 방향
- OIDC 콜백이 “크로스 사이트”로 분류되는 구조라면 보통
SameSite=None; Secure가 필요합니다. - 단,
SameSite=None는 반드시Secure와 함께여야 합니다.
Node.js(Express) 세션 쿠키 예시
import session from 'express-session';
app.set('trust proxy', 1); // 프록시 뒤라면 필수인 경우가 많음
app.use(session({
name: 'sid',
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: true,
sameSite: 'none',
// domain: '.example.com', // 필요할 때만
}
}));
여기서 trust proxy를 켜지 않으면, Express가 요청을 http로 인식해 secure: true 쿠키를 세팅하지 못하는 경우가 있습니다. 그러면 로그인 후 세션 쿠키가 생성되지 않아 무한 리다이렉트가 발생합니다.
4) 클러스터 환경에서 세션 스티키(Sticky) 또는 세션 공유 문제
증상
- 단일 인스턴스에서는 정상인데, 로드밸런서 뒤에서만 무한 리다이렉트
- 로그인 직후 다음 요청이 다른 인스턴스로 가면서 세션을 못 찾음
해결 전략
- 로드밸런서에 Sticky session 적용
- 세션 저장소를 Redis 등으로 외부화
- 또는 아예 서버 세션 대신 토큰 기반으로 설계(단, 보안과 만료/갱신 정책을 잘 설계해야 함)
예: Nginx 업스트림에서 ip-hash(간단한 스티키)
upstream app_upstream {
ip_hash;
server app1:3000;
server app2:3000;
}
server {
location / {
proxy_pass http://app_upstream;
}
}
운영에서는 ip_hash가 만능은 아니고, L7 LB의 쿠키 기반 스티키가 더 예측 가능할 때가 많습니다.
5) OIDC state/nonce 검증 실패(시간, 저장소, 멀티탭)
대부분의 OIDC 라이브러리는 CSRF 방지를 위해 state, nonce를 저장해두고 콜백에서 검증합니다. 이 저장이 깨지면 콜백을 “위조된 요청”으로 보고 다시 로그인으로 보내 무한 루프가 됩니다.
증상
- 서버 로그에
state mismatch,invalid state,nonce mismatch유사 메시지 - 모바일 사파리, 인앱 브라우저에서 재현이 잘 됨
체크 포인트
state를 쿠키에 저장하는 라이브러리라면 SameSite/Secure 문제와 연결됩니다.- 서버 세션에 저장한다면 4번의 세션 공유 문제가 원인일 수 있습니다.
- 서버 시간 드리프트가 큰 경우 토큰 검증이 실패할 수 있습니다.
빠른 점검
- 앱 서버와 Keycloak 서버의 시간이 NTP로 동기화되어 있는지 확인
- 콜백 처리 직전에 세션 저장소에서
state가 실제로 존재하는지 로깅
6) Keycloak 자체 쿠키/도메인 설정 이슈
Keycloak도 브라우저 쿠키를 사용합니다. 특히 auth.example.com 같은 서브도메인으로 Keycloak을 띄우고, 앱은 app.example.com인 구조에서 쿠키 정책이 꼬일 수 있습니다.
증상
- Keycloak 로그인 화면에서 로그인은 되는데, 다시 로그인 화면으로 돌아옴
- Keycloak 콘솔에서 이벤트 로그에 로그인 성공이 찍히는데 브라우저는 계속 재로그인
점검
- Keycloak 외부 URL(Hostname) 설정이 실제 접근 도메인과 일치하는지
- 프록시 헤더가 전달되는지
- 브라우저에서 Keycloak 도메인의 쿠키가 생성되는지
Keycloak의 이벤트 로그(Realm 설정에서 Events 활성화)를 켜고, 로그인 성공 후 어떤 단계에서 다시 authorization이 시작되는지 추적하면 원인을 빨리 좁힐 수 있습니다.
7) 실전 디버깅 루틴(10분 내 원인 좁히기)
무한 리다이렉트는 “추측”보다 “관측”이 빠릅니다. 아래 순서대로 보면 보통 10분 안에 범인이 나옵니다.
1) 브라우저 네트워크 탭에서 리다이렉트 체인 확인
- 어떤 URL들이 반복되는지
Location이http로 내려오는지- 콜백 요청에 쿠키가 포함되는지(Request Headers의
Cookie)
2) 콜백 응답의 Set-Cookie 확인
Set-Cookie가 내려오는가SameSite=None인데Secure가 있는가Domain이 의도한 범위인가
3) 서버 로그에 state/nonce/redirect_uri 관련 에러가 있는지 확인
redirect_urimismatch면 Keycloak Client 설정부터state mismatch면 세션/쿠키/스티키부터
4) 프록시 헤더 확인
- 앱 서버가
X-Forwarded-Proto를 받는지 - 앱 프레임워크에서 “프록시 신뢰” 설정이 필요한지
8) Next.js에서 자주 하는 실수(콜백 라우트, 쿠키, 프록시)
Next.js(특히 App Router)에서 OIDC를 붙일 때는 콜백 라우트를 Route Handler로 만들거나, 별도 BFF 서버를 두는 방식이 흔합니다.
- 콜백 라우트에서 쿠키를 세팅했는데,
secure조건이 맞지 않아 쿠키가 누락 - 배포 환경에서만 프록시 뒤라
x-forwarded-proto인식이 달라짐 - 서버 컴포넌트 캐시/프리렌더링이 인증 흐름과 충돌
인증 후 화면이 갱신되지 않거나, 특정 페이지가 “로그인 전 상태”로 남는 문제는 캐시와도 엮일 수 있습니다. 이런 케이스는 Next.js 14 RSC 캐시로 데이터가 안 갱신될 때도 함께 참고하면 좋습니다.
9) 체크리스트: 가장 많이 맞는 조합
현장에서 가장 자주 맞는 조합은 아래 3가지입니다.
- 앱이 프록시 뒤인데
trust proxy미설정 +secure쿠키 사용 SameSite=None없이 콜백을 처리(혹은None인데Secure누락)- 멀티 인스턴스에서 세션 공유/스티키 없음
이 3개를 먼저 의심하고, 브라우저의 Set-Cookie와 다음 요청의 Cookie 포함 여부만 확인해도 절반 이상은 해결됩니다.
10) 결론: “로그인은 성공했는데 세션이 없다”를 증명하라
Keycloak OIDC 무한 리다이렉트는 Keycloak이 고장 났다기보다, 대개 앱이 로그인 결과를 유지하지 못하는 구조적 문제입니다.
redirect_uri가 정확한가- 프록시 헤더로 외부 스킴/호스트를 올바르게 인식하는가
- 쿠키의
SameSite와Secure가 브라우저 정책에 맞는가 - 클러스터에서 세션이 유지되는가
state/nonce저장과 검증이 안정적인가
위 항목을 순서대로 관측하면, “왜 다시 로그인으로 가는지”가 로그와 네트워크에서 명확히 드러납니다. 그 지점만 고치면 무한 리다이렉트는 대부분 깔끔하게 끝납니다.