This commit is contained in:
@@ -0,0 +1,708 @@
|
|||||||
|
# Baron Safe 기반 휴대폰 승인 로그인 제안안
|
||||||
|
|
||||||
|
작성일: 2026-06-23
|
||||||
|
|
||||||
|
## 1. 목적
|
||||||
|
|
||||||
|
본 문서는 Baron SSO에서 논의 중인 "휴대전화번호 기반 간편 로그인"을 표준 기술에 가깝게 보완하기 위한 방안으로, 가칭 `baron-safe` 모바일 앱을 이용한 승인 기반 로그인 구조를 제안한다.
|
||||||
|
|
||||||
|
기존의 "휴대전화번호만 입력하면 즉시 로그인" 방식은 전화번호를 사용자 식별자(identifier)로만 사용하고, 사용자가 실제 번호 소유자이거나 현재 로그인 요청을 승인했다는 인증 근거가 부족하다. 반면 `baron-safe` 앱을 사전에 등록된 인증 장치(Authentication Device)로 사용하면, 사용자는 요청 RP와 요청 기기 정보를 확인한 뒤 직접 승인할 수 있고, Baron SSO는 승인 결과를 근거로 OIDC 로그인을 진행할 수 있다.
|
||||||
|
|
||||||
|
본 제안의 목표는 다음과 같다.
|
||||||
|
|
||||||
|
- 휴대전화번호 기반 UX의 간편함은 유지한다.
|
||||||
|
- 전화번호 단독 인증의 보안 취약점을 제거한다.
|
||||||
|
- OIDC/OAuth2 표준 모델과 최대한 정합성 있는 구조로 설계한다.
|
||||||
|
- RP별 보안 수준과 감사 추적성을 유지한다.
|
||||||
|
- 향후 WebAuthn, FIDO2, CIBA 등 표준 확장으로 발전 가능한 아키텍처를 확보한다.
|
||||||
|
|
||||||
|
## 2. 제안 배경
|
||||||
|
|
||||||
|
Baron SSO 통합로그인을 사용하는 대상은 일반 소비자 서비스처럼 불특정 다수의 익명 사용자가 아니다. 주된 사용자는 Baron 내부 구성원, 협력사 또는 고객사 구성원, 그리고 Baron SSO와 연동된 우리 회사 RP를 인지하고 사용하려는 등록 사용자로 한정된다.
|
||||||
|
|
||||||
|
따라서 로그인 편의성을 위해 보안을 낮추는 방향보다, 등록 사용자에게 한 번의 앱 설치와 기기 등록 절차를 요구하더라도 이후 모든 RP 로그인에서 더 명확한 본인 확인과 승인 추적성을 확보하는 방향이 조직 보안과 운영 책임 측면에서 더 타당하다.
|
||||||
|
|
||||||
|
`baron-safe` 앱 설치는 사용자에게 초기 번거로움을 줄 수 있다. 그러나 Baron SSO가 보호하는 대상은 내부 업무 시스템, 고객/협력사 연동 RP, 조직 데이터 접근 권한이므로, 해당 번거로움은 보안상 합리적인 비용으로 볼 수 있다. 특히 앱 기반 승인은 사용자가 "어떤 RP가, 어떤 기기에서, 언제 로그인을 요청했는지" 확인한 뒤 승인하도록 만들기 때문에, 단순 휴대전화번호 입력 방식보다 오인 로그인, 전화번호 도용, 내부 계정 오남용 위험을 줄일 수 있다.
|
||||||
|
|
||||||
|
즉, `baron-safe`는 범용 소비자 서비스의 추가 장벽이라기보다 Baron SSO 생태계에 참여하는 등록 사용자에게 제공되는 전용 안전 확인 채널로 정의하는 것이 적절하다.
|
||||||
|
|
||||||
|
## 3. 핵심 결론
|
||||||
|
|
||||||
|
`baron-safe` 앱 승인 방식은 기존 "폰번호만으로 즉시 로그인"보다 훨씬 안전하고, 표준 기술적으로도 설명 가능한 구조다.
|
||||||
|
|
||||||
|
Baron SSO의 사용자 범위가 등록된 내부/외부 사용자로 제한된다는 점을 고려하면, 앱 설치 및 기기 등록 절차는 과도한 진입 장벽이라기보다 SSO 연동 RP 전체에 공통 적용할 수 있는 보안 기반 장치로 볼 수 있다. 특히 전화번호 단독 로그인에서 부족했던 "현재 사용자가 실제로 이 요청을 승인했다"는 근거를 앱 승인과 기기 서명으로 확보할 수 있다.
|
||||||
|
|
||||||
|
이 방식은 OpenID Connect CIBA(Client-Initiated Backchannel Authentication) 모델과 가장 유사하다. CIBA는 RP가 사용자의 식별자를 기반으로 인증 요청을 시작하고, 사용자는 RP 요청 기기가 아닌 별도의 인증 장치에서 인증 및 동의를 수행하는 흐름이다.
|
||||||
|
|
||||||
|
Baron SSO에 적용하면 다음처럼 대응된다.
|
||||||
|
|
||||||
|
| CIBA 용어 | Baron SSO 대응 |
|
||||||
|
| --- | --- |
|
||||||
|
| OpenID Provider(OP) | Baron SSO + Hydra/Kratos/Backend |
|
||||||
|
| Relying Party(RP) | Baron SSO를 사용하는 업무 시스템 |
|
||||||
|
| Consumption Device(CD) | 사용자가 로그인을 시도한 브라우저/PC/키오스크 |
|
||||||
|
| Authentication Device(AD) | `baron-safe`가 설치된 사용자 휴대폰 |
|
||||||
|
| Backchannel Authentication Request | RP 또는 Baron SSO가 생성하는 로그인 승인 요청 |
|
||||||
|
| auth_req_id | Baron SSO 내부 로그인 승인 요청 ID |
|
||||||
|
| Poll/Ping/Push mode | 요청 기기가 승인 결과를 기다리는 방식 |
|
||||||
|
|
||||||
|
참고 표준:
|
||||||
|
|
||||||
|
- OpenID Connect Client-Initiated Backchannel Authentication Flow - Core 1.0
|
||||||
|
https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html
|
||||||
|
- OAuth 2.0 Device Authorization Grant, RFC 8628
|
||||||
|
https://www.rfc-editor.org/rfc/rfc8628
|
||||||
|
- OAuth 2.0 Pushed Authorization Requests, RFC 9126
|
||||||
|
https://www.rfc-editor.org/rfc/rfc9126
|
||||||
|
|
||||||
|
## 4. 기존 폰번호 단독 로그인과의 차이
|
||||||
|
|
||||||
|
| 항목 | 폰번호 단독 즉시 로그인 | Baron Safe 승인 로그인 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 사용자 입력 | 전화번호 | 전화번호 |
|
||||||
|
| 사용자 인증 근거 | 없음 또는 약함 | 등록된 앱/기기의 승인 |
|
||||||
|
| 요청 정보 확인 | 없음 | RP, IP, 기기, 시간 표시 |
|
||||||
|
| 사용자 행위 | 번호 입력만 | 앱에서 명시적 승인 |
|
||||||
|
| 세션 발급 근거 | 전화번호 식별 결과 | 승인 요청에 대한 서명된 응답 |
|
||||||
|
| 표준성 | 비표준 인증 우회에 가까움 | CIBA 스타일 Out-of-Band 인증 |
|
||||||
|
| 감사 가능성 | 낮음 | 요청/승인/거절/기기 정보 추적 가능 |
|
||||||
|
| 권장 여부 | 비권장 | 조건부 권장 |
|
||||||
|
|
||||||
|
핵심 차이는 전화번호가 인증 수단이 아니라 식별자로만 사용된다는 점을 명확히 분리하는 것이다. 실제 인증은 `baron-safe`에 등록된 인증 장치와 사용자 승인 행위가 담당한다.
|
||||||
|
|
||||||
|
## 5. 전체 아키텍처
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
autonumber
|
||||||
|
actor User as 사용자
|
||||||
|
participant CD as 요청 기기(브라우저/RP/키오스크)
|
||||||
|
participant RP as 요청 RP
|
||||||
|
participant SSO as Baron SSO Backend
|
||||||
|
participant HY as Ory Hydra
|
||||||
|
participant KR as Ory Kratos
|
||||||
|
participant PUSH as FCM/APNs
|
||||||
|
participant APP as Baron Safe 앱
|
||||||
|
|
||||||
|
User->>CD: 휴대전화번호 입력
|
||||||
|
CD->>RP: 로그인 시작
|
||||||
|
RP->>HY: OIDC Authorization Request
|
||||||
|
HY->>SSO: login_challenge 전달
|
||||||
|
SSO->>SSO: 전화번호 정규화 및 등록 기기 조회
|
||||||
|
SSO->>SSO: 승인 요청 생성(auth_req_id, nonce, expires_at)
|
||||||
|
SSO->>PUSH: 승인 알림 전송
|
||||||
|
PUSH->>APP: Push Notification
|
||||||
|
APP->>User: RP/기기/IP/시간 표시
|
||||||
|
User->>APP: 앱 잠금해제/생체인증 후 승인
|
||||||
|
APP->>SSO: 서명된 승인 응답 제출
|
||||||
|
SSO->>SSO: 기기 공개키/nonce/만료/상태 검증
|
||||||
|
SSO->>KR: 세션 확인 또는 세션 수립
|
||||||
|
SSO->>HY: AcceptLoginRequest(subject)
|
||||||
|
HY->>RP: redirectTo / authorization code
|
||||||
|
RP->>HY: token 교환
|
||||||
|
HY->>RP: ID Token / Access Token / Refresh Token
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 프로세스별 상세 설계
|
||||||
|
|
||||||
|
### 6.1 사전 등록 프로세스
|
||||||
|
|
||||||
|
`baron-safe` 앱은 단순 알림 앱이 아니라 사용자 계정에 바인딩된 인증 장치로 등록되어야 한다.
|
||||||
|
|
||||||
|
프로세스:
|
||||||
|
|
||||||
|
1. 사용자가 Baron SSO에 기존 표준 방식으로 로그인한다.
|
||||||
|
2. 사용자 설정 또는 관리자 초대 화면에서 `baron-safe` 기기 등록을 시작한다.
|
||||||
|
3. 앱이 기기 내 안전 저장소에서 key pair를 생성한다.
|
||||||
|
4. 앱이 public key, device id, push token, platform 정보를 Baron SSO에 등록한다.
|
||||||
|
5. Baron SSO는 해당 기기를 사용자 계정에 연결한다.
|
||||||
|
6. 등록 완료 후 해당 기기에서 승인 요청을 받을 수 있다.
|
||||||
|
|
||||||
|
생성되는 데이터:
|
||||||
|
|
||||||
|
| 데이터 | 생성 주체 | 저장 위치 | 설명 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| device_id | Baron Safe 또는 Backend | Backend DB | 기기 식별자 |
|
||||||
|
| public_key | Baron Safe | Backend DB | 승인 응답 서명 검증용 |
|
||||||
|
| private_key | Baron Safe | 기기 보안 저장소 | 서버로 전송 금지 |
|
||||||
|
| push_token | OS/FCM/APNs | Backend DB | 알림 발송용 |
|
||||||
|
| user_id | Kratos/Baron SSO | Backend DB | 사용자-기기 연결 |
|
||||||
|
| device_name | Baron Safe | Backend DB | 사용자 표시용 |
|
||||||
|
| registered_at | Backend | Backend DB | 등록 시각 |
|
||||||
|
| last_used_at | Backend | Backend DB | 마지막 승인 시각 |
|
||||||
|
|
||||||
|
표준 기술:
|
||||||
|
|
||||||
|
- JWK/JWS: 기기 public key 등록 및 승인 응답 서명 검증
|
||||||
|
- TLS: 앱-서버 통신 보호
|
||||||
|
- OS Secure Enclave/Keystore/Keychain: private key 보호
|
||||||
|
- 선택 적용: WebAuthn/FIDO2/passkey 기반 기기 등록
|
||||||
|
|
||||||
|
주의 사항:
|
||||||
|
|
||||||
|
- push token은 인증 수단이 아니다. 알림 전달 주소일 뿐이다.
|
||||||
|
- 실제 인증 근거는 기기 private key로 서명한 승인 응답과 사용자 확인 행위다.
|
||||||
|
- 기기 분실/교체/폐기 절차가 반드시 필요하다.
|
||||||
|
|
||||||
|
### 6.2 로그인 요청 생성 프로세스
|
||||||
|
|
||||||
|
프로세스:
|
||||||
|
|
||||||
|
1. 사용자가 RP 또는 Baron SSO 로그인 화면에서 휴대전화번호를 입력한다.
|
||||||
|
2. Baron SSO가 전화번호를 정규화한다.
|
||||||
|
3. 해당 번호의 사용자와 등록된 `baron-safe` 기기를 조회한다.
|
||||||
|
4. OIDC `login_challenge`와 RP client 정보를 조회한다.
|
||||||
|
5. Baron SSO가 승인 요청을 생성한다.
|
||||||
|
6. 승인 요청은 Redis 또는 DB에 TTL과 함께 저장된다.
|
||||||
|
|
||||||
|
생성되는 데이터:
|
||||||
|
|
||||||
|
| 데이터 | 예시 | 설명 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| auth_req_id | `ars_...` | 승인 요청 고유 ID |
|
||||||
|
| login_challenge | Hydra 값 | OIDC 로그인 요청 연결 |
|
||||||
|
| client_id | `hanmac-erp` | 요청 RP 식별자 |
|
||||||
|
| client_name | `한맥 ERP` | 앱 화면 표시용 |
|
||||||
|
| requested_subject | Kratos identity id | 승인 대상 사용자 |
|
||||||
|
| phone_hash | SHA-256 등 | 전화번호 원문 노출 최소화 |
|
||||||
|
| nonce | 난수 | 재전송 공격 방지 |
|
||||||
|
| request_ip | `203.0.113.xxx` | 요청 기기 정보 |
|
||||||
|
| user_agent | Chrome on Windows | 요청 기기 정보 |
|
||||||
|
| expires_at | 요청 만료 시각 | 예: 2분 |
|
||||||
|
| status | `pending` | pending/approved/rejected/expired |
|
||||||
|
|
||||||
|
표준 기술:
|
||||||
|
|
||||||
|
- OIDC Authorization Request
|
||||||
|
- Hydra login challenge
|
||||||
|
- CIBA의 `auth_req_id` 모델과 유사한 승인 요청 ID
|
||||||
|
- OAuth/OIDC client metadata
|
||||||
|
|
||||||
|
주의 사항:
|
||||||
|
|
||||||
|
- 전화번호 미등록/기기 미등록 여부가 외부에 드러나면 사용자 열람 공격이 가능하다.
|
||||||
|
- 응답 메시지는 "승인 요청을 확인해 주세요"처럼 일반화하는 것이 안전하다.
|
||||||
|
- 요청 TTL은 짧게 설정한다. 예: 60~180초.
|
||||||
|
|
||||||
|
### 6.3 앱 알림 및 요청 표시 프로세스
|
||||||
|
|
||||||
|
프로세스:
|
||||||
|
|
||||||
|
1. Baron SSO가 등록된 기기의 push token으로 알림을 보낸다.
|
||||||
|
2. 알림에는 민감정보를 최소화하고, 상세 정보는 앱이 서버에서 조회한다.
|
||||||
|
3. 앱은 `auth_req_id`로 승인 요청 상세를 조회한다.
|
||||||
|
4. 앱은 사용자에게 RP 정보와 요청 기기 정보를 표시한다.
|
||||||
|
|
||||||
|
앱 표시 정보:
|
||||||
|
|
||||||
|
| 항목 | 예시 |
|
||||||
|
| --- | --- |
|
||||||
|
| 요청 서비스 | 한맥 ERP |
|
||||||
|
| 요청 시간 | 2026-06-23 15:30 |
|
||||||
|
| 요청 기기 | Chrome on Windows |
|
||||||
|
| 요청 위치/IP | 203.0.113.xxx |
|
||||||
|
| 요청 방식 | Baron Safe 승인 로그인 |
|
||||||
|
| 만료 시간 | 2분 후 만료 |
|
||||||
|
|
||||||
|
표준 기술:
|
||||||
|
|
||||||
|
- FCM/APNs: 모바일 푸시 알림 전달
|
||||||
|
- TLS API: 요청 상세 조회
|
||||||
|
- OAuth2 Bearer 또는 기기 서명 기반 앱 인증
|
||||||
|
|
||||||
|
주의 사항:
|
||||||
|
|
||||||
|
- 푸시 메시지 자체에 전화번호, 사용자 이름, RP 민감정보를 과도하게 넣지 않는다.
|
||||||
|
- 앱 상세 조회 API는 등록된 기기만 접근할 수 있어야 한다.
|
||||||
|
- 승인 화면에는 "본인이 방금 요청한 로그인이 맞습니까?"를 명확히 표시한다.
|
||||||
|
|
||||||
|
### 6.4 사용자 승인 프로세스
|
||||||
|
|
||||||
|
프로세스:
|
||||||
|
|
||||||
|
1. 사용자가 `baron-safe` 앱을 연다.
|
||||||
|
2. 앱이 OS 생체 인증, 앱 PIN, 또는 패스키로 사용자 확인을 수행한다.
|
||||||
|
3. 사용자가 요청 정보를 확인한다.
|
||||||
|
4. 사용자가 `승인` 또는 `거절`을 선택한다.
|
||||||
|
5. 앱은 승인 응답 payload를 생성한다.
|
||||||
|
6. 앱은 payload에 기기 private key로 서명한다.
|
||||||
|
7. 앱이 Baron SSO에 승인 응답을 제출한다.
|
||||||
|
|
||||||
|
승인 응답 예시:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"authReqId": "ars_01J...",
|
||||||
|
"deviceId": "dev_01J...",
|
||||||
|
"decision": "approve",
|
||||||
|
"nonce": "n_01J...",
|
||||||
|
"userPresence": true,
|
||||||
|
"userVerification": "biometric",
|
||||||
|
"approvedAt": "2026-06-23T15:31:02+09:00",
|
||||||
|
"signature": "base64url(jws-signature)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
생성되는 데이터:
|
||||||
|
|
||||||
|
| 데이터 | 생성 주체 | 설명 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| decision | Baron Safe | approve/reject |
|
||||||
|
| user_presence | Baron Safe | 사용자가 앱을 조작했는지 |
|
||||||
|
| user_verification | Baron Safe | biometric/pin/passkey |
|
||||||
|
| signature | Baron Safe | 승인 응답 위변조 방지 |
|
||||||
|
| approved_at | Baron Safe/Backend | 승인 시각 |
|
||||||
|
|
||||||
|
표준 기술:
|
||||||
|
|
||||||
|
- JWS 또는 COSE 서명
|
||||||
|
- OS 생체 인증 API
|
||||||
|
- 선택 적용: FIDO2/WebAuthn user verification
|
||||||
|
|
||||||
|
주의 사항:
|
||||||
|
|
||||||
|
- "승인"은 반드시 사용자 확인 이후에만 가능해야 한다.
|
||||||
|
- 앱 백그라운드에서 자동 승인하면 안 된다.
|
||||||
|
- 동일 요청에 대한 중복 승인/재전송은 거부해야 한다.
|
||||||
|
|
||||||
|
### 6.5 서버 검증 및 로그인 완료 프로세스
|
||||||
|
|
||||||
|
프로세스:
|
||||||
|
|
||||||
|
1. Baron SSO가 `auth_req_id`의 상태를 조회한다.
|
||||||
|
2. 요청이 pending인지 확인한다.
|
||||||
|
3. 만료 여부를 확인한다.
|
||||||
|
4. 제출된 `device_id`가 해당 사용자에게 등록된 기기인지 확인한다.
|
||||||
|
5. 저장된 public key로 signature를 검증한다.
|
||||||
|
6. nonce가 일치하고 재사용되지 않았는지 확인한다.
|
||||||
|
7. 승인 결과가 approve이면 인증 완료 상태로 전환한다.
|
||||||
|
8. Kratos subject를 확정한다.
|
||||||
|
9. Hydra `AcceptLoginRequest`를 호출한다.
|
||||||
|
10. RP로 redirect 또는 token 교환 흐름을 진행한다.
|
||||||
|
|
||||||
|
서버 상태 전이:
|
||||||
|
|
||||||
|
| 이전 상태 | 이벤트 | 다음 상태 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| pending | 앱 승인 성공 | approved |
|
||||||
|
| pending | 앱 거절 | rejected |
|
||||||
|
| pending | TTL 만료 | expired |
|
||||||
|
| approved | Hydra accept 성공 | completed |
|
||||||
|
| any | 재사용/위조 시도 | blocked 또는 failed |
|
||||||
|
|
||||||
|
표준 기술:
|
||||||
|
|
||||||
|
- OIDC login challenge accept
|
||||||
|
- OAuth2 Authorization Code + PKCE
|
||||||
|
- JWS signature verification
|
||||||
|
- OIDC `amr`, `acr` claim 확장
|
||||||
|
|
||||||
|
권장 claim:
|
||||||
|
|
||||||
|
| claim | 예시 | 설명 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| amr | `["app_push", "device_key", "biometric"]` | 인증 수단 |
|
||||||
|
| acr | `baron:safe:aal2` | Baron 내부 인증 강도 |
|
||||||
|
| auth_time | Unix timestamp | 사용자 승인 시각 |
|
||||||
|
| sid | session id | 세션 관리 |
|
||||||
|
|
||||||
|
주의 사항:
|
||||||
|
|
||||||
|
- `amr`/`acr`을 통해 일반 비밀번호 로그인, SMS OTP, Baron Safe 승인을 구분해야 한다.
|
||||||
|
- 민감 RP는 특정 `acr` 이상만 허용하도록 정책화할 수 있다.
|
||||||
|
- 승인 완료 후에도 기존 OIDC/OAuth2 토큰 발급은 Hydra 표준 흐름을 유지하는 것이 좋다.
|
||||||
|
|
||||||
|
## 7. API 초안
|
||||||
|
|
||||||
|
### 7.1 승인 요청 생성
|
||||||
|
|
||||||
|
내부 또는 RP backchannel API:
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /api/v1/auth/baron-safe/login/init
|
||||||
|
```
|
||||||
|
|
||||||
|
요청:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"phoneNumber": "010-1234-5678",
|
||||||
|
"login_challenge": "hydra-login-challenge",
|
||||||
|
"client_id": "hanmac-erp"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
응답:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"authReqId": "ars_01J...",
|
||||||
|
"status": "pending",
|
||||||
|
"expiresIn": 120,
|
||||||
|
"interval": 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 요청 기기 Poll
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /api/v1/auth/baron-safe/login/poll
|
||||||
|
```
|
||||||
|
|
||||||
|
요청:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"authReqId": "ars_01J..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
응답 예시:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "authorization_pending",
|
||||||
|
"interval": 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
승인 완료 시:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"redirectTo": "https://sso.example.com/oauth2/auth?..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 앱 요청 상세 조회
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /api/v1/auth/baron-safe/requests/{authReqId}
|
||||||
|
```
|
||||||
|
|
||||||
|
응답:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"authReqId": "ars_01J...",
|
||||||
|
"clientName": "한맥 ERP",
|
||||||
|
"clientId": "hanmac-erp",
|
||||||
|
"requestDevice": "Chrome on Windows",
|
||||||
|
"requestIpAddress": "203.0.113.xxx",
|
||||||
|
"requestedAt": "2026-06-23T15:30:00+09:00",
|
||||||
|
"expiresAt": "2026-06-23T15:32:00+09:00",
|
||||||
|
"nonce": "n_01J..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.4 앱 승인/거절 제출
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /api/v1/auth/baron-safe/requests/{authReqId}/decision
|
||||||
|
```
|
||||||
|
|
||||||
|
요청:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"deviceId": "dev_01J...",
|
||||||
|
"decision": "approve",
|
||||||
|
"nonce": "n_01J...",
|
||||||
|
"userPresence": true,
|
||||||
|
"userVerification": "biometric",
|
||||||
|
"approvedAt": "2026-06-23T15:31:02+09:00",
|
||||||
|
"signature": "base64url-signature"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
응답:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "approved"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. 보안 요구사항
|
||||||
|
|
||||||
|
### 8.1 필수 요구사항
|
||||||
|
|
||||||
|
- `baron-safe` 기기는 사전에 사용자 계정에 등록되어야 한다.
|
||||||
|
- 기기별 private key는 서버에 저장하지 않는다.
|
||||||
|
- 승인 응답은 기기 private key로 서명해야 한다.
|
||||||
|
- 승인 요청은 짧은 TTL을 가져야 한다.
|
||||||
|
- nonce는 요청별 1회용이어야 한다.
|
||||||
|
- 동일 요청의 중복 승인, 만료 후 승인, 다른 기기의 승인은 거부한다.
|
||||||
|
- 앱 승인 전 사용자 확인을 수행한다.
|
||||||
|
- 모든 통신은 TLS를 사용한다.
|
||||||
|
- 승인/거절/만료/실패를 감사 로그로 남긴다.
|
||||||
|
|
||||||
|
### 8.2 권장 요구사항
|
||||||
|
|
||||||
|
- 기기 등록 시 기존 표준 로그인 또는 관리자 초대를 요구한다.
|
||||||
|
- 기기 변경/해제 시 사용자와 관리자에게 알림을 보낸다.
|
||||||
|
- 고위험 RP는 WebAuthn 또는 추가 PIN을 요구한다.
|
||||||
|
- 푸시 피로 공격 방지를 위해 단기간 반복 요청을 제한한다.
|
||||||
|
- 승인 화면에 number matching 또는 request code를 추가한다.
|
||||||
|
- 위치/IP가 평소와 다르면 경고를 표시한다.
|
||||||
|
- `baron-safe` 인증으로 생성된 세션은 일반 세션과 구분 표시한다.
|
||||||
|
|
||||||
|
## 9. 표준 기술 적용 가능성
|
||||||
|
|
||||||
|
| 영역 | 적용 가능한 표준/기술 | 적용 방식 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Out-of-Band 인증 | OpenID Connect CIBA | 앱 승인 기반 비동기 인증 모델 |
|
||||||
|
| RP 최종 로그인 | OIDC Authorization Code + PKCE | 기존 Hydra 흐름 유지 |
|
||||||
|
| RP backchannel 인증 | `private_key_jwt`, mTLS | RP가 승인 요청 생성 시 자신을 인증 |
|
||||||
|
| 승인 응답 서명 | JWS/JWK 또는 COSE | 등록 기기의 private key로 승인 payload 서명 |
|
||||||
|
| 기기 내 사용자 확인 | WebAuthn/FIDO2/passkey, OS biometric | 앱 승인 전 사용자 검증 |
|
||||||
|
| 제한 장치 대안 | OAuth2 Device Authorization Grant | TV/CLI/키오스크형 입력 제한 장치에 응용 가능 |
|
||||||
|
| 요청 보안 강화 | Pushed Authorization Requests(PAR) | RP 인증 요청 변조/노출 축소 |
|
||||||
|
| 세션 관리 | OIDC Back-Channel Logout | RP 로그아웃 전파 |
|
||||||
|
| 토큰 취소 | OAuth2 Token Revocation | refresh token 무효화 |
|
||||||
|
|
||||||
|
## 10. 앱 기술 스택 제안
|
||||||
|
|
||||||
|
`baron-safe`는 Android와 iOS를 모두 지원해야 하므로, 초기 개발에서는 단일 코드베이스로 양 플랫폼을 동시에 지원하는 크로스플랫폼 프레임워크를 우선 검토하는 것이 현실적이다.
|
||||||
|
|
||||||
|
### 10.1 권장안: Flutter 기반 크로스플랫폼 앱
|
||||||
|
|
||||||
|
권장 기술 스택:
|
||||||
|
|
||||||
|
| 영역 | 권장 기술 | 적용 이유 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| App Framework | Flutter | Android/iOS 단일 코드베이스, 기존 `userfront`와 기술 친화성 |
|
||||||
|
| Language | Dart | Flutter 기본 언어 |
|
||||||
|
| Push Notification | Firebase Cloud Messaging + APNs 연동 | Android/iOS 공통 푸시 발송 관리 |
|
||||||
|
| Secure Storage | Android Keystore, iOS Keychain/Secure Enclave 연동 | 기기 private key 및 민감 토큰 보호 |
|
||||||
|
| Local Authentication | `local_auth` 계열 생체 인증 연동 | 승인 전 지문/Face ID/기기 PIN 확인 |
|
||||||
|
| Crypto Signing | JWS/JWK 또는 COSE 서명 라이브러리 | 승인 응답 서명 및 위변조 방지 |
|
||||||
|
| API 통신 | HTTPS + certificate pinning 검토 | 앱-서버 통신 보호 |
|
||||||
|
| 상태 관리 | Riverpod 또는 Bloc | 승인 요청/기기 등록/세션 상태 관리 |
|
||||||
|
| 배포 | Google Play Enterprise, Apple Business Manager/TestFlight | 내부/협력사 대상 앱 배포 관리 |
|
||||||
|
|
||||||
|
Flutter를 권장하는 이유:
|
||||||
|
|
||||||
|
- Android/iOS 공통 UI와 비즈니스 로직을 하나의 코드베이스로 관리할 수 있다.
|
||||||
|
- 로그인 승인 앱은 고성능 네이티브 UI보다 안정적인 API 통신, 푸시, 보안 저장소, 생체 인증 연동이 핵심이므로 Flutter와 잘 맞는다.
|
||||||
|
- Baron SSO의 `userfront`가 이미 Flutter 기반이므로 조직 내 학습 비용과 코드 패턴 재사용 측면에서 유리하다.
|
||||||
|
- 필요 시 플랫폼별 보안 기능은 Method Channel 또는 플러그인으로 네이티브 연동할 수 있다.
|
||||||
|
|
||||||
|
### 10.2 대안: React Native
|
||||||
|
|
||||||
|
React Native도 Android/iOS 공통 개발이 가능하다.
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- JavaScript/TypeScript 기반으로 웹 프론트엔드 인력과 협업이 쉽다.
|
||||||
|
- 푸시, 생체 인증, secure storage 생태계가 충분하다.
|
||||||
|
|
||||||
|
단점:
|
||||||
|
|
||||||
|
- 현재 Baron SSO의 사용자 프론트가 Flutter 기반이므로 기술 스택이 하나 더 늘어난다.
|
||||||
|
- 보안 저장소와 네이티브 crypto 연동 시 라이브러리 품질 검증이 필요하다.
|
||||||
|
|
||||||
|
### 10.3 대안: 네이티브 앱 분리 개발
|
||||||
|
|
||||||
|
Android는 Kotlin, iOS는 Swift로 각각 개발하는 방식이다.
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- Android Keystore, iOS Keychain/Secure Enclave, 생체 인증, 푸시 처리 등 플랫폼 보안 기능을 가장 직접적으로 제어할 수 있다.
|
||||||
|
- 장기적으로 고보안 앱을 만들기에는 가장 안정적인 선택이다.
|
||||||
|
|
||||||
|
단점:
|
||||||
|
|
||||||
|
- Android/iOS 개발과 테스트를 별도로 운영해야 한다.
|
||||||
|
- 초기 개발 속도와 유지보수 비용이 증가한다.
|
||||||
|
- 기능 동등성 관리가 어렵다.
|
||||||
|
|
||||||
|
### 10.4 공통 구현 원칙
|
||||||
|
|
||||||
|
프레임워크와 관계없이 다음 원칙은 반드시 지켜야 한다.
|
||||||
|
|
||||||
|
- Android와 iOS 모두 기기별 key pair를 앱 최초 등록 시 생성한다.
|
||||||
|
- private key는 서버로 전송하지 않고 Android Keystore 또는 iOS Keychain/Secure Enclave에 보관한다.
|
||||||
|
- push token은 알림 전달용으로만 사용하고 인증 수단으로 취급하지 않는다.
|
||||||
|
- 승인 응답은 `auth_req_id`, `nonce`, `decision`, `device_id`, `approved_at` 등을 포함해 서명한다.
|
||||||
|
- 앱 승인 전 OS 생체 인증 또는 앱 PIN을 요구한다.
|
||||||
|
- 앱이 탈옥/루팅, 디버그 빌드, 무결성 훼손 상태일 경우 고위험 RP 승인을 제한한다.
|
||||||
|
- Android는 Play Integrity API, iOS는 App Attest 또는 DeviceCheck 적용을 검토한다.
|
||||||
|
- 앱과 서버 API는 TLS를 기본으로 하고, 필요 시 certificate pinning을 적용한다.
|
||||||
|
|
||||||
|
### 10.5 배포 및 운영 방안
|
||||||
|
|
||||||
|
Baron SSO 사용자가 불특정 다수가 아니라 등록된 내부/외부 사용자라는 점을 고려하면, 앱 배포도 일반 공개 앱보다 관리형 배포가 적합하다.
|
||||||
|
|
||||||
|
권장 배포 방식:
|
||||||
|
|
||||||
|
| 대상 | Android | iOS |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 내부 임직원 | Managed Google Play 또는 사내 MDM | Apple Business Manager 또는 MDM |
|
||||||
|
| 협력사/고객사 | 제한 공개 테스트/엔터프라이즈 배포 검토 | Apple Business Manager Custom App 또는 TestFlight |
|
||||||
|
| 초기 파일럿 | Firebase App Distribution | TestFlight |
|
||||||
|
|
||||||
|
운영 고려사항:
|
||||||
|
|
||||||
|
- 기기 등록/해제 기능을 관리자 화면과 사용자 마이페이지에 제공한다.
|
||||||
|
- 분실 기기 즉시 차단 기능이 필요하다.
|
||||||
|
- 앱 버전 최소 요구사항을 서버에서 강제할 수 있어야 한다.
|
||||||
|
- 보안 정책 변경 시 구버전 앱의 승인을 제한할 수 있어야 한다.
|
||||||
|
|
||||||
|
## 11. 구현 방식 선택지
|
||||||
|
|
||||||
|
### 11.1 완전 CIBA 지원 방식
|
||||||
|
|
||||||
|
Baron SSO가 CIBA의 backchannel authentication endpoint와 CIBA grant type을 공식 지원한다.
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- 표준 정합성이 가장 높다.
|
||||||
|
- RP와의 장기적 상호운용성이 좋다.
|
||||||
|
- Poll/Ping/Push mode를 명확히 정의할 수 있다.
|
||||||
|
|
||||||
|
단점:
|
||||||
|
|
||||||
|
- Hydra에서 CIBA를 직접 지원하지 않는 경우 구현 범위가 커진다.
|
||||||
|
- discovery metadata, client registration, token grant 확장이 필요하다.
|
||||||
|
|
||||||
|
### 11.2 CIBA 스타일 내부 브로커 방식
|
||||||
|
|
||||||
|
Baron Backend가 CIBA와 유사한 승인 브로커 역할을 하고, 최종 로그인 완료는 기존 Hydra `AcceptLoginRequest`로 연결한다.
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- 현재 Baron SSO 구조에 적용하기 쉽다.
|
||||||
|
- 기존 OIDC Authorization Code + PKCE 흐름을 유지할 수 있다.
|
||||||
|
- 단계적 도입이 가능하다.
|
||||||
|
|
||||||
|
단점:
|
||||||
|
|
||||||
|
- 외부 표준 CIBA endpoint로 바로 공개되지는 않는다.
|
||||||
|
- 내부 API 명세와 보안 검증을 엄격히 관리해야 한다.
|
||||||
|
|
||||||
|
권장:
|
||||||
|
|
||||||
|
- 초기 도입은 11.2 방식이 현실적이다.
|
||||||
|
- 이후 RP 요구가 커지면 11.1 방식으로 확장한다.
|
||||||
|
|
||||||
|
### 11.3 Device Authorization Grant 응용 방식
|
||||||
|
|
||||||
|
키오스크, TV, CLI처럼 입력이 제한된 기기에는 OAuth2 Device Authorization Grant를 응용할 수 있다.
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- 표준 RFC가 명확하다.
|
||||||
|
- 사용자가 다른 기기에서 승인하는 모델이다.
|
||||||
|
|
||||||
|
단점:
|
||||||
|
|
||||||
|
- 사용자가 user code를 입력하는 UX가 기본 모델이다.
|
||||||
|
- 휴대폰 번호 기반 push 승인과는 CIBA보다 덜 직접적이다.
|
||||||
|
|
||||||
|
## 12. 도입 단계 제안
|
||||||
|
|
||||||
|
### Phase 1: 내부 승인 브로커 MVP
|
||||||
|
|
||||||
|
- `baron-safe` 기기 등록 모델 추가
|
||||||
|
- Android/iOS 공통 앱 MVP 구현
|
||||||
|
- 승인 요청 생성 API 추가
|
||||||
|
- 앱 요청 상세 조회 API 추가
|
||||||
|
- 앱 승인/거절 API 추가
|
||||||
|
- 요청 기기 poll API 추가
|
||||||
|
- Hydra `AcceptLoginRequest` 연계
|
||||||
|
- 감사 로그 추가
|
||||||
|
|
||||||
|
### Phase 2: 보안 강화
|
||||||
|
|
||||||
|
- 기기별 key pair 및 JWS 서명 검증
|
||||||
|
- Android Keystore, iOS Keychain/Secure Enclave 연동 강화
|
||||||
|
- OS 생체 인증 또는 앱 PIN 필수화
|
||||||
|
- Play Integrity API, App Attest/DeviceCheck 적용 검토
|
||||||
|
- nonce 재사용 방지
|
||||||
|
- rate limit 및 push fatigue 방어
|
||||||
|
- RP별 allowlist와 required ACR 정책
|
||||||
|
- 관리자 화면에서 기기 등록/해제 관리
|
||||||
|
|
||||||
|
### Phase 3: 표준 확장
|
||||||
|
|
||||||
|
- CIBA discovery metadata 검토
|
||||||
|
- backchannel authentication endpoint 공개 여부 검토
|
||||||
|
- `urn:openid:params:grant-type:ciba` 지원 검토
|
||||||
|
- Ping/Poll delivery mode 정식화
|
||||||
|
- PAR/JAR 적용 검토
|
||||||
|
|
||||||
|
## 13. 운영 정책 제안
|
||||||
|
|
||||||
|
### 13.1 인증 강도 구분
|
||||||
|
|
||||||
|
`baron-safe` 승인 로그인은 다음과 같이 별도 인증 강도로 관리한다.
|
||||||
|
|
||||||
|
| 인증 방식 | 예시 ACR | 설명 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 전화번호 단독 | `baron:phone:identifier-only` | 운영 로그인 비권장 |
|
||||||
|
| SMS OTP | `baron:phone:sms-otp` | 전화번호 소유 확인 |
|
||||||
|
| Baron Safe 앱 승인 | `baron:safe:app-approval` | 등록 앱 승인 |
|
||||||
|
| Baron Safe + 생체 인증 | `baron:safe:aal2` | 앱 승인 + 사용자 확인 |
|
||||||
|
| WebAuthn/passkey | `baron:webauthn:phishing-resistant` | 피싱 저항 인증 |
|
||||||
|
|
||||||
|
### 13.2 RP별 접근 정책
|
||||||
|
|
||||||
|
- 일반 RP: Baron Safe 앱 승인 허용
|
||||||
|
- 민감 RP: Baron Safe + 생체 인증 또는 WebAuthn 요구
|
||||||
|
- 관리자 RP: WebAuthn 또는 추가 MFA 요구
|
||||||
|
- 외부망 RP: IP/위치 이상 탐지 시 추가 인증 요구
|
||||||
|
|
||||||
|
### 13.3 감사 로그 항목
|
||||||
|
|
||||||
|
| 항목 | 설명 |
|
||||||
|
| --- | --- |
|
||||||
|
| auth_req_id | 승인 요청 ID |
|
||||||
|
| user_id | 대상 사용자 |
|
||||||
|
| client_id | 요청 RP |
|
||||||
|
| device_id | 승인 앱 기기 |
|
||||||
|
| request_ip | 요청 기기 IP |
|
||||||
|
| request_user_agent | 요청 기기 User-Agent |
|
||||||
|
| decision | approve/reject/expired |
|
||||||
|
| user_verification | biometric/pin/passkey/none |
|
||||||
|
| signature_valid | 서명 검증 결과 |
|
||||||
|
| completed_at | 로그인 완료 시각 |
|
||||||
|
|
||||||
|
## 14. 주요 위험과 대응
|
||||||
|
|
||||||
|
| 위험 | 설명 | 대응 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 푸시 피로 공격 | 공격자가 반복 승인 요청을 보내 사용자가 실수 승인 | rate limit, number matching, 반복 요청 차단 |
|
||||||
|
| 휴대폰 분실 | 등록 앱이 설치된 기기 분실 | 원격 기기 해제, 관리자 잠금, 생체 인증 필수 |
|
||||||
|
| 푸시 토큰 탈취 | 알림 토큰 유출 | push token은 인증 수단으로 사용하지 않음 |
|
||||||
|
| 앱 위조 | 공격자가 가짜 앱으로 승인 시도 | 기기 private key 서명 검증 |
|
||||||
|
| 앱 무결성 훼손 | 루팅/탈옥/디버그 환경에서 승인 조작 | Play Integrity, App Attest, 고위험 RP 제한 |
|
||||||
|
| 요청 변조 | RP 정보 또는 요청 ID 변조 | 서버 저장 요청 기준 검증, nonce, signature |
|
||||||
|
| 사용자 열람 | 전화번호 입력으로 가입 여부 추론 | 응답 메시지 일반화, rate limit |
|
||||||
|
| RP 오남용 | 허용되지 않은 RP가 승인 요청 생성 | client authentication, allowlist |
|
||||||
|
|
||||||
|
## 15. 팀장 보고용 최종 제안
|
||||||
|
|
||||||
|
`baron-safe` 앱 승인 방식은 "전화번호만으로 로그인"이 아니라 "전화번호로 사용자를 식별하고, 등록된 모바일 인증 장치에서 사용자가 승인하는 로그인"으로 정의해야 한다.
|
||||||
|
|
||||||
|
이렇게 정의하면 다음 장점이 있다.
|
||||||
|
|
||||||
|
- Baron SSO 사용자가 등록된 내부/외부 사용자로 제한되므로 앱 설치와 기기 등록을 보안 절차로 수용시키기 쉽다.
|
||||||
|
- 전화번호 단독 로그인의 비표준/저보증 문제를 해결한다.
|
||||||
|
- 사용자는 요청 RP와 요청 기기를 확인하고 승인할 수 있다.
|
||||||
|
- OIDC CIBA와 유사한 표준 모델로 설명 가능하다.
|
||||||
|
- 기존 Hydra 기반 OIDC Authorization Code + PKCE 흐름을 유지할 수 있다.
|
||||||
|
- 감사 로그, RP별 정책, 세션 관리와 자연스럽게 연결된다.
|
||||||
|
|
||||||
|
권장 구현 방향은 다음과 같다.
|
||||||
|
|
||||||
|
1. 1차로 Baron Backend가 CIBA 스타일 내부 승인 브로커 역할을 수행한다.
|
||||||
|
2. 앱은 Android/iOS 공통 지원을 위해 Flutter 기반으로 우선 검토한다.
|
||||||
|
3. `baron-safe` 앱은 기기별 key pair를 생성하고 승인 응답을 서명한다.
|
||||||
|
4. 앱 승인 전 생체 인증 또는 앱 PIN을 요구한다.
|
||||||
|
5. Baron SSO는 승인 검증 후 기존 Hydra `AcceptLoginRequest`로 OIDC 흐름을 완료한다.
|
||||||
|
6. 모든 승인 로그인에는 `amr`, `acr`, `device_id`, `auth_req_id`를 기록한다.
|
||||||
|
7. 향후 표준 CIBA endpoint 지원 여부를 검토한다.
|
||||||
|
|
||||||
|
최종적으로 이 방식은 기존 제안보다 보안성과 설명 가능성이 높다. 단, push 알림 자체를 인증 수단으로 오해해서는 안 되며, 반드시 등록 기기의 서명 검증과 사용자 확인 절차가 포함되어야 한다.
|
||||||
@@ -0,0 +1,418 @@
|
|||||||
|
# Baron Safe PWA/WebView 하이브리드 앱 추진 수정안
|
||||||
|
|
||||||
|
작성일: 2026-06-30
|
||||||
|
|
||||||
|
## 1. 목적
|
||||||
|
|
||||||
|
본 문서는 기존 `Baron Safe 기반 휴대폰 승인 로그인 제안안`을 실제 앱 개발로 진행하기 위한 실행 수정안이다.
|
||||||
|
|
||||||
|
Baron SSO의 기존 `userfront`는 그대로 유지하고, 그 안에서 활용 가능한 화면 자산, UX 흐름, Flutter 구현 패턴을 Baron Safe 앱 개발 시 가져다 쓰는 방향으로 구상한다. Baron Safe 앱 자체는 PWA 또는 WebView/하이브리드 기반 모바일 앱으로 우선 검토한다.
|
||||||
|
|
||||||
|
이번 수정안의 핵심은 Baron Safe의 기본 흐름을 `전화번호 입력 시 선승인 후확인 후차단` 방식으로 정의하는 것이다. 즉 사용자가 Baron SSO에서 휴대전화번호를 입력하면 일단 기존 로그인 흐름을 진행하고, Baron Safe 앱에서는 해당 로그인 이력을 즉시 확인할 수 있게 하며, 사용자가 본인 요청이 아니라고 판단하면 앱에서 차단하거나 세션을 해제하는 구조를 기본안으로 둔다.
|
||||||
|
|
||||||
|
## 2. 추진 배경
|
||||||
|
|
||||||
|
현재 적용 중인 휴대전화번호 기반 로그인은 사용자 편의성이 높다. 다만 전화번호만으로는 사용자가 실제 번호 소유자인지, 해당 로그인 요청이 본인 의사에 의한 것인지 확인하기 어렵다.
|
||||||
|
|
||||||
|
이를 보완하기 위해 사용자의 휴대폰에 `Baron Safe` 앱을 설치하고, 해당 앱을 사용자 계정에 연결된 확인 장치로 사용한다. 사용자는 PC, 브라우저, 키오스크 또는 RP 서비스에서 Baron SSO 로그인을 진행하고, 휴대폰 Baron Safe 앱에서 최근 로그인 이력, 연결 앱 현황, 접속 기기, 접속 시간, IP 정보를 확인한다. 본인이 요청한 로그인이 아니거나 의심스러운 경우에는 앱에서 해당 세션 또는 RP 연결을 차단한다.
|
||||||
|
|
||||||
|
이 방식은 엄격한 사전 승인형 인증보다는 사용성 중심의 사후 확인 및 차단 모델에 가깝다. Baron SSO의 현재 `userfront`에 이미 구현된 `나의 App 현황`, `접속이력`, `현재 세션`, `해지됨` 화면과도 잘 맞는다.
|
||||||
|
|
||||||
|
또한 SMS 인증번호를 발송하는 방식이 아니므로, 기존 문자 인증 방식에서 발생하던 문자 발송 비용을 줄일 수 있다.
|
||||||
|
|
||||||
|
## 3. 기본 방향
|
||||||
|
|
||||||
|
`Baron Safe`는 Flutter 기반 설치형 모바일 앱, PWA, WebView/하이브리드 앱 중 하나로 구현할 수 있다.
|
||||||
|
|
||||||
|
현재 Baron SSO `userfront`는 이미 모바일 화면을 고려한 포털 UI, 연결 앱 현황, 접속 이력, QR 진입 흐름 등을 포함하고 있다. 따라서 기존 `userfront`를 직접 변경하거나 앱으로 전환하기보다는, Baron Safe 앱에서 필요한 화면 흐름과 구현 패턴을 가져와 활용하고, 푸시/생체 인증/보안 저장소 같은 기능은 Baron Safe 앱의 네이티브 또는 Flutter 플러그인 계층으로 보강하는 하이브리드 구조가 적절하다.
|
||||||
|
|
||||||
|
기본 방향은 다음과 같다.
|
||||||
|
|
||||||
|
- Android와 iOS를 모두 지원한다.
|
||||||
|
- 기존 Baron `userfront`는 그대로 두고, Baron Safe 앱 개발 시 활용 가능한 Flutter 기술 스택과 UI/상태관리 패턴을 재사용한다.
|
||||||
|
- 전화번호는 인증 수단이 아니라 사용자 식별자로 사용한다.
|
||||||
|
- 기본 흐름은 `선승인 후확인 후차단`으로 둔다.
|
||||||
|
- 사용자는 Baron Safe 앱에서 최근 로그인, 연결 앱, 활성 세션을 확인하고 필요 시 차단한다.
|
||||||
|
- 푸시 알림은 로그인 발생 사실 또는 위험 이벤트를 사용자에게 알려주는 채널로 사용한다.
|
||||||
|
- 향후 고위험 RP 또는 관리자 로그인에는 `요청 시 승인/차단` 방식을 선택 적용할 수 있다.
|
||||||
|
- 확인, 차단, 해제, 만료, 실패는 모두 감사 로그로 남긴다.
|
||||||
|
|
||||||
|
## 4. 대상 앱 정의
|
||||||
|
|
||||||
|
앱 명칭은 가칭 `Baron Safe`로 한다.
|
||||||
|
|
||||||
|
주요 역할:
|
||||||
|
|
||||||
|
- 사용자 휴대폰을 Baron SSO 계정 확인 장치로 등록
|
||||||
|
- 최근 로그인 및 현재 세션 확인
|
||||||
|
- 연결된 RP 앱 현황 확인
|
||||||
|
- 의심 세션 차단 또는 로그아웃
|
||||||
|
- RP 연결 해제 또는 차단
|
||||||
|
- 로그인 발생, 신규 기기 접속, 고위험 이벤트 푸시 수신
|
||||||
|
- 향후 고위험 로그인에 대한 사전 승인/차단
|
||||||
|
- 기기 분실, 교체, 해제 대응
|
||||||
|
|
||||||
|
Baron Safe 앱은 브라우저에서 열리는 단순 웹페이지가 아니라 휴대폰에서 앱처럼 사용하는 모바일 앱이다. 다만 초기 구현은 기존 `userfront`의 화면 자산을 활용하는 PWA 또는 WebView/하이브리드 방식으로 검토한다.
|
||||||
|
|
||||||
|
## 5. 지원 플랫폼 및 배포
|
||||||
|
|
||||||
|
| 구분 | Android | iOS |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 앱 형식 | APK 또는 AAB | IPA |
|
||||||
|
| 배포 방식 | Google Play, 사내 배포, MDM, APK 배포 검토 | TestFlight, App Store, Apple Business Manager, MDM 검토 |
|
||||||
|
| 푸시 | Firebase Cloud Messaging | APNs, Firebase 연동 가능 |
|
||||||
|
| 보안 저장소 | Android Keystore | iOS Keychain, Secure Enclave 검토 |
|
||||||
|
| 사용자 확인 | 지문, 얼굴, 기기 PIN | Face ID, Touch ID, 기기 암호 |
|
||||||
|
|
||||||
|
초기 PoC 단계에서는 Android 우선 검증이 현실적이다. 단, 설계와 코드 구조는 iOS 확장을 전제로 둔다.
|
||||||
|
|
||||||
|
## 6. 기술 스택
|
||||||
|
|
||||||
|
### 6.1 기존 userfront 기반
|
||||||
|
|
||||||
|
현재 `userfront`에서 확인된 주요 기술은 다음과 같다.
|
||||||
|
|
||||||
|
| 영역 | 현재 기술 |
|
||||||
|
| --- | --- |
|
||||||
|
| Framework | Flutter |
|
||||||
|
| Language | Dart |
|
||||||
|
| 상태 관리 | flutter_riverpod |
|
||||||
|
| Routing | go_router |
|
||||||
|
| HTTP 통신 | http |
|
||||||
|
| 다국어 | easy_localization |
|
||||||
|
| 로컬 설정 | shared_preferences |
|
||||||
|
| QR/스캔 관련 | qr_flutter, mobile_scanner |
|
||||||
|
| 이미지/SVG | flutter_svg |
|
||||||
|
| 로깅 | logging, logger |
|
||||||
|
|
||||||
|
Baron Safe 앱은 이 구성을 참고하여 필요한 부분을 가져가되, `userfront` 원본은 그대로 유지한다. 모바일 앱에 필요한 푸시, 생체 인증, 보안 저장소 기능은 Baron Safe 앱 쪽에 별도로 추가한다.
|
||||||
|
|
||||||
|
### 6.2 추가 검토 기술
|
||||||
|
|
||||||
|
| 영역 | 권장 기술 | 용도 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Push Notification | firebase_messaging, APNs 연동 | 로그인 발생, 신규 기기 접속, 차단 필요 이벤트 알림 |
|
||||||
|
| Secure Storage | flutter_secure_storage 또는 플랫폼 Method Channel | device id, refresh token, 민감 설정 저장 |
|
||||||
|
| Private Key Storage | Android Keystore, iOS Keychain/Secure Enclave | 향후 승인 응답 서명용 private key 보호 |
|
||||||
|
| Local Authentication | local_auth | 차단/해제/고위험 승인 전 지문, Face ID, 기기 PIN 확인 |
|
||||||
|
| Crypto Signing | JWS/JWK 또는 COSE 지원 라이브러리, 필요 시 네이티브 연동 | 향후 승인/차단 요청 위변조 방지 |
|
||||||
|
| API Client | 기존 http 유지 또는 dio 검토 | 앱-SSO API 통신 |
|
||||||
|
| State Management | flutter_riverpod 유지 | 세션, 연결 앱, 알림 상태 관리 |
|
||||||
|
| Routing | go_router 유지 | 홈, 세션 상세, 차단, 설정 화면 전환 |
|
||||||
|
| Deep Link | app_links 검토 | 푸시 알림에서 특정 세션/앱 상세로 진입 |
|
||||||
|
| App Integrity | Play Integrity API, DeviceCheck/App Attest 검토 | 위변조 앱과 위험 기기 탐지 |
|
||||||
|
|
||||||
|
### 6.3 PWA/WebView/하이브리드 방식 검토
|
||||||
|
|
||||||
|
기존 구상은 `userfront`의 활용 가능한 부분을 Baron Safe 앱에서 재사용하여 PWA 또는 WebView/하이브리드 방식으로 앱처럼 제공하는 방향으로 볼 수 있다. 이 방식은 현재 구현된 모바일 포털 화면과 흐름을 참고할 수 있으므로 초기 개발 속도와 화면 일관성 측면에서 장점이 있다.
|
||||||
|
|
||||||
|
| 방식 | 설명 | 장점 | 주의점 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| PWA | 웹앱을 브라우저 또는 홈 화면 설치 형태로 사용하는 방식 | 배포가 쉽고 기존 `userfront`의 화면/패턴 활용성이 높음 | iOS/Android별 푸시, 백그라운드 동작, 보안 저장소 제약 검토 필요 |
|
||||||
|
| WebView 앱 | Android/iOS 앱 안에 Baron Safe용 웹 화면을 띄우는 방식 | 앱 설치형 UX 제공, 기존 `userfront`의 화면 흐름 활용 가능 | 민감 기능은 WebView JS가 아니라 네이티브 브리지에서 처리해야 함 |
|
||||||
|
| 하이브리드 앱 | 주요 화면은 WebView, 푸시/생체인증/보안저장은 네이티브 또는 Flutter 플러그인으로 처리 | 개발 속도와 앱 기능을 절충 가능 | 웹-네이티브 메시지 경계와 인증 토큰 전달 방식 설계 필요 |
|
||||||
|
| 순수 Flutter 앱 | Baron Safe 전용 화면과 기능을 Flutter로 별도 구현 | 보안 기능과 앱 UX 제어가 가장 명확함 | 기존 `userfront` 화면 재사용성이 낮고 초기 구현량 증가 |
|
||||||
|
|
||||||
|
현 시점의 현실적인 권장안은 하이브리드 방식이다.
|
||||||
|
|
||||||
|
- `userfront`의 연결 앱 현황, 접속 이력, QR 화면, 기본 포털 UI 중 Baron Safe에 필요한 부분은 Baron Safe 앱 화면으로 활용한다.
|
||||||
|
- 로그인 발생 알림은 FCM/APNs 기반 네이티브 푸시로 처리한다.
|
||||||
|
- 차단, 연결 해제, 고위험 승인 전 사용자 확인은 Android/iOS 네이티브 또는 Flutter `local_auth` 계층에서 처리한다.
|
||||||
|
- 민감 토큰 저장과 향후 기기 서명은 Android Keystore, iOS Keychain/Secure Enclave 계층에서 처리한다.
|
||||||
|
- WebView 화면은 정보 표시와 사용자 인터랙션을 담당하되, 실제 민감 기능은 네이티브 계층에 위임한다.
|
||||||
|
|
||||||
|
## 7. 주요 기능 범위
|
||||||
|
|
||||||
|
### 7.1 1단계 PoC
|
||||||
|
|
||||||
|
목표는 전체 보안 완성보다 `전화번호 로그인 후 Baron Safe에서 확인 및 차단` 흐름을 검증하는 것이다.
|
||||||
|
|
||||||
|
기능:
|
||||||
|
|
||||||
|
- 앱 설치 및 실행
|
||||||
|
- 사용자 로그인 또는 등록 코드 입력
|
||||||
|
- 기기 등록 요청
|
||||||
|
- device id 발급
|
||||||
|
- push token 등록
|
||||||
|
- 최근 로그인/현재 세션 목록 조회
|
||||||
|
- 연결 RP 앱 현황 조회
|
||||||
|
- 세션 상세 표시
|
||||||
|
- 의심 세션 차단 또는 로그아웃
|
||||||
|
- RP 연결 해제 또는 차단
|
||||||
|
- 로그인 발생 알림 Mock 또는 수동 refresh
|
||||||
|
|
||||||
|
### 7.2 2단계 보안형 MVP
|
||||||
|
|
||||||
|
기능:
|
||||||
|
|
||||||
|
- 실제 FCM/APNs 푸시 연동
|
||||||
|
- 신규 기기 로그인, 외부망 접속, 고위험 RP 접속 알림
|
||||||
|
- 차단/해제 전 생체 인증 또는 앱 PIN 수행
|
||||||
|
- 차단 요청 payload 서명
|
||||||
|
- 서버에서 public key로 서명 검증
|
||||||
|
- 세션 강제 종료, RP 연결 해제, refresh token 무효화
|
||||||
|
- 차단/해제/확인/만료 감사 로그
|
||||||
|
- 기기 해제 및 재등록
|
||||||
|
|
||||||
|
### 7.3 3단계 운영 확장
|
||||||
|
|
||||||
|
기능:
|
||||||
|
|
||||||
|
- 다중 기기 등록
|
||||||
|
- 기기 분실 신고 및 관리자 강제 해제
|
||||||
|
- RP별 인증 강도 정책
|
||||||
|
- 위험 기반 사전 승인
|
||||||
|
- number matching 또는 request code
|
||||||
|
- FIDO2/passkey 연계 검토
|
||||||
|
- Play Integrity, App Attest 기반 앱 무결성 검토
|
||||||
|
- 사내/협력사 배포 체계 정립
|
||||||
|
|
||||||
|
## 8. 사용자 흐름
|
||||||
|
|
||||||
|
### 8.1 최초 기기 등록
|
||||||
|
|
||||||
|
1. 사용자가 기존 Baron SSO 방식으로 로그인한다.
|
||||||
|
2. 사용자 설정 화면에서 `Baron Safe 기기 등록`을 선택한다.
|
||||||
|
3. 서버가 등록용 QR 또는 등록 코드를 생성한다.
|
||||||
|
4. 사용자가 Baron Safe 앱에서 QR 또는 코드를 입력한다.
|
||||||
|
5. 앱이 기기 정보를 생성하고 push token을 발급받는다.
|
||||||
|
6. 앱이 필요 시 기기 key pair를 생성한다.
|
||||||
|
7. 앱은 device id, push token, platform, public key를 서버에 등록한다.
|
||||||
|
8. 서버는 해당 기기를 사용자 계정에 연결한다.
|
||||||
|
|
||||||
|
### 8.2 기본 흐름: 전화번호 입력 후 선승인, 확인 후 차단
|
||||||
|
|
||||||
|
1. 사용자가 Baron SSO 또는 RP 로그인 화면에서 휴대전화번호를 입력한다.
|
||||||
|
2. Baron SSO가 전화번호를 정규화하고 등록 사용자를 조회한다.
|
||||||
|
3. Baron SSO가 기존 정책에 따라 로그인을 진행한다.
|
||||||
|
4. 로그인 성공 시 Hydra/Kratos 세션과 RP 세션이 생성된다.
|
||||||
|
5. Baron SSO가 해당 로그인 이벤트를 감사 로그와 세션 목록에 기록한다.
|
||||||
|
6. Baron SSO가 등록된 Baron Safe 앱으로 로그인 발생 알림을 보낸다.
|
||||||
|
7. 사용자는 Baron Safe 앱에서 요청 서비스, 접속 기기, IP, 시간, 인증수단을 확인한다.
|
||||||
|
8. 본인 요청이 맞으면 별도 조치 없이 유지한다.
|
||||||
|
9. 본인 요청이 아니거나 의심스러우면 앱에서 `차단`, `세션 종료`, `RP 연결 해제`를 선택한다.
|
||||||
|
10. 앱은 차단/해제 요청 전 생체 인증 또는 앱 PIN을 요구한다.
|
||||||
|
11. 서버는 차단 요청을 검증한 뒤 해당 세션을 종료하고 필요 시 RP refresh token을 무효화한다.
|
||||||
|
12. 차단 결과와 후속 조치는 Baron Safe 앱과 감사 로그에 기록된다.
|
||||||
|
|
||||||
|
이 흐름은 사용자 편의성을 우선하면서, 현재 `userfront`에 구현된 앱 현황/접속 이력 화면을 Baron Safe 앱의 핵심 경험으로 활용할 수 있다.
|
||||||
|
|
||||||
|
### 8.3 대안 흐름 비교
|
||||||
|
|
||||||
|
Baron Safe 앱의 사용자 경험은 기본안 하나로만 고정하지 않고, 보안 수준과 RP 위험도에 따라 몇 가지 대안을 비교한 뒤 단계적으로 적용하는 것이 좋다.
|
||||||
|
|
||||||
|
| 안 | 방식 | 설명 | 장점 | 단점 | 적합한 용도 |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| 기본안 | 선승인 후확인 후차단 | 전화번호 입력 후 기존 로그인은 진행하고, Baron Safe에서 사용자가 최근 로그인과 세션을 확인한 뒤 의심 시 차단한다. | UX가 가장 간단하고 현재 `userfront` 화면과 잘 맞음. 반복 로그인 피로가 적음. 문자 발송 비용이 들지 않음. PoC가 빠름. | 비정상 로그인이 잠시라도 성립될 수 있음. 사후 차단이므로 고위험 RP에는 보완 필요. | 일반 업무 RP, 내부 포털, 저위험 반복 로그인 |
|
||||||
|
| 2안 | 로그인 요청 시 승인/차단 | 로그인 요청이 발생할 때마다 Baron Safe 앱으로 알림을 보내고, 사용자가 승인해야 로그인이 완료된다. | 보안성이 높고 사용자 승인 근거가 명확함. 오인 로그인 방지에 강함. | 매번 승인이 필요해 번거로움. 푸시 안정성과 앱 응답성이 중요함. | 관리자 기능, 외부망 접속, 고위험 RP |
|
||||||
|
| 3안 | 최초 1회 승인 후 기간 내 자동 허용 | RP 또는 기기별로 최초 1회만 Baron Safe 승인을 받고, 이후 일정 기간은 자동 허용한다. | 보안성과 편의성을 절충할 수 있음. 반복 업무에 적합함. | 신뢰 기간, 기기 변경, 위험 조건 정책이 필요함. | 일반 업무 RP, 동일 기기 반복 로그인 |
|
||||||
|
| 4안 | 위험 기반 선택 승인 | 평소와 같은 기기/위치/RP는 기본안으로 처리하고, 신규 기기/IP/고위험 RP일 때만 사전 승인을 요구한다. | 사용자 피로를 줄이면서 위험 상황에는 강한 인증을 적용할 수 있음. | 위험 판단 로직과 정책 관리가 필요함. 초기 구현 난도가 높음. | 운영 단계의 장기 목표 |
|
||||||
|
| 5안 | QR/코드 매칭 승인 | 로그인 화면에 숫자 코드 또는 QR을 표시하고, 앱에서 같은 코드인지 확인한 뒤 승인한다. | 푸시 피로 공격과 오인 승인을 줄일 수 있음. 사용자가 요청 기기를 더 명확히 확인함. | 사용자가 코드를 비교해야 하므로 UX가 복잡해짐. | 키오스크, 공용 PC, 관리자 로그인 |
|
||||||
|
|
||||||
|
### 8.4 권장 적용 순서
|
||||||
|
|
||||||
|
초기 PoC에서는 기본안을 우선 구현한다.
|
||||||
|
|
||||||
|
| 단계 | 적용 방식 | 설명 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| PoC | 기본안 중심 | 전화번호 로그인 후 Baron Safe에서 최근 로그인/세션/RP 연결을 확인하고 차단하는 흐름 검증 |
|
||||||
|
| MVP | 기본안 + 2안 일부 | 일반 RP는 선승인 후확인 후차단, 고위험 RP 또는 신규 기기는 요청 시 승인/차단 적용 |
|
||||||
|
| 운영 | 3안 + 4안 + 5안 확장 | 신뢰 기간, 위험 기반 정책, QR/코드 매칭으로 보안 수준 세분화 |
|
||||||
|
|
||||||
|
이 구조를 사용하면 팀장 구상인 PWA/WebView 기반 화면 재사용과 현재 개발된 `userfront` 화면을 자연스럽게 활용하면서, Baron Safe의 보안 수준을 단계적으로 높일 수 있다.
|
||||||
|
|
||||||
|
## 9. 주요 화면 초안
|
||||||
|
|
||||||
|
| 화면 | 설명 |
|
||||||
|
| --- | --- |
|
||||||
|
| 온보딩 화면 | Baron Safe 앱 소개 및 시작 |
|
||||||
|
| 기기 등록 화면 | QR 스캔 또는 등록 코드 입력 |
|
||||||
|
| 등록 완료 화면 | 등록된 사용자/기기명 표시 |
|
||||||
|
| 홈 화면 | 최근 로그인, 현재 세션, 연결 앱 요약 |
|
||||||
|
| 접속 이력 화면 | 로그인 시간, IP, 기기, 브라우저, 인증수단 표시 |
|
||||||
|
| 세션 상세 화면 | 세션 ID, RP, 접속 환경, 상태, 차단 버튼 표시 |
|
||||||
|
| 연결 앱 현황 화면 | 연동 RP 목록, 최근 인증, 연결 해제/차단 |
|
||||||
|
| 차단 확인 화면 | 생체 인증 또는 앱 PIN 후 차단 실행 |
|
||||||
|
| 차단 완료 화면 | 세션 종료 또는 RP 연결 해제 결과 표시 |
|
||||||
|
| 고위험 승인 화면 | 향후 요청 시 승인/차단 방식 적용 시 사용 |
|
||||||
|
| 보안 설정 화면 | 앱 PIN, 생체 인증 사용 여부, 등록 기기 관리 |
|
||||||
|
|
||||||
|
## 10. Backend/API 필요 항목
|
||||||
|
|
||||||
|
### 10.1 기기 등록 API
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /api/v1/baron-safe/devices/register
|
||||||
|
```
|
||||||
|
|
||||||
|
요청 항목:
|
||||||
|
|
||||||
|
- registration_code 또는 registration_token
|
||||||
|
- platform
|
||||||
|
- device_name
|
||||||
|
- push_token
|
||||||
|
- public_key
|
||||||
|
- app_version
|
||||||
|
|
||||||
|
응답 항목:
|
||||||
|
|
||||||
|
- device_id
|
||||||
|
- registered_at
|
||||||
|
- user_display_name
|
||||||
|
|
||||||
|
### 10.2 로그인/세션 이력 조회 API
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /api/v1/baron-safe/sessions
|
||||||
|
```
|
||||||
|
|
||||||
|
응답 항목:
|
||||||
|
|
||||||
|
- session_id
|
||||||
|
- client_id
|
||||||
|
- client_name
|
||||||
|
- authenticated_at
|
||||||
|
- request_ip_address
|
||||||
|
- user_agent
|
||||||
|
- auth_method
|
||||||
|
- status
|
||||||
|
- is_current
|
||||||
|
|
||||||
|
### 10.3 연결 앱 조회 API
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /api/v1/baron-safe/linked-apps
|
||||||
|
```
|
||||||
|
|
||||||
|
응답 항목:
|
||||||
|
|
||||||
|
- client_id
|
||||||
|
- client_name
|
||||||
|
- linked_at
|
||||||
|
- last_authenticated_at
|
||||||
|
- status
|
||||||
|
|
||||||
|
### 10.4 세션 차단/종료 API
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /api/v1/baron-safe/sessions/{sessionId}/block
|
||||||
|
```
|
||||||
|
|
||||||
|
요청 항목:
|
||||||
|
|
||||||
|
- device_id
|
||||||
|
- reason
|
||||||
|
- user_presence
|
||||||
|
- user_verification
|
||||||
|
- decided_at
|
||||||
|
- signature
|
||||||
|
|
||||||
|
응답 항목:
|
||||||
|
|
||||||
|
- status
|
||||||
|
- terminated_session_id
|
||||||
|
- revoked_tokens
|
||||||
|
|
||||||
|
### 10.5 RP 연결 해제/차단 API
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /api/v1/baron-safe/linked-apps/{clientId}/block
|
||||||
|
```
|
||||||
|
|
||||||
|
요청 항목:
|
||||||
|
|
||||||
|
- device_id
|
||||||
|
- reason
|
||||||
|
- user_presence
|
||||||
|
- user_verification
|
||||||
|
- decided_at
|
||||||
|
- signature
|
||||||
|
|
||||||
|
응답 항목:
|
||||||
|
|
||||||
|
- status
|
||||||
|
- client_id
|
||||||
|
- blocked_at
|
||||||
|
|
||||||
|
### 10.6 고위험 로그인 승인 API
|
||||||
|
|
||||||
|
향후 2안 또는 4안을 적용할 경우 다음 API를 추가한다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /api/v1/auth/baron-safe/login/init
|
||||||
|
GET /api/v1/auth/baron-safe/requests/{authReqId}
|
||||||
|
POST /api/v1/auth/baron-safe/requests/{authReqId}/decision
|
||||||
|
POST /api/v1/auth/baron-safe/login/poll
|
||||||
|
```
|
||||||
|
|
||||||
|
## 11. 보안 고려사항
|
||||||
|
|
||||||
|
필수 사항:
|
||||||
|
|
||||||
|
- 앱이 휴대폰 번호를 자동으로 가져오는 방식에 의존하지 않는다.
|
||||||
|
- 전화번호는 사용자 식별자이며 인증 수단이 아니다.
|
||||||
|
- 기본안은 사후 확인/차단 모델이므로 고위험 RP에는 추가 보완책을 둔다.
|
||||||
|
- 차단/해제 같은 민감 조작 전에는 생체 인증 또는 앱 PIN을 수행한다.
|
||||||
|
- private key는 서버로 전송하지 않는다.
|
||||||
|
- 차단/승인 응답은 기기 private key로 서명하는 방향을 검토한다.
|
||||||
|
- 푸시 메시지에는 민감정보를 최소화한다.
|
||||||
|
- 접속 이력과 세션 상세는 등록된 기기 또는 인증된 사용자만 조회할 수 있어야 한다.
|
||||||
|
- 전화번호 미등록 여부와 기기 미등록 여부가 외부에 노출되지 않도록 응답 메시지를 일반화한다.
|
||||||
|
- 반복 로그인 요청 또는 반복 차단 요청에는 rate limit을 적용한다.
|
||||||
|
- 모든 로그인, 확인, 차단, 해제, 실패 이벤트를 감사 로그로 남긴다.
|
||||||
|
- 차단 시 Hydra/Kratos 세션, RP 세션, refresh token 무효화 범위를 명확히 정의한다.
|
||||||
|
|
||||||
|
## 12. 권장 추진안
|
||||||
|
|
||||||
|
초기에는 기존 `userfront`는 그대로 두고, 그 안에서 활용 가능한 화면 자산과 구현 패턴을 Baron Safe 앱에서 가져다 쓰는 WebView/하이브리드 방식을 우선 검토한다. 보안 기능은 네이티브 또는 Flutter 플러그인 계층으로 분리하는 것을 권장한다.
|
||||||
|
|
||||||
|
이유는 다음과 같다.
|
||||||
|
|
||||||
|
- 현재 `userfront`는 이미 모바일 포털 화면과 연결 앱 현황, 접속 이력, QR 진입 등 Baron Safe와 가까운 화면 흐름을 일부 포함하고 있다.
|
||||||
|
- 기본안인 선승인 후확인 후차단은 현재 화면 구조와 가장 잘 맞는다.
|
||||||
|
- 초기 PoC에서는 화면을 전부 새로 만들기보다 기존 `userfront`의 활용 가능한 부분을 가져오는 편이 빠르다.
|
||||||
|
- 다만 푸시, 생체 인증, private key, 보안 저장소, 앱 무결성은 WebView 내부 웹 코드에만 맡기기 어렵다.
|
||||||
|
- 따라서 `userfront`는 유지하고, Baron Safe 앱은 활용 가능한 화면/패턴을 가져오며, 인증 장치 기능은 네이티브/Flutter 앱 계층이 담당하도록 역할을 나누는 것이 적절하다.
|
||||||
|
|
||||||
|
추진 순서:
|
||||||
|
|
||||||
|
1. 기존 `userfront` 화면 중 Baron Safe 앱에서 활용 가능한 화면, 패턴, API 흐름을 식별한다.
|
||||||
|
2. PWA 방식과 WebView 앱 방식 중 PoC에 적합한 방식을 결정한다.
|
||||||
|
3. Backend에 세션 이력 조회, 연결 앱 조회, 세션 차단, RP 연결 차단 API를 추가한다.
|
||||||
|
4. Mock push 또는 수동 refresh 방식으로 기본안 PoC를 완성한다.
|
||||||
|
5. Android FCM과 iOS APNs 기반 로그인 발생 알림을 연동한다.
|
||||||
|
6. 차단/해제 전 생체 인증과 기기 보안 저장소를 네이티브/Flutter 플러그인 계층으로 연동한다.
|
||||||
|
7. 고위험 RP에 한해 요청 시 승인/차단 API를 추가한다.
|
||||||
|
8. 운영 배포 방식과 장기적으로 완전 별도 `baron_safe_app`으로 갈지 여부를 확정한다.
|
||||||
|
|
||||||
|
## 13. 결론
|
||||||
|
|
||||||
|
Baron Safe 앱의 기본 흐름은 `사용자가 Baron SSO에서 전화번호 입력 → 로그인 선승인 → Baron Safe에서 확인 → 의심 시 차단`으로 잡는 것이 현재 개발된 `userfront` 화면과 가장 잘 맞는다.
|
||||||
|
|
||||||
|
이 방식은 사용자 편의성과 PoC 속도 측면에서 장점이 크다. 다만 사후 차단 모델이므로 관리자 기능, 외부망 접속, 고위험 RP에는 요청 시 승인/차단, 위험 기반 승인, QR/코드 매칭 같은 대안을 단계적으로 적용하는 것이 바람직하다.
|
||||||
|
|
||||||
|
## 14. 용어 주석
|
||||||
|
|
||||||
|
### PoC
|
||||||
|
|
||||||
|
PoC는 `Proof of Concept`의 약자이며, 한국어로는 개념 검증 또는 가능성 검증 정도로 볼 수 있다.
|
||||||
|
|
||||||
|
본 문서에서 PoC는 Baron Safe 앱의 전체 보안 기능과 운영 기능을 모두 완성하기 전에, 핵심 흐름이 실제로 가능한지 먼저 확인하는 단계를 의미한다.
|
||||||
|
|
||||||
|
### JWS/JWK
|
||||||
|
|
||||||
|
JWS는 `JSON Web Signature`의 약자이며, JSON 형태의 데이터가 중간에 위조되거나 변경되지 않았음을 서명으로 증명하는 표준이다.
|
||||||
|
|
||||||
|
JWK는 `JSON Web Key`의 약자이며, 공개키 또는 비밀키 정보를 JSON 형식으로 표현하는 표준이다.
|
||||||
|
|
||||||
|
### COSE 기반
|
||||||
|
|
||||||
|
COSE는 `CBOR Object Signing and Encryption`의 약자이며, 데이터를 서명하거나 암호화하기 위한 표준 형식이다.
|
||||||
|
|
||||||
|
JWS/JWK가 JSON 기반이라면, COSE는 CBOR라는 더 작고 기계 처리에 적합한 데이터 형식을 기반으로 한다. WebAuthn, FIDO2, 패스키 같은 인증 기술에서 자주 사용된다.
|
||||||
|
|
||||||
|
### 앱 private key
|
||||||
|
|
||||||
|
앱 private key는 Baron Safe 앱이 설치된 특정 휴대폰 안에서 생성되고 보관되는 비밀키를 의미한다.
|
||||||
|
|
||||||
|
이 키는 서버로 전송하지 않으며, Android Keystore 또는 iOS Keychain/Secure Enclave 같은 기기 보안 저장소에 보관하는 것이 원칙이다.
|
||||||
@@ -0,0 +1,615 @@
|
|||||||
|
# Baron Safe 하이브리드 앱 개발 정책
|
||||||
|
|
||||||
|
작성일: 2026-06-30
|
||||||
|
|
||||||
|
## 1. 목적
|
||||||
|
|
||||||
|
본 문서는 Baron Safe 앱을 PWA/WebView 하이브리드 방식으로 개발하기 위한 준비사항과 단계별 진행 순서를 정의한다.
|
||||||
|
|
||||||
|
기본 방향은 기존 Baron SSO `userfront`를 그대로 유지하면서, 그 안에서 활용 가능한 연결 앱 현황, 접속 이력, QR 화면, 기본 포털 UI, Flutter 구현 패턴을 Baron Safe 앱에서 가져다 쓰는 것이다. 단, 푸시, 생체 인증, 민감 토큰 저장, 기기 서명 같은 보안 기능은 WebView 내부 웹 코드에 맡기지 않고 Android/iOS 네이티브 또는 Flutter 플러그인 계층에서 처리한다.
|
||||||
|
|
||||||
|
## 2. 개발 원칙
|
||||||
|
|
||||||
|
### 2.1 userfront 활용 원칙
|
||||||
|
|
||||||
|
- 기존 `userfront` 원본은 Baron Safe 앱 개발을 위해 직접 변경하지 않는다.
|
||||||
|
- Baron Safe 앱에서 필요한 화면 흐름, UI 패턴, 상태관리 방식, API 호출 방식을 식별하여 활용한다.
|
||||||
|
- 연결 앱 현황, 접속 이력, QR 화면, 기본 포털 UI 중 Baron Safe에 적합한 부분을 앱 화면으로 재구성한다.
|
||||||
|
- Baron Safe 앱은 `userfront`의 단순 복제본이 아니라, 보안 확인 및 차단에 특화된 모바일 앱으로 정의한다.
|
||||||
|
|
||||||
|
### 2.2 WebView 역할
|
||||||
|
|
||||||
|
- WebView 화면은 정보 표시와 사용자 인터랙션을 담당한다.
|
||||||
|
- 최근 로그인, 현재 세션, 연결 앱, 세션 상세, 차단 버튼 등 사용자가 확인해야 하는 정보를 표시한다.
|
||||||
|
- WebView는 민감 토큰, private key, 생체 인증 결과를 직접 보관하거나 처리하지 않는다.
|
||||||
|
- WebView와 네이티브 계층 사이의 메시지는 명시적인 bridge API로 제한한다.
|
||||||
|
|
||||||
|
### 2.3 네이티브 계층 역할
|
||||||
|
|
||||||
|
- 로그인 발생 알림은 FCM/APNs 기반 네이티브 푸시로 처리한다.
|
||||||
|
- 차단, 연결 해제, 고위험 승인 전 사용자 확인은 Android/iOS 네이티브 또는 Flutter `local_auth` 계층에서 처리한다.
|
||||||
|
- 민감 토큰 저장과 향후 기기 서명은 Android Keystore, iOS Keychain/Secure Enclave 계층에서 처리한다.
|
||||||
|
- 네이티브 계층은 WebView가 요청한 민감 작업을 검증한 뒤 서버 API를 호출하거나, 필요한 경우 서명된 payload를 생성한다.
|
||||||
|
|
||||||
|
## 3. 목표 사용자 흐름
|
||||||
|
|
||||||
|
초기 기본 흐름은 `선승인 후확인 후차단` 방식이다.
|
||||||
|
|
||||||
|
1. 사용자가 Baron SSO 또는 RP 로그인 화면에서 휴대전화번호를 입력한다.
|
||||||
|
2. Baron SSO가 기존 정책에 따라 로그인을 진행한다.
|
||||||
|
3. 로그인 성공 시 세션과 접속 이력이 기록된다.
|
||||||
|
4. Baron SSO가 등록된 Baron Safe 앱으로 로그인 발생 알림을 보낸다.
|
||||||
|
5. 사용자는 Baron Safe 앱에서 접속 서비스, IP, 기기, 시간, 인증수단을 확인한다.
|
||||||
|
6. 본인 요청이면 별도 조치 없이 유지한다.
|
||||||
|
7. 의심 요청이면 앱에서 세션 차단, 로그아웃, RP 연결 해제를 수행한다.
|
||||||
|
8. 차단/해제 전에는 생체 인증 또는 앱 PIN으로 사용자 확인을 수행한다.
|
||||||
|
|
||||||
|
고위험 RP, 관리자 기능, 신규 기기 접속은 이후 단계에서 `로그인 요청 시 승인/차단` 방식으로 확장한다.
|
||||||
|
|
||||||
|
## 4. 사전 준비
|
||||||
|
|
||||||
|
### 4.0 기술 스택 정의
|
||||||
|
|
||||||
|
Baron Safe 하이브리드 앱의 기본 기술 스택은 다음과 같이 정의한다.
|
||||||
|
|
||||||
|
| 영역 | 기술 |
|
||||||
|
| --- | --- |
|
||||||
|
| App Framework | Flutter |
|
||||||
|
| Language | Dart |
|
||||||
|
| 화면 구성 | Flutter WebView 기반 하이브리드 화면 |
|
||||||
|
| WebView 대상 | Baron Safe 전용 route 또는 `userfront`에서 분리한 재사용 화면 |
|
||||||
|
| 상태 관리 | flutter_riverpod |
|
||||||
|
| Routing | go_router |
|
||||||
|
| API 통신 | http 우선, 필요 시 dio 검토 |
|
||||||
|
| Push | Firebase Cloud Messaging, iOS APNs |
|
||||||
|
| 생체 인증 | local_auth 또는 Android/iOS 네이티브 인증 |
|
||||||
|
| 보안 저장소 | Android Keystore, iOS Keychain/Secure Enclave |
|
||||||
|
| 기기 서명 | JWS/JWK 우선 검토, WebAuthn/FIDO2 확장 시 COSE 검토 |
|
||||||
|
| Backend | 기존 Baron SSO backend와 연동 |
|
||||||
|
| 인증/세션 | Ory Hydra, Ory Kratos 연동 |
|
||||||
|
| 배포 | Android APK/AAB, iOS IPA/TestFlight/MDM 검토 |
|
||||||
|
| 개발 도구 | VS Code, Flutter SDK, Android Studio SDK, Xcode |
|
||||||
|
| AI 개발 도구 | VS Code 기반 AI coding assistant, 코드 리뷰 assistant, 문서화 assistant |
|
||||||
|
|
||||||
|
기술 스택 선택 기준:
|
||||||
|
|
||||||
|
- 기존 `userfront`와의 기술 일관성을 유지한다.
|
||||||
|
- Android/iOS 공통 개발이 가능해야 한다.
|
||||||
|
- 민감 보안 기능은 WebView 내부가 아니라 네이티브 또는 Flutter 플러그인 계층에서 처리한다.
|
||||||
|
- PoC 단계에서는 구현 속도를 우선하되, MVP 단계에서 보안 저장소와 기기 서명 구조를 반드시 보강한다.
|
||||||
|
|
||||||
|
### 4.1 화면 및 기능 식별
|
||||||
|
|
||||||
|
개발 착수 전 `userfront`에서 Baron Safe에 활용 가능한 부분을 식별한다.
|
||||||
|
|
||||||
|
| 대상 | 확인 항목 |
|
||||||
|
| --- | --- |
|
||||||
|
| 연결 앱 현황 | RP 목록, 연결 상태, 최근 인증, 해지/차단 상태 |
|
||||||
|
| 접속 이력 | 로그인 시간, IP, 브라우저, 기기, 인증수단, 성공 여부 |
|
||||||
|
| 현재 세션 | 현재 접속 세션, 세션 ID, 세션 상태, 로그아웃 가능 여부 |
|
||||||
|
| QR 화면 | 기기 등록, 앱 연결, 승인 진입에 재사용 가능한지 검토 |
|
||||||
|
| 기본 포털 UI | 헤더, 메뉴, 카드, 모바일 레이아웃, 다국어 처리 방식 |
|
||||||
|
| API 호출 패턴 | 인증 쿠키/토큰 처리, 오류 처리, runtime env 처리 방식 |
|
||||||
|
|
||||||
|
### 4.2 앱 프로젝트 준비
|
||||||
|
|
||||||
|
- Baron Safe 앱용 별도 Gitea repository 또는 mono-repo 내 앱 디렉터리 구성을 결정한다.
|
||||||
|
- Android package name과 iOS bundle id를 결정한다.
|
||||||
|
- 개발/스테이징/운영 API endpoint 구분 방식을 정한다.
|
||||||
|
- 앱 버전, 빌드번호, 앱 이름, 아이콘, 권한 문구를 정한다.
|
||||||
|
- WebView에서 표시할 Baron Safe 전용 진입 URL 또는 route를 정의한다.
|
||||||
|
|
||||||
|
### 4.2.1 Gitea repository 구성
|
||||||
|
|
||||||
|
Baron Safe 앱은 기존 `userfront`를 직접 수정하지 않는다는 원칙을 유지하기 위해 별도 repository 구성을 우선 검토한다.
|
||||||
|
|
||||||
|
권장 repository:
|
||||||
|
|
||||||
|
```text
|
||||||
|
baron-safe-app
|
||||||
|
```
|
||||||
|
|
||||||
|
권장 디렉터리 구조:
|
||||||
|
|
||||||
|
```text
|
||||||
|
baron-safe-app/
|
||||||
|
README.md
|
||||||
|
docs/
|
||||||
|
architecture.md
|
||||||
|
api-contract.md
|
||||||
|
bridge-policy.md
|
||||||
|
release-policy.md
|
||||||
|
app/
|
||||||
|
pubspec.yaml
|
||||||
|
lib/
|
||||||
|
android/
|
||||||
|
ios/
|
||||||
|
test/
|
||||||
|
integration_test/
|
||||||
|
docker/
|
||||||
|
Dockerfile.web
|
||||||
|
nginx.conf
|
||||||
|
scripts/
|
||||||
|
bootstrap.sh
|
||||||
|
test.sh
|
||||||
|
build-android.sh
|
||||||
|
build-ios.sh
|
||||||
|
.vscode/
|
||||||
|
settings.json
|
||||||
|
extensions.json
|
||||||
|
tasks.json
|
||||||
|
.gitea/
|
||||||
|
workflows/
|
||||||
|
ci.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
구성 원칙:
|
||||||
|
|
||||||
|
- `app/`에는 Baron Safe Flutter 앱 코드를 둔다.
|
||||||
|
- `docs/`에는 앱 아키텍처, API 계약, WebView-native bridge 정책, 배포 정책을 둔다.
|
||||||
|
- `docker/`에는 WebView에서 로드할 정적 web build 또는 preview 서버 구성을 둔다.
|
||||||
|
- `scripts/`에는 개발자 환경 구성, 테스트, 빌드 스크립트를 둔다.
|
||||||
|
- `.vscode/`에는 팀 공통 VS Code 설정과 권장 extension을 둔다.
|
||||||
|
- `.gitea/workflows/`에는 lint, test, build 검증 workflow를 둔다.
|
||||||
|
|
||||||
|
대안:
|
||||||
|
|
||||||
|
- 기존 `baron-sso` mono-repo 내부에 `baron_safe_app/` 디렉터리로 시작할 수 있다.
|
||||||
|
- 단, 앱 배포, 모바일 권한, 서명키, release cadence가 `userfront`와 달라질 가능성이 높으므로 장기적으로는 별도 Gitea repository가 더 명확하다.
|
||||||
|
|
||||||
|
### 4.3 인프라 준비
|
||||||
|
|
||||||
|
- FCM 프로젝트 및 Android 설정 파일을 준비한다.
|
||||||
|
- iOS APNs 인증서 또는 키, bundle id, entitlement를 준비한다.
|
||||||
|
- push token 등록/갱신 API를 준비한다.
|
||||||
|
- 앱 기기 등록 테이블 또는 저장소를 설계한다.
|
||||||
|
- 로그인 이벤트 발생 시 push 발송을 트리거할 backend hook을 정의한다.
|
||||||
|
|
||||||
|
### 4.3.1 Docker 구성
|
||||||
|
|
||||||
|
모바일 앱 자체는 Docker 컨테이너로 실행되는 대상이 아니지만, 개발과 검증을 위해 다음 Docker 구성을 준비한다.
|
||||||
|
|
||||||
|
Docker 구성 대상:
|
||||||
|
|
||||||
|
- Baron Safe WebView용 web 화면 preview 서버
|
||||||
|
- API mock 서버
|
||||||
|
- 문서 preview 또는 정적 파일 서버
|
||||||
|
- CI에서 사용할 Flutter 분석/테스트 환경
|
||||||
|
|
||||||
|
권장 구성:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docker/
|
||||||
|
Dockerfile.web
|
||||||
|
Dockerfile.flutter-ci
|
||||||
|
nginx.conf
|
||||||
|
docker-compose.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
예상 서비스:
|
||||||
|
|
||||||
|
| 서비스 | 용도 |
|
||||||
|
| --- | --- |
|
||||||
|
| `baron-safe-web` | WebView에서 로드할 web build 또는 preview 화면 제공 |
|
||||||
|
| `baron-safe-api-mock` | 세션/연결 앱/차단 API mock 응답 제공 |
|
||||||
|
| `flutter-ci` | `flutter analyze`, `flutter test` 실행 |
|
||||||
|
|
||||||
|
Docker 구성 원칙:
|
||||||
|
|
||||||
|
- 로컬 개발에서 backend 전체를 띄우지 않아도 Baron Safe 화면과 앱 흐름을 확인할 수 있어야 한다.
|
||||||
|
- WebView URL은 개발/스테이징/운영 환경별로 분리한다.
|
||||||
|
- mock API는 PoC 단계에서만 사용하고, MVP 단계에서는 실제 Baron SSO backend API로 전환한다.
|
||||||
|
- Android/iOS 실제 빌드는 로컬 SDK 또는 CI runner 환경에서 수행하며, Docker는 보조 검증 환경으로 사용한다.
|
||||||
|
|
||||||
|
### 4.4 보안 정책 준비
|
||||||
|
|
||||||
|
- Baron Safe 기기 등록 정책을 정의한다.
|
||||||
|
- 차단/해제 전 생체 인증 또는 앱 PIN 요구 범위를 정의한다.
|
||||||
|
- 민감 토큰 저장 위치를 정의한다.
|
||||||
|
- WebView와 네이티브 bridge에서 허용할 명령 목록을 정의한다.
|
||||||
|
- 차단 시 종료할 세션 범위를 정의한다.
|
||||||
|
- 감사 로그 항목을 정의한다.
|
||||||
|
|
||||||
|
### 4.5 VS Code 기반 AI 개발 방식
|
||||||
|
|
||||||
|
Baron Safe 앱 개발은 VS Code를 기본 IDE로 두고, AI coding assistant를 활용한 개발 방식을 권장한다.
|
||||||
|
|
||||||
|
목표:
|
||||||
|
|
||||||
|
- 반복적인 Flutter 화면/상태관리 코드 작성 속도를 높인다.
|
||||||
|
- API 계약 변경 시 model, service, provider, test 코드를 일관되게 갱신한다.
|
||||||
|
- 문서와 코드의 불일치를 줄인다.
|
||||||
|
- 보안 민감 영역은 AI가 제안하더라도 사람이 반드시 리뷰한다.
|
||||||
|
|
||||||
|
권장 VS Code 구성:
|
||||||
|
|
||||||
|
| 항목 | 내용 |
|
||||||
|
| --- | --- |
|
||||||
|
| Flutter/Dart extension | Flutter 개발, debug, format, test 실행 |
|
||||||
|
| REST Client 또는 Thunder Client | API 계약 검증 |
|
||||||
|
| Docker extension | mock/preview 서버 실행 |
|
||||||
|
| Git/Gitea 연동 | branch, commit, PR 확인 |
|
||||||
|
| AI coding assistant | 코드 생성, 리팩터링, 테스트 초안, 문서화 보조 |
|
||||||
|
|
||||||
|
AI 활용 절차:
|
||||||
|
|
||||||
|
1. 작업 전 정책 문서와 API 계약을 먼저 확인한다.
|
||||||
|
2. AI에게 변경 범위를 명확히 지시한다.
|
||||||
|
3. 생성된 코드는 반드시 `flutter analyze`와 테스트를 통과시킨다.
|
||||||
|
4. 보안 저장소, 생체 인증, bridge, 서명 검증 코드는 사람 리뷰를 필수로 한다.
|
||||||
|
5. AI가 생성한 코드가 민감정보를 log에 남기지 않는지 확인한다.
|
||||||
|
6. PR 설명에는 AI 도움을 받은 범위와 사람이 검증한 항목을 기록한다.
|
||||||
|
|
||||||
|
AI 사용 제한:
|
||||||
|
|
||||||
|
- 앱 서명키, APNs key, FCM server key, private key, 운영 DB 접속정보를 AI 프롬프트에 넣지 않는다.
|
||||||
|
- 보안 정책을 완화하는 변경은 AI 제안만으로 반영하지 않는다.
|
||||||
|
- WebView bridge 허용 명령은 정책 문서에 정의된 목록을 벗어나지 않는다.
|
||||||
|
- 생성된 암호화/서명 코드는 공식 라이브러리와 플랫폼 문서를 기준으로 재검증한다.
|
||||||
|
|
||||||
|
## 5. 플랫폼 개발 전략
|
||||||
|
|
||||||
|
Flutter는 하나의 코드베이스로 Android와 iOS를 함께 지원할 수 있다. 다만 Baron Safe 앱은 일반 화면 앱이 아니라 푸시, 생체 인증, 보안 저장소, WebView-native bridge, 향후 기기 서명까지 포함하는 보안 앱이므로 양 플랫폼을 어떤 순서로 검증할지 정책이 필요하다.
|
||||||
|
|
||||||
|
### 5.1 개발 방식 후보
|
||||||
|
|
||||||
|
| 안 | 방식 | 설명 | 장점 | 단점 | 적합성 |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| 1안 | Android 우선 개발 후 iOS 확장 | Android에서 PoC와 주요 네이티브 기능을 먼저 검증하고 이후 iOS로 확장한다. | 초기 PoC가 빠름. FCM, WebView, local_auth, Keystore 검증이 상대적으로 수월함. 내부 테스트 배포가 쉬움. | iOS APNs, Keychain, WebView 정책 이슈가 뒤늦게 발견될 수 있음. | 높음 |
|
||||||
|
| 2안 | Android/iOS 동시 개발 | 처음부터 Android와 iOS를 같은 수준으로 개발하고 검증한다. | 양 플랫폼 차이를 초기에 발견할 수 있음. 최종 품질 균형이 좋음. | Xcode, APNs, 인증서, TestFlight까지 동시에 준비해야 하므로 초기 부담이 큼. | 중간 |
|
||||||
|
| 3안 | 공통 Flutter/WebView 코드 먼저 개발 후 플랫폼 기능 순차 적용 | 화면, 라우팅, API, 상태관리, WebView 구조를 먼저 만들고 Android/iOS 네이티브 기능을 순차 적용한다. | 공통 구조를 안정화한 뒤 플랫폼 기능을 붙일 수 있음. 코드 중복이 줄어듦. | 푸시, 생체 인증, 보안 저장소 같은 핵심 기능 검증이 늦어질 수 있음. | 높음 |
|
||||||
|
| 4안 | WebView/PWA PoC 먼저, 이후 앱화 | 기존 `userfront` 활용 화면과 기본 사용자 흐름을 웹/PWA 수준에서 먼저 검증한 뒤 앱으로 감싼다. | 화면과 정책을 빠르게 검증 가능. 앱 개발 전에 UX를 확정하기 좋음. | 네이티브 푸시, 생체 인증, 보안 저장소 검증은 별도 단계가 필요함. | 높음 |
|
||||||
|
| 5안 | iOS 우선 개발 후 Android 확장 | iOS의 제약을 먼저 해결하고 Android로 확장한다. | Apple 배포, APNs, Keychain 제약을 조기에 확인 가능. | 초기 개발 속도가 느릴 수 있고 테스트 환경 준비가 번거로움. | 낮음 |
|
||||||
|
|
||||||
|
### 5.2 권장 전략
|
||||||
|
|
||||||
|
권장안은 `3안 + 1안` 조합이다.
|
||||||
|
|
||||||
|
즉 소스코드는 Flutter 공통 코드로 처음부터 Android와 iOS를 고려해서 작성하되, 실제 네이티브 기능 검증은 Android를 먼저 진행하고 이후 iOS로 확장한다.
|
||||||
|
|
||||||
|
권장 순서:
|
||||||
|
|
||||||
|
1. 공통 Flutter/WebView 구조를 먼저 개발한다.
|
||||||
|
2. 세션/접속이력/연결 앱 조회, 차단 UI, API 연동 구조를 공통 코드로 만든다.
|
||||||
|
3. Android에서 FCM, WebView-native bridge, local_auth, Android Keystore를 먼저 검증한다.
|
||||||
|
4. Android 기준으로 선승인 후확인 후차단 PoC를 완성한다.
|
||||||
|
5. iOS에서 APNs, WKWebView, Face ID/Touch ID, Keychain/Secure Enclave를 확장 검증한다.
|
||||||
|
6. Android/iOS 차이를 반영하여 공통 UI와 플랫폼별 bridge를 정리한다.
|
||||||
|
7. 양 플랫폼에서 푸시 수신 상태, 앱 재설치, 기기 변경, 세션 차단을 반복 검증한다.
|
||||||
|
|
||||||
|
### 5.3 플랫폼별 검증 우선순위
|
||||||
|
|
||||||
|
| 단계 | Android | iOS |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| PoC | WebView, FCM, 세션 조회, 차단 API, local_auth 우선 검증 | WebView 표시와 기본 로그인 세션 유지 수준 확인 |
|
||||||
|
| MVP | Android Keystore, push background/terminated 수신, 차단/해제 생체 인증 검증 | APNs, Keychain, Face ID/Touch ID, WKWebView 정책 검증 |
|
||||||
|
| 운영 전 | APK/AAB 배포, Play Integrity 검토 | TestFlight/MDM 배포, DeviceCheck/App Attest 검토 |
|
||||||
|
|
||||||
|
### 5.4 결정 원칙
|
||||||
|
|
||||||
|
- 공통 Flutter 코드는 처음부터 Android/iOS 모두를 고려해 작성한다.
|
||||||
|
- 플랫폼별 네이티브 기능은 Android에서 먼저 PoC를 완성한 뒤 iOS로 확장한다.
|
||||||
|
- iOS를 너무 늦게 검증하지 않는다. PoC 중에도 최소한 WebView, 로그인 세션, APNs 준비 가능 여부는 확인한다.
|
||||||
|
- 푸시, 생체 인증, 보안 저장소, bridge는 플랫폼별 차이가 크므로 공통 인터페이스와 플랫폼 구현체를 분리한다.
|
||||||
|
- 운영 배포 전에는 Android/iOS 모두 동일한 보안 기준을 통과해야 한다.
|
||||||
|
|
||||||
|
## 6. 단계별 개발 순서
|
||||||
|
|
||||||
|
### 6.1 0단계: 요구사항 및 설계 확정
|
||||||
|
|
||||||
|
목표는 개발자가 바로 구현에 들어갈 수 있는 계약을 만드는 것이다.
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
- 기본 흐름을 `선승인 후확인 후차단`으로 확정한다.
|
||||||
|
- 고위험 RP에만 사전 승인 방식을 적용할지 정책 초안을 정한다.
|
||||||
|
- Baron Safe에서 활용할 `userfront` 화면과 제외할 화면을 구분한다.
|
||||||
|
- 기술 스택을 확정한다.
|
||||||
|
- Gitea repository 구성 방식을 확정한다.
|
||||||
|
- Docker 기반 preview/mock/CI 구성을 확정한다.
|
||||||
|
- VS Code 기반 AI 개발 규칙을 팀에 공유한다.
|
||||||
|
- WebView 담당 기능과 네이티브 담당 기능을 분리한다.
|
||||||
|
- API 목록과 request/response 초안을 확정한다.
|
||||||
|
- 감사 로그 이벤트 이름과 필드를 정의한다.
|
||||||
|
|
||||||
|
산출물:
|
||||||
|
|
||||||
|
- 화면 흐름도
|
||||||
|
- API 계약 초안
|
||||||
|
- WebView-native bridge 명세 초안
|
||||||
|
- 보안 정책 초안
|
||||||
|
- Gitea repository 구조 초안
|
||||||
|
- Docker 구성 초안
|
||||||
|
- VS Code 설정 및 AI 개발 가이드
|
||||||
|
|
||||||
|
### 6.2 1단계: 앱 뼈대 및 WebView PoC
|
||||||
|
|
||||||
|
목표는 Baron Safe 앱에서 기존 화면 흐름을 앱처럼 보여주는 것이다.
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
- Gitea repository를 생성한다.
|
||||||
|
- 기본 branch 전략과 PR 규칙을 설정한다.
|
||||||
|
- `.vscode/settings.json`, `.vscode/extensions.json`, `.vscode/tasks.json`를 추가한다.
|
||||||
|
- Docker preview/mock 구성을 추가한다.
|
||||||
|
- Baron Safe 앱 프로젝트를 생성한다.
|
||||||
|
- WebView 또는 Flutter WebView 컨테이너를 구성한다.
|
||||||
|
- Baron Safe 전용 route 또는 URL을 로드한다.
|
||||||
|
- 앱 헤더, 뒤로가기, 새로고침, 오류 화면을 구현한다.
|
||||||
|
- 개발/스테이징 환경 전환 방식을 만든다.
|
||||||
|
- WebView와 네이티브 계층 사이의 최소 bridge를 만든다.
|
||||||
|
|
||||||
|
검증:
|
||||||
|
|
||||||
|
- Gitea repository에서 PR 기반 개발 흐름이 동작하는지 확인한다.
|
||||||
|
- Docker preview/mock 서버가 실행되는지 확인한다.
|
||||||
|
- VS Code task로 analyze/test/mock 실행이 가능한지 확인한다.
|
||||||
|
- Android에서 WebView 화면이 정상 로드되는지 확인한다.
|
||||||
|
- 로그인 세션이 유지되는지 확인한다.
|
||||||
|
- 네트워크 오류, 인증 만료, 뒤로가기 동작을 확인한다.
|
||||||
|
|
||||||
|
### 6.3 2단계: 기기 등록 및 push token 등록
|
||||||
|
|
||||||
|
목표는 Baron Safe 앱을 사용자 계정에 연결된 기기로 등록하는 것이다.
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
- 기기 등록 화면 또는 QR/등록 코드 입력 흐름을 구현한다.
|
||||||
|
- 앱에서 device id를 생성하거나 서버에서 발급받는다.
|
||||||
|
- FCM/APNs push token을 발급받는다.
|
||||||
|
- device id, push token, platform, app version을 서버에 등록한다.
|
||||||
|
- push token 갱신 시 서버에 다시 등록한다.
|
||||||
|
- 기기 해제 API와 화면을 준비한다.
|
||||||
|
|
||||||
|
검증:
|
||||||
|
|
||||||
|
- 동일 사용자의 다중 기기 등록이 가능한지 확인한다.
|
||||||
|
- push token 갱신이 누락되지 않는지 확인한다.
|
||||||
|
- 기기 해제 후 푸시가 발송되지 않는지 확인한다.
|
||||||
|
|
||||||
|
### 6.4 3단계: 세션/연결 앱 조회 화면
|
||||||
|
|
||||||
|
목표는 사용자가 Baron Safe 앱에서 최근 로그인과 연결 앱 상태를 확인할 수 있게 하는 것이다.
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
- 최근 로그인/현재 세션 조회 API를 구현한다.
|
||||||
|
- 연결 앱 현황 조회 API를 구현한다.
|
||||||
|
- WebView 화면에서 세션 목록, 세션 상세, 연결 앱 목록을 표시한다.
|
||||||
|
- 현재 세션, 비활성 세션, 성공/실패 상태를 구분 표시한다.
|
||||||
|
- 새로고침 및 pull-to-refresh 동작을 구현한다.
|
||||||
|
|
||||||
|
검증:
|
||||||
|
|
||||||
|
- `userfront`의 접속 이력과 Baron Safe 앱의 접속 이력이 일관되는지 확인한다.
|
||||||
|
- 세션 상태가 실시간 또는 준실시간으로 갱신되는지 확인한다.
|
||||||
|
- 사용자가 본인 요청 여부를 판단할 수 있는 정보가 충분한지 확인한다.
|
||||||
|
|
||||||
|
### 6.5 4단계: 로그인 발생 푸시
|
||||||
|
|
||||||
|
목표는 로그인 발생 시 Baron Safe 앱으로 알림을 보내는 것이다.
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
- 로그인 성공 이벤트 발생 시 backend에서 push 발송을 트리거한다.
|
||||||
|
- 푸시 payload에는 민감정보를 최소화한다.
|
||||||
|
- 푸시 클릭 시 해당 세션 상세 화면으로 이동한다.
|
||||||
|
- 푸시 수신 실패 시에도 앱 내 접속 이력에서 확인 가능하도록 한다.
|
||||||
|
- 반복 로그인 알림에 대한 rate limit을 적용한다.
|
||||||
|
|
||||||
|
검증:
|
||||||
|
|
||||||
|
- Android foreground/background/terminated 상태별 푸시 수신을 확인한다.
|
||||||
|
- iOS foreground/background/terminated 상태별 푸시 수신을 확인한다.
|
||||||
|
- 푸시 클릭 deep link가 정확한 세션 상세로 이동하는지 확인한다.
|
||||||
|
|
||||||
|
### 6.6 5단계: 차단/해제 기능
|
||||||
|
|
||||||
|
목표는 의심 세션 또는 RP 연결을 사용자가 앱에서 차단할 수 있게 하는 것이다.
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
- 세션 차단/종료 API를 구현한다.
|
||||||
|
- RP 연결 해제/차단 API를 구현한다.
|
||||||
|
- 차단 버튼 클릭 시 네이티브 생체 인증 또는 앱 PIN을 요구한다.
|
||||||
|
- 인증 성공 후 차단 API를 호출한다.
|
||||||
|
- 차단 결과를 WebView 화면에 반영한다.
|
||||||
|
- 차단 이벤트를 감사 로그로 남긴다.
|
||||||
|
|
||||||
|
검증:
|
||||||
|
|
||||||
|
- 차단 후 Hydra/Kratos 세션이 종료되는지 확인한다.
|
||||||
|
- 필요한 경우 RP refresh token이 무효화되는지 확인한다.
|
||||||
|
- 차단한 세션이 다시 활성화되지 않는지 확인한다.
|
||||||
|
- 본인 현재 세션 차단 시 UX가 자연스러운지 확인한다.
|
||||||
|
|
||||||
|
### 6.7 6단계: 민감 저장소 및 서명 기반 보강
|
||||||
|
|
||||||
|
목표는 민감 정보를 WebView가 아닌 기기 보안 계층에서 관리하는 것이다.
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
- Android Keystore, iOS Keychain/Secure Enclave 사용 방식을 확정한다.
|
||||||
|
- 앱 기기 key pair 생성 방식을 구현한다.
|
||||||
|
- public key를 서버에 등록한다.
|
||||||
|
- private key는 서버로 전송하지 않는다.
|
||||||
|
- 차단/해제 요청 payload에 서명하는 구조를 검토하거나 구현한다.
|
||||||
|
- 서버에서 public key로 서명을 검증한다.
|
||||||
|
|
||||||
|
검증:
|
||||||
|
|
||||||
|
- 앱 재설치, 기기 변경, OS 업데이트 시 키 관리 동작을 확인한다.
|
||||||
|
- private key export가 불가능한지 확인한다.
|
||||||
|
- 위조된 차단 요청이 거부되는지 확인한다.
|
||||||
|
|
||||||
|
### 6.8 7단계: 고위험 로그인 사전 승인 확장
|
||||||
|
|
||||||
|
목표는 기본안으로 부족한 고위험 상황에 사전 승인 방식을 추가하는 것이다.
|
||||||
|
|
||||||
|
적용 후보:
|
||||||
|
|
||||||
|
- 관리자 기능 접근
|
||||||
|
- 외부망 또는 미확인 IP 접속
|
||||||
|
- 신규 기기 로그인
|
||||||
|
- 고위험 RP 로그인
|
||||||
|
- 짧은 시간 내 반복 로그인
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
- 고위험 조건 판단 정책을 정의한다.
|
||||||
|
- 승인 요청 생성 API를 구현한다.
|
||||||
|
- Baron Safe 앱에서 승인/차단 화면을 제공한다.
|
||||||
|
- 승인 전 생체 인증 또는 앱 PIN을 요구한다.
|
||||||
|
- 승인 결과에 따라 Hydra login challenge를 accept/reject한다.
|
||||||
|
|
||||||
|
검증:
|
||||||
|
|
||||||
|
- 고위험 조건에서만 사전 승인이 동작하는지 확인한다.
|
||||||
|
- 승인 만료, 거절, 중복 승인 요청이 정상 처리되는지 확인한다.
|
||||||
|
- 일반 RP의 로그인 UX가 과도하게 느려지지 않는지 확인한다.
|
||||||
|
|
||||||
|
### 6.9 8단계: 운영 배포 준비
|
||||||
|
|
||||||
|
목표는 내부/협력사 사용자가 안정적으로 설치하고 사용할 수 있게 하는 것이다.
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
- Android 배포 방식을 결정한다.
|
||||||
|
- iOS 배포 방식을 결정한다.
|
||||||
|
- 앱 서명키와 인증서를 관리한다.
|
||||||
|
- 개인정보 처리 방침과 권한 안내 문구를 준비한다.
|
||||||
|
- 장애 대응 절차를 정한다.
|
||||||
|
- 기기 분실/교체/해제 절차를 정한다.
|
||||||
|
- 로그 모니터링과 알림을 구성한다.
|
||||||
|
|
||||||
|
검증:
|
||||||
|
|
||||||
|
- 신규 사용자 등록 절차를 테스트한다.
|
||||||
|
- 기기 교체 시 재등록 절차를 테스트한다.
|
||||||
|
- push 장애 시 대체 확인 흐름을 테스트한다.
|
||||||
|
- backend 장애 시 앱 화면의 오류 처리를 테스트한다.
|
||||||
|
|
||||||
|
## 7. WebView-Native Bridge 정책
|
||||||
|
|
||||||
|
WebView에서 네이티브 기능을 호출할 때는 허용된 명령만 처리한다.
|
||||||
|
|
||||||
|
초기 허용 명령:
|
||||||
|
|
||||||
|
| 명령 | 설명 | 네이티브 처리 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `getDeviceInfo` | 앱 기기 정보 조회 | device id, platform, app version 반환 |
|
||||||
|
| `registerPushToken` | push token 등록 요청 | 현재 token을 서버에 등록 |
|
||||||
|
| `openBiometricPrompt` | 생체 인증 요청 | local_auth 또는 네이티브 인증 실행 |
|
||||||
|
| `blockSession` | 세션 차단 요청 | 생체 인증 후 차단 API 호출 |
|
||||||
|
| `blockLinkedApp` | RP 연결 차단 요청 | 생체 인증 후 차단 API 호출 |
|
||||||
|
| `openSettings` | 앱 설정 열기 | 네이티브 설정 화면 이동 |
|
||||||
|
|
||||||
|
금지 원칙:
|
||||||
|
|
||||||
|
- WebView에 private key를 전달하지 않는다.
|
||||||
|
- WebView에 장기 refresh token을 저장하지 않는다.
|
||||||
|
- WebView JS에서 생체 인증 성공 여부를 임의로 조작할 수 없게 한다.
|
||||||
|
- bridge 명령에는 origin 검증과 session 검증을 적용한다.
|
||||||
|
|
||||||
|
## 8. Backend API 준비 목록
|
||||||
|
|
||||||
|
초기 기본안에 필요한 API:
|
||||||
|
|
||||||
|
- `POST /api/v1/baron-safe/devices/register`
|
||||||
|
- `POST /api/v1/baron-safe/devices/push-token`
|
||||||
|
- `DELETE /api/v1/baron-safe/devices/{deviceId}`
|
||||||
|
- `GET /api/v1/baron-safe/sessions`
|
||||||
|
- `GET /api/v1/baron-safe/sessions/{sessionId}`
|
||||||
|
- `POST /api/v1/baron-safe/sessions/{sessionId}/block`
|
||||||
|
- `GET /api/v1/baron-safe/linked-apps`
|
||||||
|
- `POST /api/v1/baron-safe/linked-apps/{clientId}/block`
|
||||||
|
|
||||||
|
고위험 사전 승인 확장 시 필요한 API:
|
||||||
|
|
||||||
|
- `POST /api/v1/auth/baron-safe/login/init`
|
||||||
|
- `GET /api/v1/auth/baron-safe/requests/{authReqId}`
|
||||||
|
- `POST /api/v1/auth/baron-safe/requests/{authReqId}/decision`
|
||||||
|
- `POST /api/v1/auth/baron-safe/login/poll`
|
||||||
|
|
||||||
|
## 9. 감사 로그 정책
|
||||||
|
|
||||||
|
최소 감사 로그 이벤트:
|
||||||
|
|
||||||
|
| 이벤트 | 설명 |
|
||||||
|
| --- | --- |
|
||||||
|
| `baron_safe.device_registered` | Baron Safe 기기 등록 |
|
||||||
|
| `baron_safe.device_removed` | Baron Safe 기기 해제 |
|
||||||
|
| `baron_safe.push_token_updated` | push token 갱신 |
|
||||||
|
| `baron_safe.login_notified` | 로그인 발생 알림 발송 |
|
||||||
|
| `baron_safe.session_viewed` | 사용자가 세션 상세 확인 |
|
||||||
|
| `baron_safe.session_blocked` | 세션 차단 |
|
||||||
|
| `baron_safe.linked_app_blocked` | RP 연결 차단 |
|
||||||
|
| `baron_safe.biometric_verified` | 생체 인증 성공 |
|
||||||
|
| `baron_safe.biometric_failed` | 생체 인증 실패 |
|
||||||
|
| `baron_safe.high_risk_approved` | 고위험 로그인 승인 |
|
||||||
|
| `baron_safe.high_risk_rejected` | 고위험 로그인 거절 |
|
||||||
|
|
||||||
|
로그 필드:
|
||||||
|
|
||||||
|
- event_name
|
||||||
|
- user_id
|
||||||
|
- device_id
|
||||||
|
- client_id
|
||||||
|
- session_id
|
||||||
|
- request_ip
|
||||||
|
- user_agent
|
||||||
|
- platform
|
||||||
|
- app_version
|
||||||
|
- result
|
||||||
|
- reason
|
||||||
|
- created_at
|
||||||
|
|
||||||
|
## 10. 테스트 정책
|
||||||
|
|
||||||
|
### 10.1 기능 테스트
|
||||||
|
|
||||||
|
- 기기 등록/해제
|
||||||
|
- push token 등록/갱신
|
||||||
|
- 로그인 발생 알림
|
||||||
|
- 세션 목록 조회
|
||||||
|
- 세션 상세 조회
|
||||||
|
- 세션 차단
|
||||||
|
- RP 연결 차단
|
||||||
|
- 생체 인증 성공/실패
|
||||||
|
|
||||||
|
### 10.2 보안 테스트
|
||||||
|
|
||||||
|
- 미등록 기기의 API 접근 차단
|
||||||
|
- 다른 사용자의 세션 차단 시도 차단
|
||||||
|
- WebView bridge 명령 위조 차단
|
||||||
|
- private key export 불가 확인
|
||||||
|
- 만료된 세션 차단 요청 처리
|
||||||
|
- 반복 요청 rate limit 확인
|
||||||
|
|
||||||
|
### 10.3 플랫폼 테스트
|
||||||
|
|
||||||
|
- Android foreground/background/terminated push 수신
|
||||||
|
- iOS foreground/background/terminated push 수신
|
||||||
|
- Android Keystore 동작
|
||||||
|
- iOS Keychain/Secure Enclave 동작
|
||||||
|
- 앱 재설치/기기 변경 시 동작
|
||||||
|
|
||||||
|
## 11. 단계별 완료 기준
|
||||||
|
|
||||||
|
| 단계 | 완료 기준 |
|
||||||
|
| --- | --- |
|
||||||
|
| 0단계 | 기술스택, Gitea 구성, Docker 구성, 화면/API/bridge/보안 정책 초안 확정 |
|
||||||
|
| 1단계 | Gitea repository, VS Code 설정, Docker preview/mock, 앱 WebView 뼈대 구성 완료 |
|
||||||
|
| 2단계 | 기기 등록 및 push token 등록 완료 |
|
||||||
|
| 3단계 | 세션/연결 앱 조회 화면 동작 |
|
||||||
|
| 4단계 | 로그인 발생 푸시 수신 및 세션 상세 진입 |
|
||||||
|
| 5단계 | 생체 인증 후 세션/RP 차단 가능 |
|
||||||
|
| 6단계 | 민감 저장소와 서명 기반 요청 검증 가능 |
|
||||||
|
| 7단계 | 고위험 로그인 사전 승인 흐름 검증 |
|
||||||
|
| 8단계 | Android/iOS 배포 및 운영 절차 확정 |
|
||||||
|
|
||||||
|
## 12. 결론
|
||||||
|
|
||||||
|
Baron Safe 앱은 초기에는 하이브리드 방식으로 진행하는 것이 현실적이다. 기존 `userfront`의 화면과 구현 패턴을 활용하면 PoC 속도를 높일 수 있고, 로그인 이력 확인 및 차단이라는 기본 흐름과도 잘 맞는다.
|
||||||
|
|
||||||
|
다만 Baron Safe 앱은 인증 보조 장치 역할을 하므로, 푸시, 생체 인증, 민감 저장소, 향후 기기 서명은 반드시 네이티브 또는 Flutter 플러그인 계층에서 처리해야 한다. WebView는 화면 표시와 사용자 조작을 담당하고, 실제 보안 동작은 네이티브 계층이 담당하도록 역할을 분리하는 것이 본 정책의 핵심이다.
|
||||||
@@ -0,0 +1,372 @@
|
|||||||
|
# Baron Safe 개발 전 검토 및 방식 비교
|
||||||
|
|
||||||
|
작성일: 2026-06-30
|
||||||
|
|
||||||
|
## 1. 목적
|
||||||
|
|
||||||
|
본 문서는 `Baron Safe` 앱 개발 착수 전 검토해야 할 주요 선택지를 정리한다. 회의 중 언급된 "은행앱처럼 앱/웹앱 전환", "Flutter 앱", "웹앱", "네이티브", "푸시 기능", "선승인 후차단" 등의 키워드를 기준으로, Baron SSO 승인 로그인에 적합한 개발 방식과 승인 프로세스를 비교한다.
|
||||||
|
|
||||||
|
핵심 판단 기준은 다음과 같다.
|
||||||
|
|
||||||
|
- 앱은 단순 명료하고 직관적이어야 한다.
|
||||||
|
- 앱에 많은 기능을 담으려는 순간 `Baron Safe`의 의미가 흐려진다.
|
||||||
|
- `Baron Safe`의 본질은 "요청 RP와 요청 기기를 확인하고 승인/거절하는 안전 확인 채널"이다.
|
||||||
|
- 따라서 앱의 1차 기능은 승인 요청 확인, 승인, 거절, 기기 등록/해제 정도로 제한한다.
|
||||||
|
- 복잡한 관리자 기능, RP 목록 탐색, 일반 포털 기능, 대시보드 기능은 Baron Safe 앱에 넣지 않는다.
|
||||||
|
|
||||||
|
## 2. 전제
|
||||||
|
|
||||||
|
Baron SSO 사용자는 불특정 다수가 아니라 내부 사용자, 협력사/고객사 사용자, 그리고 Baron SSO 연동 RP를 인지하고 사용하는 등록 사용자로 한정된다. 따라서 앱 설치와 기기 등록은 다소 번거롭더라도 보안상 수용 가능한 절차로 볼 수 있다.
|
||||||
|
|
||||||
|
단, 사용자에게 앱 설치를 요구하는 대신 앱은 아주 명확한 가치를 제공해야 한다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
내가 방금 요청한 로그인이 맞는가?
|
||||||
|
어느 RP가 요청했는가?
|
||||||
|
어떤 기기/브라우저에서 요청했는가?
|
||||||
|
승인할 것인가, 거절할 것인가?
|
||||||
|
```
|
||||||
|
|
||||||
|
이 네 가지 질문에 빠르고 안전하게 답하는 것이 `Baron Safe`의 1차 목표다.
|
||||||
|
|
||||||
|
## 3. 앱 기능 범위 원칙
|
||||||
|
|
||||||
|
### 3.1 반드시 포함할 기능
|
||||||
|
|
||||||
|
| 기능 | 설명 |
|
||||||
|
| --- | --- |
|
||||||
|
| 기기 등록 | 사용자 계정과 앱 기기를 사전 바인딩 |
|
||||||
|
| 승인 요청 수신 | Push 또는 앱 내 목록으로 요청 확인 |
|
||||||
|
| 요청 정보 표시 | RP명, 요청 기기, IP/위치, 요청 시각, 만료 시간 |
|
||||||
|
| 승인 | 생체 인증/PIN 후 승인 |
|
||||||
|
| 거절 | 요청자가 본인이 아니면 즉시 거절 |
|
||||||
|
| 요청 코드 확인 | number matching 또는 request code 입력/확인 |
|
||||||
|
| 기기 해제 | 분실/교체 시 기기 연결 해제 |
|
||||||
|
|
||||||
|
### 3.2 넣지 않는 것이 좋은 기능
|
||||||
|
|
||||||
|
| 기능 | 제외 이유 |
|
||||||
|
| --- | --- |
|
||||||
|
| 일반 SSO 포털 전체 기능 | 앱 목적이 흐려짐 |
|
||||||
|
| RP 즐겨찾기/런처 | 승인 앱이 포털 앱으로 변질됨 |
|
||||||
|
| 상세 감사 로그 전체 조회 | 관리자/웹 포털 영역에 적합 |
|
||||||
|
| 조직도/프로필/설정 전체 | 앱 복잡도 증가 |
|
||||||
|
| 자체 메신저/공지 기능 | 푸시 피로도 증가 |
|
||||||
|
| 다수 메뉴 기반 대시보드 | 사용자가 승인 순간에 집중하기 어려움 |
|
||||||
|
|
||||||
|
## 4. 구현 방식 비교
|
||||||
|
|
||||||
|
### 4.1 요약 판단
|
||||||
|
|
||||||
|
| 방식 | Android/iOS 공통성 | 푸시 신뢰성 | 보안 저장소 | 개발 속도 | 운영 적합성 | 판단 |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| 네이티브 앱 | 낮음 | 매우 높음 | 매우 좋음 | 느림 | 매우 좋음 | 장기 고보안 최적 |
|
||||||
|
| Flutter 앱 | 높음 | 높음 | 좋음 | 빠름 | 좋음 | 1차 권장 |
|
||||||
|
| React Native 앱 | 높음 | 높음 | 좋음 | 빠름 | 보통 | 대안 |
|
||||||
|
| PWA/웹앱 | 높음 | 보통/제약 있음 | 제한적 | 매우 빠름 | 제한적 | 단독 인증앱으로 비권장 |
|
||||||
|
| WebView/하이브리드 | 중간 | 높음 | 네이티브 보완 가능 | 보통 | 보통 | 단순 래핑은 비권장 |
|
||||||
|
|
||||||
|
1차 권장안은 `Flutter 앱 + 네이티브 보안 기능 연동`이다. Android/iOS 공통 개발이 가능하고, 기존 `userfront`가 Flutter 기반이라 기술 일관성도 있다. 푸시는 FCM/APNs를 네이티브 앱 수준으로 사용할 수 있다.
|
||||||
|
|
||||||
|
PWA/웹앱은 설치와 배포가 쉽지만, 승인 로그인용 인증 장치로 사용하기에는 기기 바인딩, 보안 저장소, 앱 무결성, 푸시 일관성 측면에서 약점이 있다.
|
||||||
|
|
||||||
|
## 5. 네이티브 앱
|
||||||
|
|
||||||
|
Android는 Kotlin, iOS는 Swift로 각각 개발하는 방식이다.
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- APNs, FCM, Android Keystore, iOS Keychain/Secure Enclave를 가장 직접적으로 제어할 수 있다.
|
||||||
|
- 생체 인증, 앱 무결성 검증, 백그라운드 알림 처리가 가장 안정적이다.
|
||||||
|
- 장기적으로 금융앱 수준의 보안 요구에 대응하기 좋다.
|
||||||
|
|
||||||
|
단점:
|
||||||
|
|
||||||
|
- Android/iOS 개발을 각각 진행해야 한다.
|
||||||
|
- 동일 기능을 양 플랫폼에서 반복 구현해야 한다.
|
||||||
|
- 초기 개발 기간과 유지보수 비용이 크다.
|
||||||
|
|
||||||
|
적합한 경우:
|
||||||
|
|
||||||
|
- 금융앱 수준의 강한 보안 통제와 장기 운영을 최우선으로 할 때
|
||||||
|
- 플랫폼별 세밀한 보안 정책을 직접 제어해야 할 때
|
||||||
|
- 충분한 Android/iOS 네이티브 개발 인력이 있을 때
|
||||||
|
|
||||||
|
## 6. Flutter 앱
|
||||||
|
|
||||||
|
Flutter는 하나의 코드베이스로 Android/iOS 앱을 개발하고, 필요한 보안 기능은 플랫폼 플러그인 또는 Method Channel로 연동하는 방식이다.
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- Android/iOS 공통 UI와 승인 로직을 빠르게 개발할 수 있다.
|
||||||
|
- 기존 Baron SSO `userfront`가 Flutter 기반이므로 내부 기술 친화성이 높다.
|
||||||
|
- FCM/APNs, local_auth, secure storage 등 승인 앱에 필요한 주요 기능을 연동할 수 있다.
|
||||||
|
- 앱 화면을 단순하게 유지하기 쉽다.
|
||||||
|
|
||||||
|
단점:
|
||||||
|
|
||||||
|
- 일부 보안 기능은 네이티브 플러그인 품질에 의존한다.
|
||||||
|
- App Attest, Play Integrity, Secure Enclave 세부 제어는 네이티브 연동이 필요할 수 있다.
|
||||||
|
|
||||||
|
적합한 경우:
|
||||||
|
|
||||||
|
- 1차 MVP를 빠르게 만들고 양 플랫폼을 동시에 지원해야 할 때
|
||||||
|
- 앱 기능을 승인/거절 중심으로 단순하게 제한할 때
|
||||||
|
- 플랫폼별 고급 보안 기능은 단계적으로 붙일 계획일 때
|
||||||
|
|
||||||
|
권장:
|
||||||
|
|
||||||
|
- 1차 Baron Safe는 Flutter로 시작한다.
|
||||||
|
- 단, private key 생성/보관, 앱 무결성 검증, 생체 인증은 네이티브 플러그인 또는 Method Channel로 신중하게 구현한다.
|
||||||
|
|
||||||
|
## 7. 웹앱/PWA
|
||||||
|
|
||||||
|
PWA는 웹 기술로 만든 앱을 홈 화면에 설치해 앱처럼 사용하는 방식이다.
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- 앱스토어 배포 없이 빠르게 제공할 수 있다.
|
||||||
|
- Android/iOS/데스크톱에서 같은 웹 코드로 접근할 수 있다.
|
||||||
|
- 단순 승인 화면 프로토타입을 빠르게 검증할 수 있다.
|
||||||
|
|
||||||
|
단점:
|
||||||
|
|
||||||
|
- iOS Web Push는 홈 화면에 추가된 웹앱에서 동작하는 방식이며, 사용자가 직접 홈 화면 추가와 알림 허용을 해야 한다.
|
||||||
|
- 브라우저/OS별 Push API, Service Worker, 권한 정책 차이가 있다.
|
||||||
|
- 기기 private key 보호, 앱 무결성, 루팅/탈옥 탐지 등 고보안 요구에 한계가 있다.
|
||||||
|
- 사용자가 URL만 보고 접근하므로 피싱/가짜 PWA에 취약할 수 있다.
|
||||||
|
|
||||||
|
적합한 경우:
|
||||||
|
|
||||||
|
- 사전 PoC 또는 내부 데모
|
||||||
|
- 앱 설치 전 임시 승인 채널
|
||||||
|
- 낮은 위험도의 보조 확인 화면
|
||||||
|
|
||||||
|
비권장:
|
||||||
|
|
||||||
|
- Baron Safe의 최종 인증 장치 역할을 PWA만으로 구현하는 것은 비권장이다.
|
||||||
|
- 특히 "등록 기기에서 서명된 승인"을 핵심 보안 근거로 삼으려면 네이티브 앱 또는 Flutter 앱이 더 적합하다.
|
||||||
|
|
||||||
|
## 8. WebView/하이브리드 방식
|
||||||
|
|
||||||
|
네이티브 앱 껍데기에 웹 화면을 담는 방식이다. 은행앱처럼 일부 화면은 앱, 일부 화면은 웹으로 전환하는 구조도 여기에 포함된다.
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- 앱 배포 후 UI 일부를 웹에서 빠르게 바꿀 수 있다.
|
||||||
|
- 네이티브 푸시와 웹 UI를 결합할 수 있다.
|
||||||
|
- 기존 웹 자산을 재사용할 수 있다.
|
||||||
|
|
||||||
|
단점:
|
||||||
|
|
||||||
|
- 인증 승인 앱의 핵심 화면이 웹으로 과도하게 넘어가면 보안 경계가 흐려진다.
|
||||||
|
- WebView 내 세션, 쿠키, 딥링크, 리다이렉트 처리가 복잡해질 수 있다.
|
||||||
|
- 피싱 화면과 진짜 승인 화면을 사용자가 구분하기 어려워질 수 있다.
|
||||||
|
|
||||||
|
권장 방향:
|
||||||
|
|
||||||
|
- Baron Safe의 핵심 승인 화면은 앱 네이티브/Flutter 화면으로 유지한다.
|
||||||
|
- 약관, 도움말, 공지, 기기 관리 안내처럼 보안 민감도가 낮은 화면만 WebView로 처리한다.
|
||||||
|
- 승인/거절/서명/생체 인증은 WebView가 아니라 앱 네이티브 레이어에서 수행한다.
|
||||||
|
|
||||||
|
## 9. 앱/웹앱 전환 방식
|
||||||
|
|
||||||
|
회의에서 언급된 "은행앱처럼 앱/웹앱 전환"은 다음 구조로 해석할 수 있다.
|
||||||
|
|
||||||
|
### 9.1 웹에서 앱으로 전환
|
||||||
|
|
||||||
|
사용자가 웹 로그인 화면에서 전화번호를 입력하면, Baron SSO가 승인 요청을 만들고 앱으로 전환한다.
|
||||||
|
|
||||||
|
가능한 방식:
|
||||||
|
|
||||||
|
- Universal Links(iOS), App Links(Android)
|
||||||
|
- 커스텀 URL Scheme
|
||||||
|
- Push 알림 탭
|
||||||
|
- QR 코드 스캔
|
||||||
|
|
||||||
|
권장:
|
||||||
|
|
||||||
|
- 기본은 Push 알림으로 앱을 열게 한다.
|
||||||
|
- 앱이 이미 설치되어 있으면 Universal/App Link로 바로 열 수 있다.
|
||||||
|
- 앱이 없으면 설치 안내 페이지로 이동한다.
|
||||||
|
|
||||||
|
### 9.2 앱에서 웹으로 복귀
|
||||||
|
|
||||||
|
사용자가 앱에서 승인하면 요청 기기 웹 화면은 poll 또는 server push로 승인 상태를 확인하고 로그인 완료를 진행한다.
|
||||||
|
|
||||||
|
권장:
|
||||||
|
|
||||||
|
- 앱이 요청 기기로 직접 토큰을 전달하지 않는다.
|
||||||
|
- 앱은 Baron SSO 서버에 승인만 보낸다.
|
||||||
|
- 요청 기기는 Baron SSO 서버에서 승인 상태를 확인한다.
|
||||||
|
- 최종 RP 로그인은 기존 Hydra/OIDC 흐름으로 완료한다.
|
||||||
|
|
||||||
|
## 10. 푸시 기능 비교
|
||||||
|
|
||||||
|
### 10.1 플랫폼별 푸시 구조
|
||||||
|
|
||||||
|
| 대상 | 기본 푸시 채널 | 설명 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Android 네이티브/Flutter | FCM | Google Firebase Cloud Messaging 사용 |
|
||||||
|
| iOS 네이티브/Flutter | APNs, 또는 FCM 경유 APNs | Apple Push Notification service 사용 |
|
||||||
|
| Android PWA | Web Push + Service Worker | Chrome/브라우저 기반 |
|
||||||
|
| iOS PWA | Web Push + Home Screen web app | iOS/iPadOS 16.4 이후 홈 화면 웹앱에서 지원 |
|
||||||
|
|
||||||
|
참고:
|
||||||
|
|
||||||
|
- Firebase Cloud Messaging은 Android, iOS, Web에 메시지를 보낼 수 있는 공식 서비스다.
|
||||||
|
- Apple은 iOS/iPadOS 16.4부터 홈 화면에 추가된 웹앱의 Web Push를 지원한다고 설명한다.
|
||||||
|
- Web Push API는 Service Worker 기반으로 서버가 웹앱에 비동기 메시지를 보낼 수 있게 한다.
|
||||||
|
|
||||||
|
### 10.2 푸시 효율성 비교
|
||||||
|
|
||||||
|
| 방식 | 도달 안정성 | 즉시성 | 사용자 설정 영향 | 구현 난이도 | Baron Safe 적합성 |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| 네이티브 앱 + FCM/APNs | 높음 | 높음 | 알림 권한/절전 정책 영향 | 중간 | 매우 높음 |
|
||||||
|
| Flutter 앱 + FCM/APNs | 높음 | 높음 | 알림 권한/절전 정책 영향 | 중간 | 높음 |
|
||||||
|
| PWA Web Push | 중간 | 중간 | 브라우저/홈화면/권한 영향 큼 | 중간 | 제한적 |
|
||||||
|
| 웹 폴링 | 낮음 | 낮음 | 사용자가 화면을 열어둬야 함 | 낮음 | 보조 수단 |
|
||||||
|
| SMS | 중간 | 중간 | 통신사/비용/지연 영향 | 낮음 | fallback |
|
||||||
|
|
||||||
|
판단:
|
||||||
|
|
||||||
|
- 승인 로그인에서 가장 중요한 것은 "요청이 왔을 때 사용자가 빠르게 인지하고 승인할 수 있는가"이다.
|
||||||
|
- 이 기준에서는 네이티브 앱 또는 Flutter 앱의 FCM/APNs 기반 푸시가 가장 적합하다.
|
||||||
|
- PWA Web Push는 가능하지만, iOS에서는 홈 화면 추가와 권한 허용이 전제이며, 운영 통제가 네이티브 앱보다 약하다.
|
||||||
|
- SMS는 앱 미설치자 fallback으로는 가능하지만, Baron Safe의 주 인증 채널로 삼기에는 비용과 보안 한계가 있다.
|
||||||
|
|
||||||
|
## 11. 승인 프로세스 선택지
|
||||||
|
|
||||||
|
### 11.1 기본 권장: 요청 생성 -> 앱 승인 -> 로그인 진행
|
||||||
|
|
||||||
|
프로세스:
|
||||||
|
|
||||||
|
1. 요청 기기에서 전화번호 입력
|
||||||
|
2. Baron SSO가 승인 요청 생성
|
||||||
|
3. Baron Safe 앱으로 Push 발송
|
||||||
|
4. 사용자가 요청 정보를 확인
|
||||||
|
5. 사용자가 승인
|
||||||
|
6. 앱이 서명된 승인 응답 전송
|
||||||
|
7. 요청 기기 로그인 진행
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- 가장 명확하고 안전하다.
|
||||||
|
- 사용자가 요청을 확인한 뒤에만 로그인이 진행된다.
|
||||||
|
- 감사 로그와 상태 전이가 단순하다.
|
||||||
|
|
||||||
|
단점:
|
||||||
|
|
||||||
|
- 앱 푸시 지연 또는 사용자가 앱을 보지 않으면 로그인 대기 시간이 발생한다.
|
||||||
|
|
||||||
|
권장:
|
||||||
|
|
||||||
|
- Baron Safe 기본 프로세스로 채택한다.
|
||||||
|
|
||||||
|
### 11.2 선승인 후차단 방식
|
||||||
|
|
||||||
|
회의 중 언급된 "선승인 후차단"은 두 가지로 해석될 수 있다.
|
||||||
|
|
||||||
|
해석 A:
|
||||||
|
|
||||||
|
- 낮은 위험도의 RP는 일단 승인 흐름을 빠르게 진행하고, 이상 징후가 있으면 사후 차단한다.
|
||||||
|
|
||||||
|
해석 B:
|
||||||
|
|
||||||
|
- 사용자가 사전에 특정 RP/기기를 신뢰 등록해두고, 이후 요청은 자동 승인에 가깝게 처리하되 위험 발생 시 차단한다.
|
||||||
|
|
||||||
|
검토:
|
||||||
|
|
||||||
|
- 이 방식은 UX는 빠르지만 Baron Safe의 핵심인 "요청별 명시 승인"이 약해진다.
|
||||||
|
- 전화번호 단독 로그인의 문제를 다시 일부 가져올 수 있다.
|
||||||
|
- 고위험 RP에는 부적합하다.
|
||||||
|
|
||||||
|
허용 가능 조건:
|
||||||
|
|
||||||
|
- 동일 기기, 동일 RP, 동일 네트워크 등 낮은 위험 조건이 모두 충족될 때
|
||||||
|
- 짧은 기간의 trust window 안에서만
|
||||||
|
- 사용자가 명시적으로 "이 기기에서 24시간 동안 다시 묻지 않기"를 선택한 경우
|
||||||
|
- 관리자 정책상 허용된 RP에 한정
|
||||||
|
|
||||||
|
권장:
|
||||||
|
|
||||||
|
- 1차 개발에서는 제외한다.
|
||||||
|
- 2차 이후 "신뢰 기기/신뢰 RP" 기능으로 제한 검토한다.
|
||||||
|
|
||||||
|
### 11.3 선차단 후승인 방식
|
||||||
|
|
||||||
|
위험 신호가 있는 요청은 앱에 바로 승인 버튼을 주지 않고, 추가 확인 또는 관리자 확인을 요구하는 방식이다.
|
||||||
|
|
||||||
|
예시:
|
||||||
|
|
||||||
|
- 평소와 다른 국가/IP
|
||||||
|
- 짧은 시간 내 반복 요청
|
||||||
|
- 미등록 RP
|
||||||
|
- 고위험 관리자 기능 접근
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- 공격성 요청을 사용자가 실수 승인하는 위험을 줄인다.
|
||||||
|
|
||||||
|
단점:
|
||||||
|
|
||||||
|
- 정책과 UX가 복잡해진다.
|
||||||
|
|
||||||
|
권장:
|
||||||
|
|
||||||
|
- 1차에서는 위험 요청을 "승인 불가/거절 권고"로 표시하는 정도로 시작한다.
|
||||||
|
- 관리자 승인까지 포함하는 복잡한 흐름은 후속 단계로 둔다.
|
||||||
|
|
||||||
|
## 12. 푸시 피로 공격 방지
|
||||||
|
|
||||||
|
Baron Safe는 푸시 기반 승인 앱이므로 푸시 피로 공격을 반드시 고려해야 한다.
|
||||||
|
|
||||||
|
필수 대책:
|
||||||
|
|
||||||
|
- 사용자별, 전화번호별, IP별, RP별 rate limit
|
||||||
|
- 같은 요청을 반복 발송하지 않는 collapse key 또는 deduplication
|
||||||
|
- 승인 화면에 number matching 표시
|
||||||
|
- 요청 기기 화면에도 동일한 숫자 코드 표시
|
||||||
|
- 앱에서 해당 숫자를 확인하거나 입력해야 승인 가능
|
||||||
|
- 반복 요청 발생 시 자동 차단 및 감사 로그 기록
|
||||||
|
- 고위험 요청은 앱에서 기본 버튼을 "거절" 중심으로 배치
|
||||||
|
|
||||||
|
예시 UX:
|
||||||
|
|
||||||
|
```text
|
||||||
|
한맥 ERP 로그인을 승인하시겠습니까?
|
||||||
|
|
||||||
|
요청 기기: Chrome on Windows
|
||||||
|
요청 위치: 203.0.113.xxx
|
||||||
|
요청 코드: 42
|
||||||
|
|
||||||
|
요청 기기에 표시된 숫자와 일치할 때만 승인하세요.
|
||||||
|
|
||||||
|
[거절] [승인]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 13. 최종 권고
|
||||||
|
|
||||||
|
1차 개발은 다음 방향이 가장 현실적이다.
|
||||||
|
|
||||||
|
1. 앱은 Flutter 기반으로 개발한다.
|
||||||
|
2. 기능은 승인 요청 확인, 승인, 거절, 기기 등록/해제로 제한한다.
|
||||||
|
3. 핵심 승인 화면은 WebView가 아닌 앱 화면으로 구현한다.
|
||||||
|
4. Push는 FCM/APNs 기반으로 구현한다.
|
||||||
|
5. PWA/Web Push는 PoC 또는 fallback으로만 검토한다.
|
||||||
|
6. 승인 응답은 기기 private key로 서명한다.
|
||||||
|
7. 앱 승인 전 생체 인증 또는 앱 PIN을 요구한다.
|
||||||
|
8. 푸시 피로 공격 방지를 위해 rate limit와 number matching을 1차부터 포함한다.
|
||||||
|
9. 선승인 후차단 방식은 1차에서 제외하고, 신뢰 기기/신뢰 RP 기능으로 후속 검토한다.
|
||||||
|
|
||||||
|
Baron Safe는 "작은 앱"이어야 한다. 기능을 많이 넣는 앱이 아니라, 로그인 요청을 안전하게 확인하고 승인하는 단 하나의 일을 확실히 하는 앱으로 설계해야 한다.
|
||||||
|
|
||||||
|
## 14. 참고 공식 문서
|
||||||
|
|
||||||
|
- Firebase Cloud Messaging: https://firebase.google.com/docs/cloud-messaging
|
||||||
|
- Firebase Cloud Messaging for Flutter: https://firebase.google.com/docs/cloud-messaging/flutter/get-started
|
||||||
|
- Apple Notifications: https://developer.apple.com/notifications/
|
||||||
|
- Web Push for Web Apps on iOS and iPadOS: https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/
|
||||||
|
- MDN Push API: https://developer.mozilla.org/en-US/docs/Web/API/Push_API
|
||||||
|
- Android Notifications: https://developer.android.com/develop/ui/views/notifications
|
||||||
|
- Flutter platform channels: https://docs.flutter.dev/platform-integration/platform-channels
|
||||||
@@ -0,0 +1,691 @@
|
|||||||
|
# 비표준 로그인 승인 화면 공통화 개발안
|
||||||
|
|
||||||
|
작성일: 2026-06-23
|
||||||
|
|
||||||
|
## 1. 목적
|
||||||
|
|
||||||
|
본 문서는 Baron SSO의 비표준 로그인 방식인 로그인 링크, Magic Link, QR 로그인의 승인 화면을 공통화하기 위한 단계별 개발안을 정의한다.
|
||||||
|
|
||||||
|
목표는 다음과 같다.
|
||||||
|
|
||||||
|
- 사용자가 승인 전에 "어떤 기기가 로그인되려는지" 쉽게 확인하도록 한다.
|
||||||
|
- 링크/QR/Magic Link마다 흩어진 승인 UX를 하나의 공통 패턴으로 정리한다.
|
||||||
|
- 기존 로그인 동작에 영향을 주지 않도록 opt-in 방식으로 개발한다.
|
||||||
|
- 개발 후 기존 표준 로그인, 링크 로그인, QR 로그인, OIDC 흐름에 영향이 없는지 반드시 확인한다.
|
||||||
|
|
||||||
|
## 2. 현재 상태 요약
|
||||||
|
|
||||||
|
현재 코드 기준 관련 위치는 다음과 같다.
|
||||||
|
|
||||||
|
| 영역 | 파일 | 현재 역할 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| UserFront API client | `userfront/lib/core/services/auth_proxy_service.dart` | 링크, Magic Link, QR init/poll/approve API 호출 |
|
||||||
|
| QR 승인 화면 | `userfront/lib/features/auth/presentation/approve_qr_screen.dart` | QR 스캔 후 자동 승인 처리 |
|
||||||
|
| 로그인 화면 | `userfront/lib/features/auth/presentation/login_screen.dart` | 비밀번호, 링크, QR 로그인 UI 및 poll 처리 |
|
||||||
|
| 라우팅 | `userfront/lib/main.dart` | `/ql/:ref`, `/qr/approve` 계열 라우트 연결 |
|
||||||
|
| Backend route | `backend/cmd/server/main.go` | `/api/v1/auth/qr/approve`, `/magic-link/verify`, `/enchanted-link/poll` 등록 |
|
||||||
|
| Backend handler | `backend/internal/handler/auth_handler.go` | QR/link pending 상태, Redis 저장, 감사 로그 처리 |
|
||||||
|
|
||||||
|
이미 존재하는 재사용 가능 코드도 있다.
|
||||||
|
|
||||||
|
| 기존 코드 | 재사용 이유 |
|
||||||
|
| --- | --- |
|
||||||
|
| `storeQrMeta`, `loadQrMeta` | QR 요청 기기의 IP/User-Agent 저장 구조가 이미 있다. |
|
||||||
|
| `storeLoginApproverMeta`, `loadLoginApproverMeta` | 링크 승인자의 IP/User-Agent/session 정보를 저장하는 구조가 이미 있다. |
|
||||||
|
| `writeQrAuditLog`, `writeLinkAuditLog` | 승인 성공 감사 로그가 이미 있다. |
|
||||||
|
| `AuthProxyService.approveQrLogin` | QR 승인 API client가 이미 있다. |
|
||||||
|
| `AuthProxyService.verifyMagicLink`, `verifyLoginCode` | 링크/Magic Link 승인 API client가 이미 있다. |
|
||||||
|
|
||||||
|
## 3. 개발 원칙
|
||||||
|
|
||||||
|
이번 개발은 기존 소스코드 영향도를 최소화하기 위해 아래 원칙을 따른다.
|
||||||
|
|
||||||
|
1. 기존 API 응답 형식을 즉시 변경하지 않는다.
|
||||||
|
2. 기존 QR 자동 승인 화면을 바로 제거하지 않는다.
|
||||||
|
3. 새 공통 승인 화면은 feature flag가 켜졌을 때만 사용한다.
|
||||||
|
4. Backend에는 조회용 metadata API를 먼저 추가하고, 기존 승인 API는 최대한 유지한다.
|
||||||
|
5. 공통 UI는 기존 API client 위에 얇게 얹고, 성공/실패 동작은 기존 흐름과 동일하게 유지한다.
|
||||||
|
6. 차단 버튼은 1차 개발에서는 UI placeholder 또는 별도 후속 단계로 분리할 수 있다.
|
||||||
|
7. 개발 후에는 기존 자동 승인 흐름과 새 수동 승인 흐름을 모두 테스트한다.
|
||||||
|
|
||||||
|
## 4. 목표 화면
|
||||||
|
|
||||||
|
공통 승인 화면은 다음 정보를 표시한다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
새 로그인 요청
|
||||||
|
|
||||||
|
요청 서비스: Baron SSO 또는 RP 이름
|
||||||
|
요청 방식: QR 로그인 / 로그인 링크 / Magic Link
|
||||||
|
요청 기기: Chrome on Windows
|
||||||
|
요청 위치/IP: 203.0.113.xxx
|
||||||
|
요청 시간: 2026-06-23 10:30
|
||||||
|
|
||||||
|
이 요청을 승인하면 해당 기기가 로그인됩니다.
|
||||||
|
본인이 요청한 로그인이 맞습니까?
|
||||||
|
|
||||||
|
[내 요청입니다] [내 요청이 아닙니다] [잘 모르겠습니다]
|
||||||
|
```
|
||||||
|
|
||||||
|
1차 개발에서는 버튼 동작을 다음처럼 나눈다.
|
||||||
|
|
||||||
|
| 버튼 | 1차 처리 | 후속 처리 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 내 요청입니다 | 기존 승인 API 호출 | 유지 |
|
||||||
|
| 내 요청이 아닙니다 | pending block API가 있으면 호출, 없으면 안전 안내 후 승인하지 않음 | `blocked_by_user` 상태 저장 구현 |
|
||||||
|
| 잘 모르겠습니다 | 승인하지 않고 안내 화면 표시 | `blocked_uncertain` 또는 `hold` 상태 구현 |
|
||||||
|
|
||||||
|
## 5. Backend 개발안
|
||||||
|
|
||||||
|
### 5.1 새 응답 모델 추가
|
||||||
|
|
||||||
|
Backend에 공통 승인 요청 metadata 응답 모델을 추가한다.
|
||||||
|
|
||||||
|
권장 위치:
|
||||||
|
|
||||||
|
```text
|
||||||
|
backend/internal/domain/auth_models.go
|
||||||
|
```
|
||||||
|
|
||||||
|
예시:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type LoginApprovalRequestInfo struct {
|
||||||
|
PendingRef string `json:"pendingRef"`
|
||||||
|
ApprovalType string `json:"approvalType"` // qr, link, magic_link
|
||||||
|
Status string `json:"status"` // pending, approved, success, expired, blocked
|
||||||
|
RequestService string `json:"requestService,omitempty"`
|
||||||
|
RequestClientID string `json:"requestClientId,omitempty"`
|
||||||
|
RequestIPAddress string `json:"requestIpAddress,omitempty"`
|
||||||
|
RequestUserAgent string `json:"requestUserAgent,omitempty"`
|
||||||
|
RequestDevice string `json:"requestDevice,omitempty"`
|
||||||
|
RequestedAt string `json:"requestedAt,omitempty"`
|
||||||
|
ExpiresAt string `json:"expiresAt,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
주의:
|
||||||
|
|
||||||
|
- `requestIpAddress`는 전체 IP를 그대로 노출하지 않고 마스킹한다.
|
||||||
|
- `requestUserAgent`는 원문과 표시용을 분리하는 것이 좋다.
|
||||||
|
- `pendingRef`가 유효하지 않아도 사용자 존재 여부를 추론할 수 있는 메시지를 주지 않는다.
|
||||||
|
|
||||||
|
### 5.2 공통 승인 정보 조회 API 추가
|
||||||
|
|
||||||
|
새 API를 추가한다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /api/v1/auth/approval/info
|
||||||
|
```
|
||||||
|
|
||||||
|
요청:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pendingRef": "abc123",
|
||||||
|
"approvalType": "qr"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
응답:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pendingRef": "abc123",
|
||||||
|
"approvalType": "qr",
|
||||||
|
"status": "pending",
|
||||||
|
"requestService": "Baron SSO",
|
||||||
|
"requestClientId": "userfront",
|
||||||
|
"requestIpAddress": "203.0.113.xxx",
|
||||||
|
"requestUserAgent": "Chrome on Windows",
|
||||||
|
"requestDevice": "Desktop browser",
|
||||||
|
"requestedAt": "2026-06-23T10:30:00+09:00",
|
||||||
|
"expiresAt": "2026-06-23T10:35:00+09:00"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
구현 위치:
|
||||||
|
|
||||||
|
```text
|
||||||
|
backend/cmd/server/main.go
|
||||||
|
backend/internal/handler/auth_handler.go
|
||||||
|
```
|
||||||
|
|
||||||
|
route 추가:
|
||||||
|
|
||||||
|
```go
|
||||||
|
auth.Post("/approval/info", authHandler.GetLoginApprovalInfo)
|
||||||
|
```
|
||||||
|
|
||||||
|
처리 방식:
|
||||||
|
|
||||||
|
1. `pendingRef`, `approvalType`을 받는다.
|
||||||
|
2. `approvalType=qr`이면 `loadQrMeta(pendingRef)`를 우선 사용한다.
|
||||||
|
3. `approvalType=link` 또는 `magic_link`이면 `loadLoginMeta(pendingRef)`와 기존 Redis 상태를 조회한다.
|
||||||
|
4. Redis의 `prefixSession + pendingRef` 상태를 확인한다.
|
||||||
|
5. 표시 가능한 정보만 응답한다.
|
||||||
|
|
||||||
|
영향도:
|
||||||
|
|
||||||
|
- 신규 API 추가만 수행하므로 기존 API 동작에 영향 없음.
|
||||||
|
- 기존 `/qr/approve`, `/magic-link/verify`, `/login/code/verify`는 변경하지 않는다.
|
||||||
|
|
||||||
|
### 5.3 요청 metadata 저장 보강
|
||||||
|
|
||||||
|
QR은 이미 `storeQrMeta(pendingRef, c)`가 있다.
|
||||||
|
|
||||||
|
링크 계열은 init 시점의 요청자 metadata 저장이 부족할 수 있으므로, 별도 공통 metadata 저장 함수를 추가한다.
|
||||||
|
|
||||||
|
권장 함수:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (h *AuthHandler) storeApprovalRequestMeta(pendingRef, approvalType, clientID string, c *fiber.Ctx, ttl time.Duration)
|
||||||
|
func (h *AuthHandler) loadApprovalRequestMeta(pendingRef string) (approvalRequestMeta, bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
권장 Redis key:
|
||||||
|
|
||||||
|
```text
|
||||||
|
approval_request_meta:{pendingRef}
|
||||||
|
```
|
||||||
|
|
||||||
|
1차 적용 범위:
|
||||||
|
|
||||||
|
- `InitQRLogin`: 기존 `storeQrMeta` 유지, 새 metadata도 병행 저장
|
||||||
|
- `InitEnchantedLink`: pendingRef 생성 직후 새 metadata 저장
|
||||||
|
- Magic Link는 token 자체보다 연결된 `pendingRef` 기준으로 metadata 조회
|
||||||
|
|
||||||
|
영향도 최소화 방법:
|
||||||
|
|
||||||
|
- 기존 `prefixQrMeta`는 제거하지 않는다.
|
||||||
|
- 새 `approval_request_meta`는 조회 실패해도 기존 동작을 막지 않는다.
|
||||||
|
- metadata 저장 실패는 로그인 실패로 처리하지 않는다.
|
||||||
|
|
||||||
|
### 5.4 block API는 2단계로 분리
|
||||||
|
|
||||||
|
1차 공통 화면에서 `내 요청이 아닙니다` 버튼은 승인하지 않는 것만으로도 기존 자동 승인보다 안전하다.
|
||||||
|
|
||||||
|
다만 최종적으로는 아래 API가 필요하다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /api/v1/auth/approval/block
|
||||||
|
```
|
||||||
|
|
||||||
|
요청:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pendingRef": "abc123",
|
||||||
|
"approvalType": "qr",
|
||||||
|
"decision": "blocked_by_user"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
응답:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "blocked_by_user"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2단계 구현 시 처리:
|
||||||
|
|
||||||
|
1. `prefixSession + pendingRef`의 status를 `blocked_by_user`로 변경한다.
|
||||||
|
2. poll API가 `blocked_by_user`를 반환하게 한다.
|
||||||
|
3. 감사 로그 `login.request.blocked_by_user`를 남긴다.
|
||||||
|
|
||||||
|
1차 개발에서는 이 API를 만들되 feature flag로 UI 연결을 늦춰도 된다.
|
||||||
|
|
||||||
|
## 6. UserFront 개발안
|
||||||
|
|
||||||
|
### 6.1 공통 모델 추가
|
||||||
|
|
||||||
|
권장 파일:
|
||||||
|
|
||||||
|
```text
|
||||||
|
userfront/lib/features/auth/domain/login_approval_request.dart
|
||||||
|
```
|
||||||
|
|
||||||
|
예시 필드:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
class LoginApprovalRequest {
|
||||||
|
final String pendingRef;
|
||||||
|
final String approvalType;
|
||||||
|
final String status;
|
||||||
|
final String? requestService;
|
||||||
|
final String? requestClientId;
|
||||||
|
final String? requestIpAddress;
|
||||||
|
final String? requestUserAgent;
|
||||||
|
final String? requestDevice;
|
||||||
|
final DateTime? requestedAt;
|
||||||
|
final DateTime? expiresAt;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
영향도:
|
||||||
|
|
||||||
|
- 신규 파일 추가이므로 기존 화면 영향 없음.
|
||||||
|
|
||||||
|
### 6.2 AuthProxyService API 추가
|
||||||
|
|
||||||
|
권장 파일:
|
||||||
|
|
||||||
|
```text
|
||||||
|
userfront/lib/core/services/auth_proxy_service.dart
|
||||||
|
```
|
||||||
|
|
||||||
|
추가 함수:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
static Future<Map<String, dynamic>> getLoginApprovalInfo({
|
||||||
|
required String pendingRef,
|
||||||
|
required String approvalType,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
요청 대상:
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /api/v1/auth/approval/info
|
||||||
|
```
|
||||||
|
|
||||||
|
영향도:
|
||||||
|
|
||||||
|
- 기존 함수 수정 없이 새 함수만 추가한다.
|
||||||
|
- 기존 QR/link 로그인 호출 경로는 그대로 유지한다.
|
||||||
|
|
||||||
|
### 6.3 공통 승인 화면 추가
|
||||||
|
|
||||||
|
권장 파일:
|
||||||
|
|
||||||
|
```text
|
||||||
|
userfront/lib/features/auth/presentation/login_approval_screen.dart
|
||||||
|
```
|
||||||
|
|
||||||
|
props:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
class LoginApprovalScreen extends StatefulWidget {
|
||||||
|
final String pendingRef;
|
||||||
|
final String approvalType; // qr, link, magic_link
|
||||||
|
final bool autoApproveFallback;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
화면 동작:
|
||||||
|
|
||||||
|
1. 진입 시 `AuthProxyService.getLoginApprovalInfo` 호출
|
||||||
|
2. 요청 정보 표시
|
||||||
|
3. `내 요청입니다` 클릭 시 approvalType별 기존 API 호출
|
||||||
|
4. 성공 시 기존 성공 화면 또는 dashboard 이동
|
||||||
|
5. `내 요청이 아닙니다`, `잘 모르겠습니다`는 세션 발급 없이 안내
|
||||||
|
|
||||||
|
approvalType별 기존 API 매핑:
|
||||||
|
|
||||||
|
| approvalType | 승인 API |
|
||||||
|
| --- | --- |
|
||||||
|
| `qr` | `AuthProxyService.approveQrLogin` |
|
||||||
|
| `magic_link` | `AuthProxyService.verifyMagicLink` |
|
||||||
|
| `link_code` | `AuthProxyService.verifyLoginCode` |
|
||||||
|
|
||||||
|
주의:
|
||||||
|
|
||||||
|
- Magic Link는 token 기반이므로 `pendingRef`만으로 승인할 수 없는 경우가 있다.
|
||||||
|
- 따라서 Magic Link 공통화는 token verify 화면의 wrapper로 시작하고, 1차에서는 QR부터 적용하는 것이 안전하다.
|
||||||
|
|
||||||
|
### 6.4 QR 승인 화면 점진 전환
|
||||||
|
|
||||||
|
현재 `ApproveQrScreen`은 진입 후 자동 승인한다.
|
||||||
|
|
||||||
|
1차 변경안:
|
||||||
|
|
||||||
|
```text
|
||||||
|
기존:
|
||||||
|
/ql/:ref -> ApproveQrScreen -> 자동 approveQrLogin
|
||||||
|
|
||||||
|
신규 feature flag ON:
|
||||||
|
/ql/:ref -> LoginApprovalScreen(approvalType=qr) -> 사용자가 직접 승인
|
||||||
|
|
||||||
|
feature flag OFF:
|
||||||
|
/ql/:ref -> ApproveQrScreen 유지
|
||||||
|
```
|
||||||
|
|
||||||
|
권장 feature flag:
|
||||||
|
|
||||||
|
```text
|
||||||
|
USERFRONT_COMMON_APPROVAL_SCREEN=true
|
||||||
|
```
|
||||||
|
|
||||||
|
또는 Flutter runtime env:
|
||||||
|
|
||||||
|
```text
|
||||||
|
VITE/ENV 성격에 맞는 userfront runtime env key로 추가
|
||||||
|
```
|
||||||
|
|
||||||
|
영향도 최소화:
|
||||||
|
|
||||||
|
- 기존 `ApproveQrScreen`은 삭제하지 않는다.
|
||||||
|
- 라우트에서 feature flag만 보고 새 화면으로 분기한다.
|
||||||
|
- 문제가 있으면 flag를 끄고 즉시 기존 자동승인으로 복귀한다.
|
||||||
|
|
||||||
|
### 6.5 링크/Magic Link 적용 순서
|
||||||
|
|
||||||
|
링크와 Magic Link는 QR보다 변형이 많다. 따라서 QR 적용 후 안정화되면 다음 순서로 진행한다.
|
||||||
|
|
||||||
|
1. QR 수동 승인 화면 적용
|
||||||
|
2. Magic Link verify 화면에 요청 정보 표시만 추가
|
||||||
|
3. Enchanted Link 승인 흐름에 공통 요청 정보 표시
|
||||||
|
4. `내 요청이 아닙니다` block API 연결
|
||||||
|
|
||||||
|
## 7. 단계별 개발 순서
|
||||||
|
|
||||||
|
### Phase 0. 기준 테스트 확보
|
||||||
|
|
||||||
|
목표:
|
||||||
|
|
||||||
|
- 개발 전 현재 동작이 정상임을 확인한다.
|
||||||
|
|
||||||
|
확인 항목:
|
||||||
|
|
||||||
|
1. 비밀번호 로그인 정상
|
||||||
|
2. 링크 로그인 init/poll 정상
|
||||||
|
3. Magic Link verify 정상
|
||||||
|
4. QR init/poll/approve 정상
|
||||||
|
5. OIDC login_challenge가 있는 로그인 정상
|
||||||
|
|
||||||
|
권장 명령:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./backend/internal/handler -run 'Test.*QR|Test.*Link|Test.*LoginCode|Test.*Password'
|
||||||
|
flutter test
|
||||||
|
npm run test -- tests/orgfront-auto-login.spec.ts --project=chromium
|
||||||
|
```
|
||||||
|
|
||||||
|
프로젝트 환경에 따라 실제 명령은 조정한다.
|
||||||
|
|
||||||
|
### Phase 1. Backend 조회 API만 추가
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
1. `LoginApprovalRequestInfo` 모델 추가
|
||||||
|
2. `GetLoginApprovalInfo` handler 추가
|
||||||
|
3. `POST /api/v1/auth/approval/info` route 추가
|
||||||
|
4. QR metadata 조회 테스트 추가
|
||||||
|
5. 잘못된 pendingRef 응답 테스트 추가
|
||||||
|
|
||||||
|
영향도:
|
||||||
|
|
||||||
|
- 신규 API만 추가되므로 기존 로그인 흐름 영향 없음.
|
||||||
|
|
||||||
|
완료 기준:
|
||||||
|
|
||||||
|
- 기존 테스트 모두 통과
|
||||||
|
- 신규 API 테스트 통과
|
||||||
|
- 기존 QR approve API 응답 변경 없음
|
||||||
|
|
||||||
|
### Phase 2. Backend metadata 저장 보강
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
1. `approval_request_meta:{pendingRef}` Redis 저장 함수 추가
|
||||||
|
2. `InitQRLogin`에 병행 저장 추가
|
||||||
|
3. `InitEnchantedLink`에 병행 저장 추가
|
||||||
|
4. 저장 실패가 로그인 실패로 이어지지 않게 처리
|
||||||
|
|
||||||
|
영향도:
|
||||||
|
|
||||||
|
- Redis key만 추가된다.
|
||||||
|
- 기존 key 삭제/변경 없음.
|
||||||
|
|
||||||
|
완료 기준:
|
||||||
|
|
||||||
|
- Redis metadata 미저장 상황에서도 로그인 성공
|
||||||
|
- QR/link 기존 poll/approve 정상
|
||||||
|
|
||||||
|
### Phase 3. UserFront 공통 화면 신규 추가
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
1. `LoginApprovalRequest` 모델 추가
|
||||||
|
2. `AuthProxyService.getLoginApprovalInfo` 추가
|
||||||
|
3. `LoginApprovalScreen` 신규 추가
|
||||||
|
4. QR 승인용 UI 연결 로직 작성
|
||||||
|
5. feature flag 기본값 OFF
|
||||||
|
|
||||||
|
영향도:
|
||||||
|
|
||||||
|
- 기본값 OFF이므로 배포해도 기존 UX 유지
|
||||||
|
- 새 화면은 직접 route 또는 테스트에서만 접근
|
||||||
|
|
||||||
|
완료 기준:
|
||||||
|
|
||||||
|
- feature flag OFF에서 기존 QR 자동 승인 동작 동일
|
||||||
|
- feature flag ON에서 새 승인 화면 표시
|
||||||
|
- 승인 버튼 클릭 시 기존 `approveQrLogin` 호출
|
||||||
|
|
||||||
|
### Phase 4. QR 라우트 opt-in 연결
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
1. `userfront/lib/main.dart`의 QR approve route에서 feature flag 분기
|
||||||
|
2. flag ON이면 `LoginApprovalScreen(approvalType: 'qr')`
|
||||||
|
3. flag OFF이면 기존 `ApproveQrScreen`
|
||||||
|
|
||||||
|
영향도:
|
||||||
|
|
||||||
|
- flag OFF 기준 영향 없음.
|
||||||
|
|
||||||
|
완료 기준:
|
||||||
|
|
||||||
|
- flag OFF e2e: 기존 QR 로그인 성공
|
||||||
|
- flag ON e2e: 요청 정보 확인 후 수동 승인 성공
|
||||||
|
- 미로그인 승인 기기는 기존처럼 로그인 유도
|
||||||
|
|
||||||
|
### Phase 5. 링크/Magic Link 화면 확장
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
1. Magic Link verify 진입 화면에서 approval info 조회
|
||||||
|
2. 가능한 경우 요청 정보 표시
|
||||||
|
3. 기존 verify API 호출은 그대로 유지
|
||||||
|
4. Enchanted Link code verify에도 같은 UI wrapper 적용 검토
|
||||||
|
|
||||||
|
영향도:
|
||||||
|
|
||||||
|
- token 기반 링크는 기존 URL 처리와 충돌 가능성이 있어 QR보다 늦게 적용한다.
|
||||||
|
|
||||||
|
완료 기준:
|
||||||
|
|
||||||
|
- 기존 Magic Link URL이 계속 동작
|
||||||
|
- verifyOnly=true 흐름이 깨지지 않음
|
||||||
|
- pendingRef 없는 Magic Link도 기존처럼 처리
|
||||||
|
|
||||||
|
### Phase 6. block API 연결
|
||||||
|
|
||||||
|
작업:
|
||||||
|
|
||||||
|
1. `POST /api/v1/auth/approval/block` 추가
|
||||||
|
2. pending 상태 `blocked_by_user` 저장
|
||||||
|
3. QR/link poll에서 `blocked_by_user` 반환
|
||||||
|
4. UserFront에서 `내 요청이 아닙니다` 클릭 시 block 호출
|
||||||
|
5. 감사 로그 추가
|
||||||
|
|
||||||
|
영향도:
|
||||||
|
|
||||||
|
- poll 응답에 새 code가 추가된다.
|
||||||
|
- 기존 UserFront가 모르는 code를 받지 않도록, UI 연결 전 poll 처리부터 방어적으로 추가한다.
|
||||||
|
|
||||||
|
완료 기준:
|
||||||
|
|
||||||
|
- 차단된 pendingRef는 세션 발급 불가
|
||||||
|
- 원래 요청 브라우저는 차단 메시지 표시
|
||||||
|
- 감사 로그에 차단 이벤트 기록
|
||||||
|
|
||||||
|
## 8. 영향도 관리 방안
|
||||||
|
|
||||||
|
### 8.1 기존 소스코드 영향 최소화
|
||||||
|
|
||||||
|
| 대상 | 영향도 관리 |
|
||||||
|
| --- | --- |
|
||||||
|
| Backend 기존 승인 API | 응답 구조 변경 금지 |
|
||||||
|
| Backend Redis 기존 key | 삭제/이름 변경 금지 |
|
||||||
|
| UserFront 기존 QR 화면 | 삭제 금지, feature flag fallback 유지 |
|
||||||
|
| UserFront 기존 링크 화면 | QR 안정화 전까지 변경 금지 |
|
||||||
|
| OIDC Hydra/Kratos 연동 | 신규 metadata API는 Hydra/Kratos accept 흐름에 개입하지 않음 |
|
||||||
|
|
||||||
|
### 8.2 호환성 원칙
|
||||||
|
|
||||||
|
기존 클라이언트는 새 API를 몰라도 정상 동작해야 한다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
기존 UserFront -> 기존 API만 사용 -> 정상
|
||||||
|
신규 UserFront flag OFF -> 기존 화면 사용 -> 정상
|
||||||
|
신규 UserFront flag ON -> approval/info 조회 후 기존 approve API 사용 -> 정상
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.3 실패 시 fallback
|
||||||
|
|
||||||
|
`approval/info` 조회가 실패하면 아래 정책 중 하나를 선택한다.
|
||||||
|
|
||||||
|
권장 기본값:
|
||||||
|
|
||||||
|
```text
|
||||||
|
운영/스테이징: 승인 차단 후 "요청 정보를 확인할 수 없습니다" 표시
|
||||||
|
개발 로컬: 기존 ApproveQrScreen fallback 허용
|
||||||
|
```
|
||||||
|
|
||||||
|
로컬 개발 중에는 빠른 검증을 위해 fallback을 허용하되, 운영에서는 요청 정보를 확인하지 못한 승인은 막는 쪽이 안전하다.
|
||||||
|
|
||||||
|
## 9. 개발 후 영향도 확인 체크리스트
|
||||||
|
|
||||||
|
### 9.1 Backend 테스트
|
||||||
|
|
||||||
|
필수 확인:
|
||||||
|
|
||||||
|
1. `POST /api/v1/auth/approval/info` 정상 응답
|
||||||
|
2. 없는 pendingRef는 안전한 오류 반환
|
||||||
|
3. QR init 후 approval info 조회 가능
|
||||||
|
4. QR approve 기존 테스트 통과
|
||||||
|
5. Enchanted Link init/poll 기존 테스트 통과
|
||||||
|
6. Magic Link verify 기존 테스트 통과
|
||||||
|
7. Login Code verify 기존 테스트 통과
|
||||||
|
8. Password login 기존 테스트 통과
|
||||||
|
|
||||||
|
권장 테스트 파일:
|
||||||
|
|
||||||
|
```text
|
||||||
|
backend/internal/handler/auth_handler_qr_test.go
|
||||||
|
backend/internal/handler/auth_handler_link_test.go
|
||||||
|
backend/internal/handler/auth_handler_login_code_test.go
|
||||||
|
backend/internal/handler/auth_handler_login_test.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.2 UserFront 테스트
|
||||||
|
|
||||||
|
필수 확인:
|
||||||
|
|
||||||
|
1. feature flag OFF에서 기존 QR 승인 화면이 표시된다.
|
||||||
|
2. feature flag OFF에서 QR 자동 승인이 기존처럼 동작한다.
|
||||||
|
3. feature flag ON에서 공통 승인 화면이 표시된다.
|
||||||
|
4. `내 요청입니다` 클릭 시 QR 승인 성공
|
||||||
|
5. 미로그인 승인 기기는 로그인 화면으로 이동
|
||||||
|
6. approval info 조회 실패 시 안전 안내 표시
|
||||||
|
7. 기존 로그인 링크 탭 동작 유지
|
||||||
|
8. 기존 비밀번호 로그인 동작 유지
|
||||||
|
|
||||||
|
권장 테스트 파일:
|
||||||
|
|
||||||
|
```text
|
||||||
|
userfront/test/qr_scan_screen_test.dart
|
||||||
|
userfront/test/login_navigation_race_test.dart
|
||||||
|
userfront/test/auth_proxy_service_test.dart
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.3 E2E 테스트
|
||||||
|
|
||||||
|
로컬에서 최소 2개 브라우저 또는 context로 확인한다.
|
||||||
|
|
||||||
|
시나리오:
|
||||||
|
|
||||||
|
1. PC 브라우저에서 QR 로그인 시작
|
||||||
|
2. 모바일 역할 브라우저에서 QR approve URL 접근
|
||||||
|
3. 공통 승인 화면에서 요청 정보 확인
|
||||||
|
4. `내 요청입니다` 클릭
|
||||||
|
5. PC 브라우저 poll이 성공하고 dashboard 진입
|
||||||
|
|
||||||
|
회귀 확인:
|
||||||
|
|
||||||
|
1. 비밀번호 로그인
|
||||||
|
2. 링크 로그인
|
||||||
|
3. QR 로그인 flag OFF
|
||||||
|
4. QR 로그인 flag ON
|
||||||
|
5. OIDC RP callback
|
||||||
|
|
||||||
|
## 10. 보안 확인 항목
|
||||||
|
|
||||||
|
개발 완료 전 반드시 확인한다.
|
||||||
|
|
||||||
|
| 항목 | 기준 |
|
||||||
|
| --- | --- |
|
||||||
|
| IP 노출 | 전체 IP가 아니라 마스킹된 값만 사용자에게 표시 |
|
||||||
|
| User-Agent | 원문 저장은 가능하나 UI에는 요약 표시 권장 |
|
||||||
|
| pendingRef 오류 | 사용자 존재 여부를 추론할 수 없는 메시지 사용 |
|
||||||
|
| approval/info | 인증 없이 호출 가능한 경우 rate limit 필요 |
|
||||||
|
| QR 승인 | 승인 기기의 기존 세션 검증 유지 |
|
||||||
|
| 링크 승인 | verifyOnly 흐름 유지 |
|
||||||
|
| 감사 로그 | 승인/차단/실패 이벤트가 구분되어야 함 |
|
||||||
|
|
||||||
|
## 11. 롤백 방안
|
||||||
|
|
||||||
|
문제가 발생하면 아래 순서로 롤백한다.
|
||||||
|
|
||||||
|
1. UserFront feature flag를 OFF로 변경한다.
|
||||||
|
2. 기존 `ApproveQrScreen` 경로로 즉시 복귀한다.
|
||||||
|
3. Backend 신규 API는 남겨도 기존 흐름에 영향이 없으므로 즉시 제거하지 않아도 된다.
|
||||||
|
4. 신규 Redis key는 TTL 만료로 자연 정리된다.
|
||||||
|
5. 문제가 Backend route 추가와 관련 있으면 `/approval/info`, `/approval/block` route만 비활성화한다.
|
||||||
|
|
||||||
|
롤백 시 제거하지 않아야 할 것:
|
||||||
|
|
||||||
|
- 기존 QR/link Redis key
|
||||||
|
- 기존 `/qr/approve`
|
||||||
|
- 기존 `/magic-link/verify`
|
||||||
|
- 기존 `/login/code/verify`
|
||||||
|
- 기존 `ApproveQrScreen`
|
||||||
|
|
||||||
|
## 12. 권장 구현 범위
|
||||||
|
|
||||||
|
1차 로컬 구현 범위는 아래로 제한하는 것이 좋다.
|
||||||
|
|
||||||
|
1. Backend `approval/info` API 추가
|
||||||
|
2. QR init에 공통 metadata 병행 저장
|
||||||
|
3. UserFront 공통 승인 화면 추가
|
||||||
|
4. QR 승인 라우트만 feature flag로 공통 화면 연결
|
||||||
|
5. 기존 QR 자동 승인 fallback 유지
|
||||||
|
6. 테스트로 flag OFF/ON 영향도 확인
|
||||||
|
|
||||||
|
2차 구현 범위:
|
||||||
|
|
||||||
|
1. Enchanted Link/Magic Link 화면 연결
|
||||||
|
2. `approval/block` API 추가
|
||||||
|
3. poll 응답 `blocked_by_user` 지원
|
||||||
|
4. 감사 로그 차단 이벤트 추가
|
||||||
|
|
||||||
|
## 13. 최종 완료 기준
|
||||||
|
|
||||||
|
이 작업은 다음 조건을 모두 만족해야 완료로 본다.
|
||||||
|
|
||||||
|
1. 기존 비밀번호 로그인에 영향이 없다.
|
||||||
|
2. 기존 링크 로그인에 영향이 없다.
|
||||||
|
3. 기존 QR 자동 승인 흐름은 feature flag OFF에서 그대로 동작한다.
|
||||||
|
4. feature flag ON에서 QR 공통 승인 화면이 표시된다.
|
||||||
|
5. 승인 화면에서 요청 기기/IP/시간/서비스가 표시된다.
|
||||||
|
6. `내 요청입니다` 승인 후 원래 요청 브라우저가 로그인된다.
|
||||||
|
7. approval info 조회 실패 시 세션 발급으로 이어지지 않는다.
|
||||||
|
8. Backend 단위 테스트와 UserFront 테스트가 통과한다.
|
||||||
|
9. 신규 Redis key는 TTL을 가진다.
|
||||||
|
10. 롤백 시 feature flag OFF만으로 기존 동작으로 돌아갈 수 있다.
|
||||||
|
|
||||||
@@ -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와 기능 범위를 제한해야 한다.
|
||||||
Reference in New Issue
Block a user