# 휴대폰 번호 단독 로그인 기술 검토 및 구현 대안

작성일: 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와 기능 범위를 제한해야 한다.
