Skip to content

feat: device-aware JWT auth, prekey upload, key fingerprint endpoint, and AI agent CI#212

Open
Themancalledpg wants to merge 4 commits into
codebestia:mainfrom
Themancalledpg:fix/clicked-device-auth-prekeys-ci
Open

feat: device-aware JWT auth, prekey upload, key fingerprint endpoint, and AI agent CI#212
Themancalledpg wants to merge 4 commits into
codebestia:mainfrom
Themancalledpg:fix/clicked-device-auth-prekeys-ci

Conversation

@Themancalledpg

Copy link
Copy Markdown
Contributor

Summary

This PR implements device-aware authentication by extending JWTs with deviceId, adds the prekey batch upload endpoint for end-to-end encrypted messaging, exposes a safety-number key-fingerprint endpoint for key verification, and adds a GitHub Actions CI workflow for the ai_agent module.

closes #158
closes #159
closes #162
closes #150

Changes

  • JWT + socket auth carry deviceId and validate device state #158 — JWT + socket auth carry deviceId and validate device state: Extended signToken to include deviceId in the token payload. Added a devices table to the schema (userId, identityPublicKey, isRevoked). Updated HTTP auth middleware to verify the (userId, deviceId) pair exists and is not revoked (401 otherwise). Updated Socket.IO middleware similarly — socket identity is bound from the verified token, never from event payloads. Legacy tokens missing deviceId are rejected with 401. The POST /auth/verify endpoint now accepts identityPublicKey to register or look up the device and embed its id in the returned JWT.

  • POST /devices/:id/prekeys — upload a prekey batch #159POST /devices/:id/prekeys — upload a prekey batch: Implemented prekey upload endpoint accepting { signedPreKey: { keyId, publicKey, signature }, oneTimePreKeys: [{ keyId, publicKey }] }. Validates signed prekey signature against the device's identityPublicKey using Node's Ed25519 verifier (400 on bad signature). Upserts the signed prekey (one per device via unique index), inserts OTPs with onConflictDoNothing by (deviceId, keyId), caps stored OTPs per device at 200, and enforces device-owner-only access (403 otherwise).

  • Safety-number / key-fingerprint endpoint (key verification) #162 — Safety-number / key-fingerprint endpoint: Implemented GET /users/:id/key-fingerprint. Retrieves all active device identity public keys for a user, sorts them lexicographically, concatenates with \n, computes SHA-256, and formats as a 60-digit safety number in two 30-digit halves (matching Signal's safety number convention). Returns both the raw 60-digit string and a space-separated "groups of 5" display format. Derivation algorithm documented in code comments for client interoperability.

  • Create GitHub Actions CI workflow for ai_agent #150 — GitHub Actions CI for ai_agent: Created .github/workflows/ai-agent-ci.yml triggered on push/PR changes to apps/ai_agent/** or the workflow file. Uses astral-sh/setup-uv@v5 with uv.lock cache, runs uv sync --extra dev, then uv run pytest --cov=main --cov-report=xml. Uploads coverage to Codecov with continue-on-error: true and posts a Markdown coverage summary to the job summary.

Test plan

  • JWT test suite extended: deviceId in payload, legacy-token rejection
  • Auth integration test updated: identityPublicKey required in verify, device revocation path, new-device-for-existing-user path
  • users.test.ts updated: db.query.devices mocked, signToken calls include deviceId
  • New devices.prekeys.test.ts: ownership, revocation, bad signature, OTP cap, batch trim, success path
  • New users.fingerprint.test.ts: determinism, 60-digit format, groups-of-5 display, empty-device 404, no-user 404
  • CI workflow self-validates on first run

@drips-wave

drips-wave Bot commented Jun 25, 2026

Copy link
Copy Markdown

@Themancalledpg Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant