Refactor: 인증 흐름 개선 및 에러 메시지 통합
.vscode/settings.json - chatgpt 확장 자동 실행 비활성화 설정 추가 app/auth/callback/route.ts - OAuth 콜백 처리 개선: 에러 메시지 매핑 함수 사용 및 리다이렉트 로직 정리 app/auth/confirm/route.ts - 이메일 인증(토큰 검증) 라우트 신규 구현: recovery 쿠키 설정 및 안전한 리다이렉트 처리 app/forgot-password/page.tsx - UI 텍스트/플레이스홀더 정리, 메시지 렌더링 조건부 처리 app/reset-password/page.tsx - 리셋 페이지 접근/세션 검증 로직 정리 및 UI 문구/아이콘 변경 features/auth/actions.ts - 에러 처리 통합(getAuthErrorMessage 사용), 서버 액션 주석 정리 - 비밀번호 재설정 플로우 반환값을 객체로 변경하고 recovery 쿠키 삭제/로그아웃 처리 features/auth/components/reset-password-form.tsx - 클라이언트 폼: updatePassword 결과 처리 개선, 라우터 리다이렉션 및 에러 메시지 표시 개선 features/auth/constants.ts - 인증 관련 상수(에러 메시지, 코드/상태 매핑, 라우트, 검증 규칙, 쿠키 이름) 신규 추가 features/auth/errors.ts - Supabase Auth 에러를 한글 메시지로 변환하는 유틸 추가 features/auth/schemas/auth-schema.ts - zod 스키마 메시지 문구 정리 및 포맷 통일 utils/supabase/middleware.ts - 세션/쿠키 갱신 및 라우트 보호 로직 개선, recovery 쿠키 기반 리다이렉트 처리 추가 utils/supabase/server.ts - 서버용 Supabase 클라이언트 초기화 함수 추가 (쿠키 읽기/쓰기 처리)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* [인증 관련 상수 정의]
|
||||
*
|
||||
* 인증 모듈 전체에서 공통으로 사용하는 상수들을 정의합니다.
|
||||
@@ -61,6 +61,107 @@ export const AUTH_ERROR_MESSAGES = {
|
||||
DEFAULT: "요청을 처리하는 중 오류가 발생했습니다.",
|
||||
} as const;
|
||||
|
||||
// ========================================
|
||||
// Supabase Auth 에러 코드 매핑
|
||||
// ========================================
|
||||
|
||||
export const AUTH_ERROR_CODE_MESSAGES = {
|
||||
anonymous_provider_disabled: "익명 로그인은 비활성화되어 있습니다.",
|
||||
bad_code_verifier: "PKCE code verifier가 일치하지 않습니다.",
|
||||
bad_json: "요청 본문이 올바른 JSON이 아닙니다.",
|
||||
bad_jwt: "Authorization 헤더의 JWT가 유효하지 않습니다.",
|
||||
bad_oauth_callback: "OAuth 콜백에 필요한 값(state)이 없습니다.",
|
||||
bad_oauth_state: "OAuth state 형식이 올바르지 않습니다.",
|
||||
captcha_failed: "CAPTCHA 검증에 실패했습니다.",
|
||||
conflict: "요청 충돌이 발생했습니다. 잠시 후 다시 시도해주세요.",
|
||||
email_address_invalid: "예시/테스트 도메인은 사용할 수 없습니다.",
|
||||
email_address_not_authorized:
|
||||
"기본 SMTP 사용 시 허용되지 않은 이메일 주소입니다.",
|
||||
email_conflict_identity_not_deletable:
|
||||
"이메일 충돌로 이 아이덴티티를 삭제할 수 없습니다.",
|
||||
email_exists: "이미 가입된 이메일 주소입니다.",
|
||||
email_not_confirmed: "이메일 인증이 완료되지 않았습니다.",
|
||||
email_provider_disabled: "이메일/비밀번호 가입이 비활성화되어 있습니다.",
|
||||
flow_state_expired: "로그인 흐름이 만료되었습니다. 다시 시도해주세요.",
|
||||
flow_state_not_found: "로그인 흐름을 찾을 수 없습니다. 다시 시도해주세요.",
|
||||
hook_payload_invalid_content_type:
|
||||
"훅 페이로드의 Content-Type이 올바르지 않습니다.",
|
||||
hook_payload_over_size_limit: "훅 페이로드가 최대 크기를 초과했습니다.",
|
||||
hook_timeout: "훅 요청 시간이 초과되었습니다.",
|
||||
hook_timeout_after_retry: "훅 요청 재시도 후에도 시간이 초과되었습니다.",
|
||||
identity_already_exists: "이미 연결된 아이덴티티입니다.",
|
||||
identity_not_found: "아이덴티티를 찾을 수 없습니다.",
|
||||
insufficient_aal: "추가 인증이 필요합니다.",
|
||||
invalid_credentials: "이메일 또는 비밀번호가 일치하지 않습니다.",
|
||||
invite_not_found: "초대 링크가 만료되었거나 이미 사용되었습니다.",
|
||||
manual_linking_disabled: "계정 연결 기능이 비활성화되어 있습니다.",
|
||||
mfa_challenge_expired: "MFA 인증 시간이 초과되었습니다.",
|
||||
mfa_factor_name_conflict: "MFA 요인 이름이 중복되었습니다.",
|
||||
mfa_factor_not_found: "MFA 요인을 찾을 수 없습니다.",
|
||||
mfa_ip_address_mismatch: "MFA 등록 시작/종료 IP가 일치하지 않습니다.",
|
||||
mfa_phone_enroll_not_enabled: "전화 MFA 등록이 비활성화되어 있습니다.",
|
||||
mfa_phone_verify_not_enabled: "전화 MFA 검증이 비활성화되어 있습니다.",
|
||||
mfa_totp_enroll_not_enabled: "TOTP MFA 등록이 비활성화되어 있습니다.",
|
||||
mfa_totp_verify_not_enabled: "TOTP MFA 검증이 비활성화되어 있습니다.",
|
||||
mfa_verification_failed: "MFA 검증에 실패했습니다.",
|
||||
mfa_verification_rejected: "MFA 검증이 거부되었습니다.",
|
||||
mfa_verified_factor_exists: "이미 검증된 전화 MFA가 존재합니다.",
|
||||
mfa_web_authn_enroll_not_enabled: "WebAuthn MFA 등록이 비활성화되어 있습니다.",
|
||||
mfa_web_authn_verify_not_enabled: "WebAuthn MFA 검증이 비활성화되어 있습니다.",
|
||||
no_authorization: "Authorization 헤더가 필요합니다.",
|
||||
not_admin: "관리자 권한이 없습니다.",
|
||||
oauth_provider_not_supported: "OAuth 제공자가 비활성화되어 있습니다.",
|
||||
otp_disabled: "OTP 로그인이 비활성화되어 있습니다.",
|
||||
otp_expired: "OTP가 만료되었습니다.",
|
||||
over_email_send_rate_limit: "이메일 발송 제한을 초과했습니다.",
|
||||
over_request_rate_limit: "요청 제한을 초과했습니다.",
|
||||
over_sms_send_rate_limit: "SMS 발송 제한을 초과했습니다.",
|
||||
phone_exists: "이미 가입된 전화번호입니다.",
|
||||
phone_not_confirmed: "전화번호 인증이 완료되지 않았습니다.",
|
||||
phone_provider_disabled: "전화번호 가입이 비활성화되어 있습니다.",
|
||||
provider_disabled: "OAuth 제공자가 비활성화되어 있습니다.",
|
||||
provider_email_needs_verification: "OAuth 이메일 인증이 필요합니다.",
|
||||
reauthentication_needed: "비밀번호 변경을 위해 재인증이 필요합니다.",
|
||||
reauthentication_not_valid: "재인증 코드가 유효하지 않습니다.",
|
||||
refresh_token_already_used: "세션이 만료되었습니다. 다시 로그인해주세요.",
|
||||
refresh_token_not_found: "세션을 찾을 수 없습니다.",
|
||||
request_timeout: "요청 시간이 초과되었습니다.",
|
||||
same_password: "새 비밀번호는 기존 비밀번호와 달라야 합니다.",
|
||||
saml_assertion_no_email: "SAML 응답에 이메일이 없습니다.",
|
||||
saml_assertion_no_user_id: "SAML 응답에 사용자 ID가 없습니다.",
|
||||
saml_entity_id_mismatch: "SAML 엔티티 ID가 일치하지 않습니다.",
|
||||
saml_idp_already_exists: "SAML IdP가 이미 등록되어 있습니다.",
|
||||
saml_idp_not_found: "SAML IdP를 찾을 수 없습니다.",
|
||||
saml_metadata_fetch_failed: "SAML 메타데이터를 불러오지 못했습니다.",
|
||||
saml_provider_disabled: "SAML SSO가 비활성화되어 있습니다.",
|
||||
saml_relay_state_expired: "SAML relay state가 만료되었습니다.",
|
||||
saml_relay_state_not_found: "SAML relay state를 찾을 수 없습니다.",
|
||||
session_expired: "세션이 만료되었습니다.",
|
||||
session_not_found: "세션을 찾을 수 없습니다.",
|
||||
signup_disabled: "회원가입이 비활성화되어 있습니다.",
|
||||
single_identity_not_deletable: "유일한 아이덴티티는 삭제할 수 없습니다.",
|
||||
sms_send_failed: "SMS 발송에 실패했습니다.",
|
||||
sso_domain_already_exists: "SSO 도메인이 이미 등록되어 있습니다.",
|
||||
sso_provider_not_found: "SSO 제공자를 찾을 수 없습니다.",
|
||||
too_many_enrolled_mfa_factors: "등록 가능한 MFA 요인 수를 초과했습니다.",
|
||||
unexpected_audience: "토큰 audience가 일치하지 않습니다.",
|
||||
unexpected_failure: "인증 서버 오류가 발생했습니다.",
|
||||
user_already_exists: "이미 존재하는 사용자입니다.",
|
||||
user_banned: "계정이 일시적으로 차단되었습니다.",
|
||||
user_not_found: "사용자를 찾을 수 없습니다.",
|
||||
user_sso_managed: "SSO 사용자 정보는 수정할 수 없습니다.",
|
||||
validation_failed: "요청 값 형식이 올바르지 않습니다.",
|
||||
weak_password: "비밀번호가 정책을 만족하지 않습니다.",
|
||||
} as const;
|
||||
|
||||
export const AUTH_ERROR_STATUS_MESSAGES = {
|
||||
403: "요청한 기능을 사용할 수 없습니다.",
|
||||
422: "요청을 처리할 수 없는 상태입니다.",
|
||||
429: "요청이 너무 많습니다. 잠시 후 다시 시도해주세요.",
|
||||
500: "인증 서버 오류가 발생했습니다.",
|
||||
501: "요청한 기능이 활성화되어 있지 않습니다.",
|
||||
} as const;
|
||||
|
||||
// ========================================
|
||||
// 라우트 경로 상수
|
||||
// ========================================
|
||||
@@ -91,6 +192,10 @@ export const PUBLIC_AUTH_PAGES = [
|
||||
AUTH_ROUTES.AUTH_CALLBACK,
|
||||
] as const;
|
||||
|
||||
// 복구 플로우 전용 쿠키 (비밀번호 재설정 화면 외 접근 차단에 사용)
|
||||
export const RECOVERY_COOKIE_NAME = "sb-recovery";
|
||||
export const RECOVERY_COOKIE_MAX_AGE_SECONDS = 10 * 60;
|
||||
|
||||
// ========================================
|
||||
// 검증 규칙 상수
|
||||
// ========================================
|
||||
|
||||
Reference in New Issue
Block a user