다크모드
SignIn 계약 — BE 참고 매뉴얼
범위
이 문서는 Leyve 로그인 화면이 요구하는 인증 계약을 설명합니다. 현재 레포는 Supabase로 동작을 검증하는 프로토타입이며, 실서비스 AWS + RDS MariaDB 구현에서도 아래 문맥·식별자·보안 불변식을 유지해야 합니다. Supabase 테이블 구조나 RLS를 목표 아키텍처로 간주하지 않습니다.
인증 경로
| 로그인 문맥 | 식별자 유형 | 사용자 입력 |
|---|---|---|
| Organization Owner | EMAIL | 이메일 + 패스워드 |
| Organization 최고관리자/중간관리자/조회자 | ORGANIZATION_USERNAME | 조직 코드 + username + 패스워드 |
| Workspace | WORKSPACE_USERNAME | 워크스페이스 코드 + username + 패스워드 |
FE는 세 경로를 iam-sign-in 계약으로 전달합니다. 프로토타입 adapter는 src/composables/iamAuthRepo.js, organizationAuthRepo.js, workspaceAuthRepo.js에 있습니다. 로그인 화면의 대상 사용자·운영자 포털 설명 문구는 API 계약이 아니며 화면에 노출하지 않습니다. 로그인 문맥과 식별자 유형은 route와 요청 payload로 명시합니다.
요청 payload는 식별자 유형별 필드를 사용합니다. code와 signInId 같은 범용 필드는 받지 않으며 호환 alias도 두지 않습니다.
json
{"signInContext":"organization","identifierType":"EMAIL","email":"id@email.com","password":"..."}
{"signInContext":"organization","identifierType":"ORGANIZATION_USERNAME","organizationCode":"org-7k3m9p4x2d","organizationUsername":"username","password":"..."}
{"signInContext":"workspace","identifierType":"WORKSPACE_USERNAME","workspaceCode":"ws-4f8q2r7n5c","workspaceUsername":"username","password":"..."}username은 전역 고유일 필요가 없습니다. 저장·조회 시에는 정규화된 {username}@{organization_code} 또는 {username}@{workspace_code} 복합 식별자를 사용해 코드가 다른 영역에서 같은 username을 허용합니다. DB generated column sign_in_identifier_key가 이 복합 조회 키를 소유하며 사용자 UI에는 노출하지 않습니다.
정규화와 검증
- 이메일: trim + lowercase
- 조직 코드:
org-+ Crockford 계열 10자 - 워크스페이스 코드:
ws-+ Crockford 계열 10자 username: 영문·숫자로 시작/종료하고 내부에.,_,-허용- 코드와
username: trim + lowercase
FE 정규식은 빠른 입력 안내일 뿐 보안 경계가 아닙니다. API가 같은 규칙을 다시 검증하고, 조직/워크스페이스 활성 상태와 principal·affiliation·membership 유효기간을 확인해야 합니다. 레거시 무접두 워크스페이스 코드는 허용하지 않으며 FE와 API 모두 ws- 형식을 검사합니다.
세션과 인가 불변식
- 로그인 방법은 principal을 찾는 수단일 뿐 role이나 권한을 부여하지 않습니다. 다만 제품 정책상 Organization
EMAIL은 activeorganization.owner에게만 허용하고, 서버가 인증 후 Membership과 Owner Role Assignment를 재검증합니다. 최고관리자/중간관리자/조회자는ORGANIZATION_USERNAME를 사용합니다. - Organization 로그인은 Organization affiliation 범위를, Workspace 로그인은 해당 Workspace membership 범위를 세션 문맥으로 확정합니다.
- 한 principal이 여러 Workspace membership을 가질 수 있습니다.
- Workspace 로그인 후 다른 접근 가능한 Workspace로 이동할 수 있더라도, 매 요청에서 대상 membership과 role assignment를 다시 검사합니다.
- Leysys 본사 staff 로그인은 고객 로그인 계약에 포함하지 않으며 별도
ops-centersurface/issuer를 사용합니다.platform/superadmin은 고객 Role key가 아닙니다. - 인증 실패 응답은 사용자·코드 존재 여부를 구별하지 않습니다.
패스워드 복구
- Organization 이메일 계정: 검증된 이메일 recovery를 사용할 수 있습니다.
- Organization
username: 현재는 조직 최고관리자에게 재설정을 요청합니다. - Workspace
username: 현재는 워크스페이스 최고관리자에게 재설정을 요청합니다.
username 계정의 self-service 복구를 추가할 때는 별도 본인 확인 수단, 일회성 토큰, 만료, 재사용 방지, 모든 세션 폐기, 최초 로그인 시 변경 강제를 상태머신으로 정의해야 합니다. 최고관리자가 기존 패스워드를 조회하거나 전달하는 API는 만들지 않습니다.
오류 계약
FE에 반환하는 메시지는 다음 원칙을 지킵니다.
입력한 인증 정보를 확인해 주세요.로그인 세션을 시작하지 못했습니다.
DB 키, 내부 이메일, Auth provider 원문 오류, principal 존재 여부는 응답이나 로그에 노출하지 않습니다. 패스워드와 recovery token은 애플리케이션 로그·감사 이벤트·분석 payload에 기록하지 않습니다.
Supabase 프로토타입 배포 계약
Prototype UI의 고정 샘플 자격증명은 이메일 leysys@leysys.com, 조직 org-a1 / leysys, 워크스페이스 ws-a1 / leysys이며 패스워드는 모두 abcd1234입니다. 이 짧은 코드는 Prototype provider 경계에서만 처리하고 canonical DB code 제약이나 IAM payload의 별칭으로 추가하지 않습니다. 각 경로는 각각 organization.owner, organization.admin, workspace.admin principal로 분리합니다.
iam-sign-in과 iam-context는 호스팅 환경의 기본 시크릿인 SUPABASE_PUBLISHABLE_KEYS, SUPABASE_SECRET_KEYS JSON dictionary에서 default 키를 사용합니다. 두 dictionary는 모두 파싱 가능한 JSON이어야 하고 default에는 비어 있지 않은 문자열이 있어야 합니다. 단일 키 환경 변수나 다른 키 이름으로 대체하지 않습니다. 값이 없거나 형식이 잘못되면 함수는 fail-closed로 응답합니다. 시크릿 값이나 키 dictionary를 로그에 기록하지 않습니다.
로그인 전에는 authenticated 사용자 문맥이 없으므로 Edge Function의 secret key가 service_role로 IAM 테이블을 조회합니다. 마이그레이션은 조회 테이블에 select, 세션 원장에 select, insert, update, 인가 감사 이벤트에 select, insert만 명시적으로 부여합니다. RLS 우회 여부와 별개로 PostgreSQL table privilege가 필요하며, 권한 누락은 일반 자격 증명 오류로 숨기지 말고 서버 로그에서 운영 설정 결함으로 관측해야 합니다.
이 Prototype은 보존할 운영 데이터가 없으므로 추가 호환 migration을 만들지 않습니다. 20260822000000_organization_workspace_canonical_baseline.sql을 최종 DB blueprint로 갱신하고 DB reset, iam-sign-in, iam-context, FE adapter를 하나의 breaking release로 배포합니다.