692 lines
21 KiB
Markdown
692 lines
21 KiB
Markdown
# 비표준 로그인 승인 화면 공통화 개발안
|
|
|
|
작성일: 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만으로 기존 동작으로 돌아갈 수 있다.
|
|
|