diff --git a/apps/api/src/routes/moderation.ts b/apps/api/src/routes/moderation.ts index 4895362..cdf7eef 100644 --- a/apps/api/src/routes/moderation.ts +++ b/apps/api/src/routes/moderation.ts @@ -1,9 +1,5 @@ import { Hono } from 'hono' -import { setDeviceId } from '@repo/domain' - -import { authedId, unauthorized } from '../http' - import type { App } from '../context' // ---- Player reporting ------------------------------------------------------ @@ -29,19 +25,8 @@ export const moderationRoutes = new Hono({ strict: false }) .post('/api/PlayerReporting/v1/hile', (c) => c.json(false)) // The client reporting its device id (form-encoded `oldDeviceId`, `newDeviceId`, - // `platform`), rotating from the id it thinks we hold to the current one. We don't - // reconcile the two: the client is the only source for either, so a mismatch tells - // us nothing and last write wins. `platform` is ignored — the account already - // records the platform its login is linked to. Auth-gated; the client ignores the - // response body, and the real service answers with an empty array. - .post('/api/PlayerReporting/v1/deviceId', async (c) => { - const id = await authedId(c) - if (id === null) return unauthorized(c) - const body = await c.req.parseBody().catch(() => ({}) as Record) - const newDeviceId = body.newDeviceId - if (typeof newDeviceId !== 'string' || newDeviceId === '') { - return c.json({ error: 'newDeviceId is required' }, 400) - } - await setDeviceId(c.env.DB, id, newDeviceId) - return c.json([]) - }) + // `platform`), rotating from the id it thinks we hold to the current one. Carries no + // bearer token and fires before account creation, so there is no caller to attribute + // the id to and nothing to store it against — we accept it and drop it. The client + // ignores the response body; the real service answers with an empty array. + .post('/api/PlayerReporting/v1/deviceId', (c) => c.json([])) diff --git a/apps/api/src/test/integration/api.test.ts b/apps/api/src/test/integration/api.test.ts index 63539ab..c9659ca 100644 --- a/apps/api/src/test/integration/api.test.ts +++ b/apps/api/src/test/integration/api.test.ts @@ -222,13 +222,12 @@ describe('public endpoints', () => { }) }) - test('POST /api/PlayerReporting/v1/deviceId stores the new device id', async () => { + // Unauthenticated by design — the client posts this before it has an account, so + // there's no bearer token to check and nothing to attribute the id to. + test('POST /api/PlayerReporting/v1/deviceId accepts an unauthenticated report', async () => { const res = await exports.default.fetch(`${ORIGIN}/api/PlayerReporting/v1/deviceId`, { method: 'POST', - headers: { - ...(await bearer()), - 'Content-Type': 'application/x-www-form-urlencoded', - }, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ oldDeviceId: '491e8b9', newDeviceId: '491e8b9566cb1b593367c72860e978b3d5765326', @@ -237,20 +236,6 @@ describe('public endpoints', () => { }) expect(res.status).toBe(200) expect(await res.json()).toEqual([]) - - const row = await env.DB.prepare( - "SELECT json_extract(data, '$.deviceId') AS deviceId FROM account WHERE account_id = 42" - ).first<{ deviceId: string | null }>() - expect(row?.deviceId).toBe('491e8b9566cb1b593367c72860e978b3d5765326') - }) - - test('POST /api/PlayerReporting/v1/deviceId 401s without a bearer token', async () => { - const res = await exports.default.fetch(`${ORIGIN}/api/PlayerReporting/v1/deviceId`, { - method: 'POST', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: new URLSearchParams({ newDeviceId: 'abc' }), - }) - expect(res.status).toBe(401) }) test('POST /api/playerReputation/v2/bulk returns a reputation per id', async () => { diff --git a/packages/domain/src/accounts-db.ts b/packages/domain/src/accounts-db.ts index a41d778..c65310c 100644 --- a/packages/domain/src/accounts-db.ts +++ b/packages/domain/src/accounts-db.ts @@ -55,12 +55,6 @@ export interface Account { phone?: string /** Set via PUT /account/me/bio; read back via GET /account/:id/bio. */ bio?: string - /** - * Hardware/install id the client last reported (POST /api/PlayerReporting/v1/deviceId). - * The client rotates it — it posts the id it believes we hold plus the new one — - * so this is simply the most recent value it told us about, not a proven identity. - */ - deviceId?: string /** Remaining username changes; decremented by PUT /account/me/username. */ availableUsernameChanges?: number /** @@ -217,15 +211,6 @@ export async function setLastLoginTime(db: D1Database, id: number, time: string) .run() } -/** Record the device id the client last reported. False when no such account exists. */ -export async function setDeviceId(db: D1Database, id: number, deviceId: string): Promise { - const { meta } = await db - .prepare("UPDATE account SET data = json_set(data, '$.deviceId', ?2) WHERE account_id = ?1") - .bind(id, deviceId) - .run() - return meta.changes > 0 -} - /** Look up multiple accounts by AccountId (order not guaranteed). */ export async function getAccountsByIds(db: D1Database, ids: number[]): Promise { if (ids.length === 0) return []