diff --git a/휴대폰 번호 단독 로그인 기술 검토 및 구현 검토.md b/휴대폰 번호 단독 로그인 기술 검토 및 구현 검토.md new file mode 100644 index 0000000..662311b --- /dev/null +++ b/휴대폰 번호 단독 로그인 기술 검토 및 구현 검토.md @@ -0,0 +1,477 @@ +# 휴대폰 번호 단독 로그인 기술 검토 및 구현 대안 + +작성일: 2026-06-23 + +## 1. 목적 + +본 문서는 Baron SSO에서 논의된 "휴대폰 번호만 입력하면 즉시 로그인되는 방식"의 기술 구현안을 검토하고, 각 프로세스에서 어떤 데이터가 생성되는지, 해당 단계가 표준 기술에 해당하는지, 비표준 또는 우회 구현이라면 어떤 위험이 있는지 정리한다. + +검토 범위는 다음 이슈와 현재 코드 기준 구현 흔적을 포함한다. + +- Gitea Issue #1241: `[Feature/API] Headless 폰번호 전용 로그인 API 구현` +- Gitea Issue #1245: `[Research/Design] 기존 QR코드/링크 로그인 아키텍처 분석 및 Headless 폰번호 로그인 적용 설계안` +- Gitea Issue #1247: `[Architecture/Design] Baron Backend ↔ Ory Kratos 인터랙션 분석 및 폰번호 로그인 세션 발급 아키텍처` +- Gitea Issue #1248: `[Feature] 휴대폰 번호 입력만으로 즉시 로그인 기능 구현 (인증 코드 없는 Passwordless)` +- Gitea Issue #1252: `[Feature] userfront 내 휴대폰 번호 입력 전용 초간결 로그인 화면/모드 구현` +- Gitea Issue #1258: `[Feature] userfront 내 휴대폰 번호 전용 독립 라우트(connect) 신설 및 비인가/로그아웃 리다이렉션 전면 일괄 전환` + +## 2. 결론 요약 + +휴대폰 번호만으로 로그인시키는 방식은 사용자 인증 관점에서 표준 로그인 방식으로 보기 어렵다. 휴대폰 번호는 사용자 식별자(identifier)일 수는 있지만, 그 자체로 사용자 본인성 또는 번호 소유를 증명하는 인증 수단(authenticator)이 아니다. + +현재 논의된 구현안은 크게 세 가지로 나뉜다. + +| 방식 | 요약 | 표준성 판단 | 권장 여부 | +| --- | --- | --- | --- | +| Kratos Admin 세션 직접 발급 | 번호로 사용자를 찾은 뒤 관리자 API로 세션 생성 | 제품 내부 Admin API 사용일 수 있으나 사용자 인증 우회 | 비권장 | +| Courier 인터셉트/코드 자동 제출 | Kratos code login을 시작하고 발송 코드를 백엔드가 가로채 제출 | Kratos code login 자체는 표준 플로우이나 코드 소유자 확인을 제거한 우회 | 비권장 | +| Headless client assertion + 폰번호 | RP 클라이언트 JWT는 검증하고 사용자는 번호만 입력 | 클라이언트 인증은 표준 JWT 기반일 수 있으나 사용자 인증은 부재 | 제한적/보완 필수 | + +보완 없이 운영하면 전화번호를 아는 사람 또는 내부 시스템 접근자가 타인의 세션을 만들 수 있는 구조가 된다. 따라서 최소한 SMS OTP, FIDO2/WebAuthn, 기기 바인딩, 사전 등록 단말 인증, 관리자 승인, 네트워크 제한 중 하나 이상의 실질적인 사용자 인증 또는 환경 인증이 필요하다. + +## 3. 현재 코드 기준 상태 + +현재 코드에서 확인되는 관련 위치는 다음과 같다. + +| 영역 | 파일 | 확인 내용 | +| --- | --- | --- | +| Backend route | `backend/cmd/server/main.go` | `/api/v1/auth/headless/link/init`, `/api/v1/auth/headless/link/poll` 등록 | +| Headless link init/poll | `backend/internal/handler/auth_handler.go` | `HeadlessLinkInit`, `HeadlessLinkPoll`, `startHeadlessPhoneLink` 구현 | +| Kratos code login 시작 | `backend/internal/service/ory_service.go` | `InitiateLinkLogin`에서 Kratos code login flow 시작 | +| Kratos code 제출 | `backend/internal/service/ory_service.go` | `VerifyLoginCode`에서 `self-service/login?flow=...`에 code 제출 | +| Courier relay | `backend/internal/handler/auth_handler.go` | `HandleKratosCourierRelay`에서 login code를 Redis에 저장하거나 QR 흐름을 자동 검증 | +| Admin 세션 직접 발급 함수 | `backend/internal/handler/auth_handler.go` | `issueKratosSession` 함수는 있으나 현재 검색 기준 호출부는 없음 | +| OryProvider IssueSession | `backend/internal/service/ory_service.go` | `IssueSession`은 `domain.ErrNotSupported` 반환 | +| Userfront phone-only route | `userfront/lib/main.dart`, `userfront/lib/features/auth/presentation/login_screen.dart` | 현재 검색 기준 `connect`, `phone_only`, `phone-login` 직접 구현은 확인되지 않음 | + +따라서 이슈 #1248에서 제안된 `POST /api/v1/auth/phone-login` 직접 API는 현재 코드상 활성 라우트로 확인되지 않는다. 실제로 가까운 구현은 `/api/v1/auth/headless/link/init` 및 `/poll` 기반의 headless link 흐름이다. + +## 4. 프로세스별 데이터 생성 및 표준성 검토 + +### 4.1 휴대폰 번호 입력 및 정규화 + +프로세스: + +1. 사용자가 휴대폰 번호를 입력한다. +2. Userfront 또는 Backend가 하이픈, 공백 등을 제거한다. +3. Backend가 `normalizePhoneForLoginID` 계열 로직으로 E.164에 가까운 로그인 식별자로 변환한다. +4. 변환된 값으로 Kratos identity 또는 로컬 원장을 조회한다. + +생성 또는 변환되는 데이터: + +| 데이터 | 예시 | 생성 주체 | 용도 | +| --- | --- | --- | --- | +| Raw phone number | `010-1234-5678` | 사용자/Userfront | 입력값 | +| Sanitized phone | `01012345678` | Backend | 정규화 전처리 | +| Login ID | `+821012345678` | Backend | Kratos credentials identifier 조회 | +| Identity ID | UUID | Kratos/Admin 조회 | 최종 subject 후보 | + +표준성 판단: + +- 전화번호 E.164 정규화는 표준적인 식별자 정규화에 해당한다. +- 그러나 전화번호 입력만으로 사용자를 인증하는 것은 표준 인증 기술이 아니다. +- 이 단계는 "식별" 단계이지 "인증" 단계가 아니다. + +위험: + +- 전화번호는 공유되거나 유출되기 쉽다. +- 번호 입력 성공/실패 응답이 다르면 사용자 존재 여부 열람(user enumeration)이 가능하다. +- 가입자 번호 재할당, 퇴사자 번호 회수, 가족/공용 단말 사용 같은 실제 운영 문제가 인증 실패로 이어질 수 있다. + +권장 보완: + +- 존재 여부 응답을 일반화한다. +- 번호는 반드시 소유 증명 단계로 이어지게 한다. +- 사용자 원장에는 E.164, 국가 코드, 원본 표시값을 분리 저장한다. + +### 4.2 사용자 존재 여부 조회 + +프로세스: + +1. Backend가 정규화된 전화번호를 사용해 `UserExists` 또는 Kratos Admin identity 조회를 수행한다. +2. 사용자가 없으면 오류를 반환한다. +3. 사용자가 있으면 다음 세션 생성 단계로 진행한다. + +생성 또는 조회되는 데이터: + +| 데이터 | 생성/조회 주체 | 용도 | +| --- | --- | --- | +| credentials identifier | Backend | Kratos identity 검색 조건 | +| identity id | Kratos | OIDC subject 또는 세션 대상 | +| user exists boolean | Backend | 로그인 진행 여부 | + +표준성 판단: + +- IdP 원장에서 identifier로 identity를 조회하는 행위 자체는 일반적이다. +- 다만 이 결과만으로 로그인 성공을 허용하면 인증 요소가 없다. + +위험: + +- 공격자가 전화번호 목록으로 등록 여부를 확인할 수 있다. +- 내부 API가 열려 있으면 대량 스캐닝이 가능하다. + +권장 보완: + +- rate limit, IP 제한, WAF 룰, device attestation 등을 추가한다. +- `404 User not registered` 대신 일반 메시지를 사용한다. +- 감사 로그에는 입력 원문이 아닌 마스킹된 번호와 해시를 남긴다. + +### 4.3 Kratos Admin 세션 직접 발급 방식 + +이슈 #1248 초반 설계에서는 다음 흐름이 제안되었다. + +1. `POST /api/v1/auth/phone-login` 요청을 받는다. +2. Backend가 전화번호로 Kratos identity ID를 찾는다. +3. Backend가 Kratos Admin API로 해당 identity에 대한 세션을 직접 생성한다. +4. 세션 토큰을 Userfront에 반환한다. + +생성되는 데이터: + +| 데이터 | 생성 주체 | 저장/전달 위치 | +| --- | --- | --- | +| Kratos session id | Kratos | Kratos DB, Backend 응답 처리 | +| Kratos session token | Kratos | Backend 응답, Userfront token store | +| authenticated_at | Kratos | 세션 메타데이터 | +| AAL | Backend 요청/Kratos | 세션 보증 수준 | + +표준성 판단: + +- 관리자 API를 통한 세션 생성 기능 자체가 제품 기능일 수는 있다. +- 그러나 사용자가 아무 인증 수단도 제시하지 않았는데 관리자 권한으로 세션을 만들어 주는 것은 사용자 인증 표준 흐름이 아니다. +- OAuth2/OIDC 관점에서도 인증 서버가 사용자의 본인성을 확인하지 않은 채 login challenge를 accept하면 인증 의미가 훼손된다. + +현재 코드 상태: + +- `issueKratosSession(ctx, identityID)` 함수가 존재한다. +- 현재 검색 기준 이 함수의 호출부는 확인되지 않는다. +- `OryProvider.IssueSession`은 `domain.ErrNotSupported`를 반환한다. + +위험: + +- Backend 권한 탈취 시 임의 사용자 세션 발급이 가능하다. +- 감사 로그에는 "정상 로그인"처럼 남을 수 있으나 실제 사용자 행위가 아니다. +- AAL1로 표시되더라도 실질적 authenticator가 없으므로 보증 수준 해석이 왜곡된다. + +권장 보완: + +- 운영 로그인 경로로 사용하지 않는다. +- 필요한 경우 break-glass 관리자 지원 기능으로만 제한하고, 별도 승인/티켓/감사/만료 정책을 둔다. +- 관리자 세션 발급 기능은 feature flag로 기본 비활성화한다. + +### 4.4 Courier 인터셉트 및 Code Claim 방식 + +이슈 #1248 후반 코멘트 및 #1245, #1247에서는 실제 구현에 가까운 방식으로 Courier 인터셉트가 설명되어 있다. + +프로세스: + +1. Backend가 전화번호를 기반으로 Kratos code login flow를 시작한다. +2. Kratos가 SMS 또는 email courier 발송을 시도한다. +3. Kratos Courier Webhook이 Backend의 relay endpoint를 호출한다. +4. Backend가 `template_data.login_code`를 추출한다. +5. Backend가 추출한 code를 즉시 Kratos `self-service/login?flow=...`에 제출한다. +6. Kratos가 session token을 발급한다. +7. Backend가 Redis pending session을 success로 갱신한다. +8. Poll 또는 후속 처리에서 Userfront/RP가 로그인 완료를 확인한다. + +생성되는 데이터: + +| 단계 | 데이터 | 생성 주체 | 저장 위치 | +| --- | --- | --- | --- | +| flow init | flow id | Kratos | Redis `login_code_flow:{loginID}` | +| pending 생성 | pendingRef | Backend | Redis `enchanted_session:{pendingRef}` | +| pending 매핑 | loginID -> pendingRef | Backend | Redis `login_code_pending:{loginID}` | +| courier | login_code | Kratos | Webhook payload | +| code 저장 | normalized login code | Backend | Redis `login_code_value:{pendingRef}` | +| code verify | session token, session id | Kratos | Redis session payload 또는 응답 | +| audit | login event | Backend | Audit DB | + +표준성 판단: + +- Kratos code login flow 자체는 IdP의 정상 self-service login flow이다. +- Courier Webhook을 통해 메시지를 발송 대행하는 구조도 제품 통합 방식으로 볼 수 있다. +- 그러나 사용자가 수신한 OTP/SMS를 직접 확인하지 않고, 서버가 코드를 가로채 자동 제출하는 것은 OTP의 본래 보안 속성인 "사용자 소유 채널 확인"을 제거한다. +- 따라서 전체 로그인 방식은 표준 passwordless/SMS OTP 인증으로 보기 어렵다. 표준 기술을 사용해 비표준 인증 우회를 구성한 형태에 가깝다. + +위험: + +- 전화번호만 알면 세션 발급까지 진행될 수 있다. +- SMS 비용을 줄이는 대신 소유 증명이 사라진다. +- Courier relay가 내부 인증 없이 외부에서 호출 가능하면 코드 주입 또는 흐름 교란이 가능하다. +- Redis pending key 탈취 시 승인 흐름이 오염될 수 있다. + +권장 보완: + +- Courier relay endpoint는 내부 네트워크 또는 mTLS, shared secret, HMAC signature로 보호한다. +- code는 저장하지 않고 즉시 처리하되, 저장이 필요하면 TTL을 매우 짧게 유지한다. +- 자동 제출은 운영 사용자 로그인에 사용하지 않고, 테스트/개발 dry-run에 한정한다. +- 실서비스에서는 사용자에게 SMS OTP를 전달하고 사용자가 입력하도록 한다. + +### 4.5 Headless client assertion + 폰번호 로그인 + +이슈 #1241은 RP가 `client_assertion`을 제출하고, 사용자는 전화번호만 전달하는 headless API를 제안한다. + +프로세스: + +1. RP가 `client_id`, `client_assertion`, `phoneNumber`, `login_challenge`를 Backend에 제출한다. +2. Backend가 Hydra login request를 조회한다. +3. Backend가 RP의 JWT client assertion을 JWKS로 검증한다. +4. Backend가 전화번호로 사용자를 조회한다. +5. Backend가 세션 발급 또는 code login flow를 진행한다. +6. Backend가 Hydra `AcceptLoginRequest`를 호출한다. +7. RP는 `redirectTo` 또는 OIDC authorization code 흐름을 이어간다. + +생성되는 데이터: + +| 데이터 | 생성 주체 | 표준 기술 여부 | +| --- | --- | --- | +| client assertion JWT | RP | OAuth2 JWT client authentication 계열 | +| JWKS key | RP | JOSE/JWK 표준 | +| login challenge | Hydra | Ory Hydra/OIDC 로그인 플로우 | +| Kratos subject | Kratos | IdP subject | +| redirectTo | Hydra | OAuth2/OIDC authorization redirect | + +표준성 판단: + +- `client_assertion`과 JWKS 검증은 OAuth2/OIDC에서 사용하는 표준적인 클라이언트 인증 방식에 가깝다. +- Hydra login challenge, authorization code, PKCE는 표준 OIDC/OAuth2 기술이다. +- 그러나 이 검증은 RP 클라이언트가 신뢰 가능한지를 확인할 뿐, 최종 사용자가 전화번호 소유자임을 증명하지 않는다. +- 따라서 사용자 인증 단계는 여전히 비표준 또는 불충분한 상태다. + +위험: + +- 신뢰된 RP가 잘못 구현되거나 침해되면 임의 전화번호 로그인 시도가 가능하다. +- `client_assertion`을 사용자 인증으로 오해할 수 있다. +- RP별 정책 편차가 커지면 SSO의 인증 보증 수준이 불명확해진다. + +권장 보완: + +- headless API는 confidential client만 허용한다. +- RP별로 `headless_phone_login_enabled` 같은 명시적 allowlist를 둔다. +- 사용자 인증은 별도로 SMS OTP, WebAuthn, 앱 푸시 승인, 사전 등록 단말 증명 중 하나를 요구한다. +- `acr` 또는 `amr` claim에 실제 인증 방식을 명확히 기록한다. + +### 4.6 Hydra AcceptLoginRequest 및 OIDC 연동 + +프로세스: + +1. Backend가 Hydra login challenge를 조회한다. +2. 인증 완료로 판단한 subject를 결정한다. +3. Backend가 `AcceptLoginRequest(login_challenge, subject)`를 호출한다. +4. Hydra가 RP로 돌아갈 `redirectTo`를 반환한다. +5. RP는 authorization code 또는 token 교환을 수행한다. + +생성되는 데이터: + +| 데이터 | 생성 주체 | 용도 | +| --- | --- | --- | +| subject | Backend/Kratos | OIDC user identifier | +| redirectTo | Hydra | RP redirect | +| authorization code | Hydra | token endpoint 교환 | +| id/access/refresh token | Hydra | RP 세션 수립 | + +표준성 판단: + +- Hydra의 OAuth2/OIDC 처리 자체는 표준 흐름이다. +- PKCE가 적용된 authorization code flow는 표준 보안 권고에 부합한다. +- 단, Hydra가 accept하는 전제인 "사용자 인증 완료"가 비표준 방식이면 전체 로그인 보증 수준은 낮아진다. + +권장 보완: + +- 비표준 인증으로 accept한 경우 `amr=["phone_identifier_only"]`처럼 별도 표시를 한다. +- 민감 RP에는 해당 `amr`을 허용하지 않는다. +- RP별 required ACR 정책을 둔다. + +### 4.7 감사 로그 및 로그아웃 바인딩 + +프로세스: + +1. 로그인 성공 후 Backend가 audit log를 기록한다. +2. OIDC accept 또는 consent granted 이벤트에 `session_id`, `client_id`, `user_id`를 남긴다. +3. 로그아웃 시 audit log에서 session-client binding을 복원한다. +4. Hydra refresh revoke 및 backchannel logout을 수행한다. + +생성되는 데이터: + +| 데이터 | 생성 주체 | 용도 | +| --- | --- | --- | +| audit event | Backend | 로그인/승인 이력 | +| session_id | Kratos/Backend | 세션 식별 | +| client_id | Hydra/RP | RP 식별 | +| consent event | Backend | RP 동의/연동 추적 | + +표준성 판단: + +- Backchannel Logout, refresh token revoke는 OIDC/OAuth2 생태계의 표준적 세션 관리 기술이다. +- 감사 로그 기반 바인딩 복원은 내부 구현 전략이며 표준 자체는 아니다. + +위험: + +- 감사 로그 누락 또는 비동기 지연 시 로그아웃 전파 누락 가능성이 있다. +- 비표준 로그인으로 생성된 세션과 표준 로그인 세션이 같은 수준으로 취급될 수 있다. + +권장 보완: + +- 세션-클라이언트 바인딩은 감사 로그 외에도 명시적 저장소를 둘지 검토한다. +- 비표준 로그인 세션에는 별도 `login_method`, `amr`, `risk_level`을 남긴다. + +## 5. 표준 기술과 비표준 요소 매핑 + +| 구성 요소 | 적용 기술 | 표준/비표준 판단 | 비고 | +| --- | --- | --- | --- | +| 전화번호 E.164 정규화 | E.164 형식 | 표준 | 식별자 정규화 | +| Kratos self-service code login | Ory Kratos code login | 제품 표준 기능 | 사용자가 코드를 확인해야 인증 의미가 있음 | +| SMS OTP | One-time code over SMS | 일반적 MFA/passwordless 구현 | SIM swap, SMS 탈취 위험은 별도 존재 | +| Courier Webhook | Ory Courier 통합 | 제품 통합 기능 | 발송 대행은 가능 | +| Courier code 자동 추출/제출 | 내부 우회 | 비표준 | 사용자 소유 채널 확인 제거 | +| Kratos Admin 세션 직접 생성 | Admin API | 운영 인증에는 비표준/고위험 | break-glass 외 비권장 | +| OAuth2 client assertion JWT | JWT client authentication | 표준 계열 | 클라이언트 인증이지 사용자 인증이 아님 | +| JWKS | JOSE/JWK | 표준 | 키 배포/검증 | +| Hydra login challenge accept | OIDC/OAuth2 | 표준 | 인증 전제가 중요 | +| Authorization Code + PKCE | OAuth2/OIDC | 표준 | public client 권장 흐름 | +| Backchannel Logout | OIDC Back-Channel Logout | 표준 | RP 지원 필요 | +| Refresh token revoke | OAuth2 Token Revocation | 표준 | RP/AS 구현 정책 필요 | + +## 6. 대체 기술 및 보완 제안 + +### 6.1 권장안 A: SMS OTP 기반 폰 로그인 + +사용자 UX: + +1. 전화번호 입력 +2. SMS OTP 수신 +3. OTP 입력 +4. Kratos code login verify +5. Hydra accept + +장점: + +- 전화번호 소유 확인이 가능하다. +- 기존 Kratos code login 구조를 가장 자연스럽게 사용한다. +- 사용자 인증 방식이 명확하다. + +보완: + +- OTP TTL 3~5분 +- 재전송 제한 +- 번호별/IP별 rate limit +- 실패 횟수 제한 +- SIM swap 위험 안내 및 고위험 RP 추가 인증 + +### 6.2 권장안 B: WebAuthn/FIDO2 패스키 + +사용자 UX: + +1. 전화번호 또는 계정 식별자 입력 +2. 브라우저/OS 패스키 인증 +3. Kratos 세션 발급 +4. Hydra accept + +장점: + +- 피싱 저항성이 높다. +- SMS보다 강한 사용자 인증이다. +- 반복 로그인 UX가 빠르다. + +보완: + +- 초기 등록 절차 필요 +- 분실/기기 교체 복구 정책 필요 + +### 6.3 권장안 C: 사내 전용 단말/키오스크 제한 모드 + +휴대폰 번호 단독 UX를 반드시 유지해야 한다면 일반 로그인으로 보지 말고 "통제된 환경의 단말 로그인"으로 분리한다. + +필수 조건: + +- 전용 단말 인증서 또는 mTLS +- 고정 네트워크/IP allowlist +- 단말별 client credential +- 단말 등록/폐기 관리 +- 사용자별 허용 RP 제한 +- 짧은 세션 TTL +- 민감 기능 재인증 + +판단: + +- 사용자는 전화번호로 식별하고, 실제 인증은 단말/네트워크/관리 정책이 대신 수행하는 구조다. +- 이 경우에도 `amr`에는 `trusted_device_phone_identifier`처럼 일반 로그인과 다른 방식을 명시해야 한다. + +### 6.4 권장안 D: 모바일 앱 푸시 승인 + +사용자 UX: + +1. PC/키오스크에서 전화번호 입력 +2. 등록된 모바일 앱으로 푸시 승인 +3. 사용자가 앱에서 승인 +4. Backend가 Kratos/Hydra 흐름 완료 + +장점: + +- 빠른 UX를 유지하면서 사용자 소유 기기 확인이 가능하다. +- QR 로그인과 유사한 승인 모델을 재사용할 수 있다. + +보완: + +- 앱 등록/기기 바인딩 필요 +- 푸시 피로 공격 방지를 위한 rate limit와 number matching 필요 + +### 6.5 권장안 E: 공통 승인 화면 적용 + +현재 작업 트리에 있는 `approval/info`, `approval/reject`, `LoginApprovalScreen` 계열 변경은 QR/링크 같은 비표준 로그인에 승인 전 정보를 보여주는 보완책이다. + +권장 적용: + +- QR, 링크, headless link 모두 공통 승인 화면 사용 +- 승인 전에 요청 서비스, 기기, IP, 시간 표시 +- `내 요청이 아닙니다` 선택 시 pending 상태를 `rejected` 또는 `blocked`로 변경 +- 승인 거절 이벤트를 감사 로그로 남김 + +주의: + +- 승인 화면은 "보완책"이지 전화번호 단독 로그인의 인증 부재를 완전히 해결하지 않는다. +- 사용자가 직접 승인하는 별도 소유 기기나 세션이 있을 때 의미가 있다. + +## 7. 운영 정책 제안 + +### 7.1 기본 정책 + +- 휴대폰 번호만으로 세션을 발급하는 API는 운영 기본 경로로 노출하지 않는다. +- `POST /api/v1/auth/phone-login` 형태의 단일 요청 즉시 로그인은 금지한다. +- Courier code 자동 제출은 개발/테스트 dry-run 또는 제한된 내부 시나리오로만 허용한다. +- 모든 비표준 로그인에는 `login_method`, `amr`, `risk_level`, `client_id`, `device_id`를 남긴다. + +### 7.2 RP별 정책 + +- RP별로 허용 가능한 인증 강도를 설정한다. +- 민감 RP는 SMS OTP 이상 또는 WebAuthn을 요구한다. +- headless 로그인 허용 RP는 별도 allowlist와 보안 심사를 거친다. + +### 7.3 감사 및 탐지 + +- 전화번호 기반 로그인 시도는 성공/실패 모두 감사 로그에 남긴다. +- 동일 IP의 다수 번호 시도, 동일 번호의 반복 실패, 짧은 시간 내 다수 RP 로그인은 탐지 대상으로 둔다. +- 비표준 로그인으로 발급된 세션은 대시보드와 관리자 화면에서 구분 표시한다. + +## 8. 권장 구현 방향 + +1. 폰번호 단독 즉시 로그인은 구현하더라도 기본 비활성 feature flag로 둔다. +2. 운영 사용자 로그인에는 SMS OTP 또는 WebAuthn을 붙인다. +3. 키오스크/전용 단말 요구사항이라면 별도 라우트와 별도 보증 수준으로 분리한다. +4. Hydra accept 시 `amr`/`acr`를 명확히 기록하고 RP 정책과 연결한다. +5. Courier relay는 내부망 또는 서명 검증으로 보호한다. +6. 공통 승인 화면을 QR/링크/headless link에 적용해 사용자가 요청 정보를 확인하게 한다. +7. 표준 로그인과 비표준 로그인의 감사 로그, 세션 TTL, RP 접근 권한을 분리한다. + +## 9. 최종 판단 + +휴대폰 번호 단독 로그인은 "간편한 식별 UX"로는 사용할 수 있지만, 그 자체를 사용자 인증으로 간주하면 안 된다. + +Baron SSO의 표준 인증 경로로 채택하려면 다음 중 하나가 반드시 추가되어야 한다. + +- SMS OTP 또는 음성 OTP +- WebAuthn/FIDO2 패스키 +- 등록된 모바일 앱 푸시 승인 +- 사내 전용 단말 인증 및 네트워크 제한 +- 관리자 승인 또는 업무 시스템 내 2차 승인 + +이 중 아무 것도 없는 `phoneNumber -> session` 구조는 표준 인증이 아니라 인증 우회에 가깝다. 따라서 운영 적용 시에는 "비표준 저보증 로그인"으로 명시하고, 접근 가능한 RP와 기능 범위를 제한해야 한다.