18장. 가입과 이메일 검증을 계정 수명주기로 본다
가입은 사용자 row 생성이 아니라 상태 기계다. PENDING_EMAIL, ACTIVE, LOCKED, DISABLED, DELETED를 구분한다. 이메일 검증 token은 충분한 entropy, 단일 사용, 짧은 만료, 목적 binding을 가진다. DB에는 token 원문 대신 hash를 저장하고 성공 또는 만료 때 폐기한다.
PENDING_EMAIL --valid token--> ACTIVE
PENDING_EMAIL --expired token--> PENDING_EMAIL + resend
ACTIVE --admin disable--> DISABLED
재전송 endpoint에는 rate limit을 두고 “계정이 있습니다”를 노출하지 않는 응답을 쓴다. 이메일 링크의 host를 요청 header에서 무조건 조합하지 말고 승인된 public base URL을 사용한다. open redirect용 returnUrl은 allowlist로 제한한다.
초대 기반 B2B 가입에서는 초대 이메일과 실제 OIDC 이메일이 같다는 이유만으로 자동 membership을 만들지 않는다. 초대 ID, tenant, role, 만료, 사용 여부를 서버에서 검증하고 고권한 초대는 추가 승인을 둔다.