guides
문제 해결
SSO 연동 시 흔한 문제와 해결 방법
문제 해결
흔한 오류
리다이렉트 루프
증상: 로그인 후 /auth/callback -> /auth/login이 반복됩니다.
원인과 해결:
| 원인 | 확인 방법 | 해결 |
|---|---|---|
redirect_uri 불일치 |
Connect 등록 URI와 앱 URI 비교 | Connect에 정확한 콜백 URL 등록 |
| 쿠키 미설정 | 브라우저 DevTools > Application > Cookies | Secure, SameSite 속성 확인 |
| 미들웨어 순서 오류 | auth 미들웨어가 redirect 전에 실행되는지 확인 | sequence() 순서 조정 |
CORS 문제
증상: 브라우저 콘솔에 Access-Control-Allow-Origin 관련 에러가 나타납니다.
해결: SSO 플로우는 서버 사이드 리다이렉트로 동작하므로 CORS 설정이 필요하지 않습니다.
이 에러가 발생한다면 클라이언트에서 fetch()로 직접 Connect 서버를 호출하고 있는지 확인하세요.
토큰 교환은 반드시 서버 사이드에서 이루어져야 합니다.
쿠키 미설정
증상: 로그인 성공 후에도 locals.user가 null입니다.
원인과 해결:
- 도메인 불일치: 쿠키의 도메인 설정이 앱 도메인과 일치하는지 확인합니다.
- Secure 플래그: 프로덕션에서는 HTTPS가 필수입니다. HTTP에서는 Secure 쿠키가 설정되지 않습니다.
- SameSite 속성:
SameSite=Lax가 기본값입니다. cross-origin 리다이렉트 시 정상 동작합니다.
디버그 체크리스트
SSO 연동이 동작하지 않을 때, 다음 5단계를 순서대로 확인하세요.
1단계: Connect 서버 상태 확인
curl -s https://connect.modfolio.io/health | jq .
정상 응답:
{
"status": "ok",
"version": "1.0.0"
}
2단계: OIDC Discovery 확인
curl -s https://connect.modfolio.io/.well-known/openid-configuration | jq .
authorization_endpoint, token_endpoint, jwks_uri가 모두 포함되어야 합니다.
3단계: client_id 확인
앱의 clientId가 Connect에 등록된 값과 정확히 일치하는지 확인합니다.
대소문자를 구분하며, 오타가 있으면 authorization 단계에서 실패합니다.
// 올바른 예시 — 레포 이름과 동일
export const auth = createAstroAuth({
clientId: "your-app-name", // Connect에 등록된 이름
});
4단계: 콜백 URL 확인
로컬 개발과 프로덕션에서 콜백 URL이 다릅니다. Connect에 두 URL 모두 등록되어 있어야 합니다.
- 로컬:
http://localhost:4321/auth/callback - 프로덕션:
https://your-app.com/auth/callback
5단계: 네트워크 요청 추적
브라우저 DevTools > Network 탭에서 다음 순서로 요청이 진행되는지 확인합니다.
1. GET /auth/login → 302 → connect.modfolio.io/sso/authorize
2. connect.modfolio.io 로그인 처리
3. 302 → /auth/callback?code=... → 200 (쿠키 설정)
4. 302 → / (또는 원래 페이지)
3번에서 쿠키가 설정되지 않거나 4번으로 진행되지 않으면 콜백 핸들러를 점검하세요.
로컬 개발 팁
포트 매핑
각 프레임워크의 기본 포트:
| 프레임워크 | 기본 포트 | 명령어 |
|---|---|---|
| Astro | 4321 | bun run dev |
| SvelteKit | 5173 | bun run dev |
| SolidStart | 3000 | bun run dev |
콜백 URL에 포트 번호를 포함해야 합니다.
예: http://localhost:4321/auth/callback
HTTPS 로컬 설정
프로덕션 환경을 정확히 재현하려면 로컬에서도 HTTPS를 사용하세요.
# mkcert로 로컬 인증서 생성
mkcert -install
mkcert localhost
# Astro에서 HTTPS로 실행
bun run dev --host --https --cert localhost.pem --key localhost-key.pem
단, 대부분의 경우 HTTP 로컬 개발로도 충분합니다.
SDK는 로컬 환경에서 Secure 쿠키 플래그를 자동으로 비활성화합니다.
Health Check 엔드포인트
운영 중 Connect 서버 상태를 모니터링할 때 사용합니다.
# 기본 상태 확인
curl -sf https://connect.modfolio.io/health
# JWKS 공개키 확인 (JWT 서명 검증용)
curl -s https://connect.modfolio.io/.well-known/jwks.json | jq '.keys | length'
# OIDC Discovery 엔드포인트 수 확인
curl -s https://connect.modfolio.io/.well-known/openid-configuration | jq 'keys | length'
에러 응답 형식
Connect 서버의 에러 응답은 일관된 JSON 형식을 따릅니다.
interface ConnectErrorResponse {
error: string; // 에러 코드 (OAuth 2.0 표준)
error_description: string; // 사람이 읽을 수 있는 설명
}
주요 에러 코드:
| 코드 | HTTP 상태 | 설명 |
|---|---|---|
invalid_client |
401 | client_id가 등록되지 않음 |
invalid_grant |
400 | authorization code가 만료되었거나 이미 사용됨 |
invalid_request |
400 | 필수 파라미터 누락 |
invalid_scope |
400 | 요청한 scope를 지원하지 않음 |
server_error |
500 | Connect 서버 내부 오류 |
access_denied |
403 | 사용자가 동의를 거부함 |
에러 응답 처리 예시:
const response = await fetch("https://connect.modfolio.io/sso/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code, code_verifier, redirect_uri }),
});
if (!response.ok) {
const error = await response.json() as ConnectErrorResponse;
console.error(`SSO 토큰 교환 실패: [${error.error}] ${error.error_description}`);
}
SDK를 사용하면 위 에러 처리가 자동으로 수행됩니다. 수동으로 API를 호출하는 경우에만 이 형식을 참고하세요.