feat(api): add QR scan-login completion poll on the internal face

QR scan-to-login is a device-code grant where the QR encodes the existing
short-lived account-link code (spec §B3 player game-login). velocity mints a
code in-game, renders it as a QR, the player scans it on a phone already signed
in to the panel, and that web session's verify writes the durable account_links
row bound to that user. The only new verifiable surface that flow needs is the
completion poll velocity calls to learn the link landed and admit the player.

Add GET /api/v1/internal/account/link/status/{mc_uuid}: a read-only, internal
handleLinkStatus keyed by the verified mc_uuid velocity already holds. It reuses
the existing UserByMCUUID, so it adds no migration and no mutation to the
load-bearing VerifyLinkCode; ErrNotFound maps to {linked:false} (pending /
not-yet-scanned), a hit to {linked:true, user_id}. Keying on the public UUID and
not the scanned code means the read carries no guessing surface and needs no
attempt cap — the internal face already gates it to service callers, and the poll
consumes nothing so a velocity restart re-polls safely.

QR render, limbo collision routing, in-game admit, and the reclaim
inherit-disambiguation stay CODE-ONLY (Java/Velocity) and are labeled as such;
this endpoint reports link completion only.

Document the route in openapi.yaml (x-felis-face internal, x-felis-tier service)
so the parity gate holds, and cover it with a hermetic vertical that proves the
poll reflects the durable link only after the external verify and binds the
verifier's id, plus unknown-uuid, idempotency, and internal-only face separation.
This commit is contained in:
flyemoji committed 2026-06-30 04:08:01 +09:00
1 parent 28c3eee361
commit 116595f3ee
4 files changed
+207

No files matched your search

+31
View File
@@ -668,6 +668,37 @@ paths:
'401':
$ref: '#/components/responses/Unauthorized'
/api/v1/internal/account/link/status/{mc_uuid}:
get:
tags: [account-internal]
operationId: linkStatus
summary: Poll whether an in-game UUID has finished linking — the QR scan-to-login completion check (spec §B3).
description: >
Internal-only, read-only. After a new player scans the QR-encoded link code
and the web verify writes the durable account_links row, velocity polls this
for the UUID it minted against and admits the player on linked:true, binding
the in-game session to user_id. Keyed by the verified UUID (not the scanned
code), so it consumes nothing and is safe to poll repeatedly; an unlinked or
never-seen UUID returns linked:false, and user_id is present only when linked.
x-felis-face: [internal]
x-felis-tier: service
security: [{ serviceToken: [] }]
parameters:
- { name: mc_uuid, in: path, required: true, schema: { type: string, format: uuid } }
responses:
'200':
description: Link-completion status; user_id is present only when linked.
content:
application/json:
schema:
type: object
required: [linked]
properties:
linked: { type: boolean }
user_id: { type: string }
'401':
$ref: '#/components/responses/Unauthorized'
/api/v1/internal/player/reclaim:
post:
tags: [account-internal]