feat(gateway): verify Kakao account ownership
This commit is contained in:
@@ -30,7 +30,28 @@ Gateway API는 다음 저장 경계를 사용합니다.
|
||||
- `GatewayOperation`: build/reset/open/close 등 실행 요청과 결과
|
||||
- `GatewayReleaseOperation`, `GatewayReleaseState`: Gateway 전체 릴리스 queue와 현재·이전 commit
|
||||
- `GatewayRuntimeAction`: profile별 시간 가속·연기 요청, 부분 적용과 최종 결과
|
||||
- Redis: gateway session, OAuth 임시 상태, flush channel
|
||||
- Redis: gateway session, OAuth 임시 상태, KakaoTalk 로그인 challenge, flush channel
|
||||
|
||||
Kakao 로그인은 URL 이름만으로 사용자를 연결하지 않습니다. `account_email`과
|
||||
`talk_message` scope를 항상 요청하고 callback의 `/v2/user/me` 응답에서 고유 ID,
|
||||
이메일 보유·유효·인증 상태를 확인합니다. 고유 ID가 다른 계정의 같은 이메일로
|
||||
접근하는 경우와 변경 이메일이 이미 다른 `AppUser`에 속한 경우는 `CONFLICT`로
|
||||
끝나며 session을 만들지 않습니다. 기존 Kakao 계정이면 stable OAuth ID로
|
||||
사용자를 찾은 뒤 이메일과 갱신된 token metadata를 함께 저장합니다.
|
||||
|
||||
일반 비밀번호 로그인도 `oauth_type=KAKAO`이면 저장 access token을 사용하고,
|
||||
필요하면 아직 유효한 refresh token으로 갱신한 뒤 `/v2/user/me`를 호출합니다.
|
||||
provider ID가 저장 `oauth_id`와 다르면 session을 발급하지 않고, 확인된 이메일은
|
||||
unique constraint 아래에서 동기화합니다. provider나 refresh 호출 실패는 Kakao
|
||||
재로그인을 요구하며 저장된 identity를 임의로 바꾸지 않습니다.
|
||||
|
||||
`AppUser.kakaoTalkVerifiedUntil`이 지났으면 Gateway는 4자리 코드를 생성해 Kakao
|
||||
“나와의 채팅”에 한 번 보내고 Redis에 사용자별 180초 challenge를 둡니다. 유효한
|
||||
challenge는 재로그인에서도 재사용하여 중복 메시지를 보내지 않습니다. 제출은
|
||||
Redis script가 원자적으로 성공 소비 또는 실패 횟수 차감(최대 3회)을 수행합니다.
|
||||
성공하면 유효 기한을 10일 뒤로 저장한 후에만 Gateway session을 만듭니다.
|
||||
challenge와 OAuth pending state에는 TTL이 있으며 Redis 장애나 메시지 발송 실패는
|
||||
로그인 실패로 끝납니다.
|
||||
|
||||
Orchestrator는 `GatewayOperation`을 claim하고 source ref를 commit으로
|
||||
해결합니다. `WorkspaceManager`가 commit별 worktree를 준비하고 build runner가
|
||||
|
||||
@@ -79,6 +79,7 @@ storage, route guards, and image loading.
|
||||
| gateway login/status | `index.php` | 450/700px desktop widths, mobile collapse, Pretendard title, real login mutation/session storage, actual seasonal map asset |
|
||||
| gateway account | `i_entrance/user_info.php` | 550px × minimum 575px panel, 14px Pretendard, three legacy textures, success and API-error password flows |
|
||||
| gateway OAuth join | `oauth_kakao/join.php` | 700px centered registration card, Kakao exchange/register success, retained-input API error, hover/focus |
|
||||
| gateway Kakao OTP | `index.php#modalOTP` | 동일 문구·500px modal, desktop/mobile geometry와 색상·typography, password/OAuth 진입, autofocus·focus-visible·active·disabled·오류 재시도·session 저장 |
|
||||
| game login hand-off | unauthenticated `hwe/index.php` redirect | `/che/login` delegates to `/gateway/` |
|
||||
| troop | `hwe/v_troop.php` | existing `app/game-frontend/e2e/troop.spec.ts` desktop/mobile geometry and interaction suite |
|
||||
| current city | `hwe/b_currentCity.php` | ref-specific 16px Times New Roman, 1000px summary/1024px general tables, 400px selector, 64px icon, nation title color, force summary, actor/spy/admin redaction, and map-click query navigation |
|
||||
@@ -169,3 +170,18 @@ screenshot only when `CITY_PARITY_ARTIFACT_DIR` is set.
|
||||
For a review run that also writes full-page screenshots, create an ignored
|
||||
artifact directory and set `FRONTEND_PARITY_ARTIFACT_DIR` before invoking the
|
||||
suite. The ordinary CI run does not write screenshots after successful tests.
|
||||
|
||||
Kakao OTP 화면만 실제 Chromium으로 재검증하고 선택적으로 artifact를 남기려면 다음
|
||||
명령을 사용합니다. Ref helper는 checked-out `index.php` markup과 실제 빌드 CSS를
|
||||
사용하므로 live Ref service가 없어도 정적 geometry 기준을 재현하지만, OAuth
|
||||
callback 자체의 provider 검증을 대신하지는 않습니다.
|
||||
|
||||
```sh
|
||||
KAKAO_OTP_ARTIFACT_DIR=/path/to/ignored/artifacts \
|
||||
pnpm exec playwright test --config app/gateway-frontend/e2e/playwright.config.mjs \
|
||||
app/gateway-frontend/e2e/kakao-otp.spec.ts
|
||||
|
||||
REF_SAM_ROOT=/path/to/ref/sam \
|
||||
KAKAO_OTP_ARTIFACT_DIR=/path/to/ignored/artifacts \
|
||||
node tools/frontend-legacy-parity/kakao-otp-ref-geometry.mjs
|
||||
```
|
||||
|
||||
@@ -28,10 +28,13 @@ Legacy member numbers map to deterministic UUIDs. Existing rows are updated by
|
||||
that UUID, so references such as `ng_old_generals.owner` remain stable even
|
||||
when an old account was deleted before the dump.
|
||||
|
||||
Kakao members retain `oauth_id`, email and metadata. Cutover sets
|
||||
`kakao_verified_at` and `kakao_grace_started_at` to the migration time and starts
|
||||
the verification grace period there. Source rows without an OAuth ID retain
|
||||
their metadata, but the importer does not invent a provider identifier.
|
||||
Kakao members retain `oauth_id`, email and metadata. A parseable legacy
|
||||
`token_valid_until` is copied to `kakao_talk_verified_until`, preserving the
|
||||
remaining KakaoTalk ownership-proof interval instead of forcing an immediate
|
||||
message at cutover. Cutover also sets `kakao_verified_at` and
|
||||
`kakao_grace_started_at` to the migration time and starts the local-account
|
||||
verification grace period there. Source rows without an OAuth ID retain their
|
||||
metadata, but the importer does not invent a provider identifier.
|
||||
|
||||
Legacy password hashes remain usable when gateway-api has
|
||||
`GATEWAY_LEGACY_PASSWORD_GLOBAL_SALT`; a successful login upgrades the stored
|
||||
@@ -131,7 +134,7 @@ browser.
|
||||
5. Put the affected target in maintenance mode, take a PostgreSQL backup, then
|
||||
run the same commands with `--apply`.
|
||||
6. Repeat each apply. Counts must remain unchanged.
|
||||
7. Verify Kakao migration timestamps, password-hash shapes, archive ownership,
|
||||
7. Verify Kakao migration timestamps including `kakao_talk_verified_until`, password-hash shapes, archive ownership,
|
||||
old-nation/history duplicate preservation, `/past-plays` list/detail access,
|
||||
foreign-owner denial and the dynasty link.
|
||||
8. Retain the MariaDB dumps as rollback evidence. Rollback restores the
|
||||
|
||||
Reference in New Issue
Block a user