Skip to content

Implement room creation flow with JWT-based session setup#10

Merged
wmxscott merged 4 commits into
mainfrom
claude/implement-phase-5-lgEgq
Mar 23, 2026
Merged

Implement room creation flow with JWT-based session setup#10
wmxscott merged 4 commits into
mainfrom
claude/implement-phase-5-lgEgq

Conversation

@wmxscott
Copy link
Copy Markdown
Owner

Summary

This PR implements the core room creation flow for Ratifyd, enabling users to start ephemeral interview sessions. When a user clicks "Start Session" on the landing page, the system generates cryptographic keypairs, creates a room, and issues a self-signed JWT that serves as the session token.

Key Changes

  • New createRoom() function (src/lib/room/createRoom.ts):

    • Generates RSA signing keypair for JWT signing and nonce proofs
    • Generates RSA-OAEP keypair for receiving encrypted room keys
    • Generates AES-GCM room key for encrypting notes and chat in Yjs
    • Creates unique ownerId and roomId (both UUIDs)
    • Persists all keypairs and keys to localStorage
    • Mints a self-issued owner JWT and navigates to the room via URL fragment
    • Ensures public keys are never exposed in the URL
  • Updated LandingPage component (src/components/LandingPage/LandingPage.tsx):

    • Added "Start Session" button that triggers createRoom()
    • Implemented loading state during room creation
    • Added error handling with user-friendly error messages
    • Improved UI with centered layout and descriptive copy
  • Comprehensive test suite (src/lib/room/createRoom.test.ts):

    • Validates JWT structure and claims (role, room, iss, jti, exp, iat)
    • Verifies keypair persistence to localStorage
    • Confirms room key is saved under the correct roomId
    • Validates JWT signature using the stored public key
    • Ensures no public keys leak into the URL
    • Tests uniqueness of generated IDs across multiple calls

Implementation Details

  • The JWT payload includes room (roomId), role (OWNER), iss (ownerId), and jti (unique token ID)
  • All cryptographic material is stored in localStorage keyed by peerId/roomId
  • The JWT is passed to the room via URL fragment (#token=<jwt>)
  • Self-admission logic is deferred to the useSession hook after YjsProvider initialization

https://claude.ai/code/session_01UASxoGUhQFs33RpjEbwWRB

claude and others added 3 commits March 23, 2026 02:36
Implements the room creation flow per the implementation plan:

- `src/lib/room/createRoom.ts` — generates RSA signing keypair, RSA-OAEP
  keypair, and AES-GCM room key; stores all keys in localStorage keyed by
  ownerId/roomId; mints a self-issued owner JWT; redirects to `/#token=<jwt>`.
  Public keys are never placed in the URL.
- `src/components/LandingPage/LandingPage.tsx` — wires the "Start Session"
  button to `createRoom()` with loading/error state.
- `src/lib/room/createRoom.test.ts` — comprehensive tests verifying JWT
  structure (role, iss, room, jti, exp−iat), localStorage contents, URL
  fragment format, signature correctness, and no public key leakage into URL.

All 133 tests pass. Lint and build are clean.

https://claude.ai/code/session_01UASxoGUhQFs33RpjEbwWRB
Comment thread src/lib/room/createRoom.ts Outdated

const signingKP = await generateSigningKeyPair()
const oaepKP = await generateOaepKeyPair()
const roomKey = await generateRoomKey()
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 3 awaits can probably go in a await Promise.all

Comment thread src/lib/room/createRoom.ts Outdated

await saveSigningKeyPair(signingKP.privateKey, signingKP.publicKey, ownerId)
await saveOaepKeyPair(oaepKP.privateKey, oaepKP.publicKey, ownerId)
await saveRoomKey(roomKey, roomId)
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 3 awaits can probably go in a await Promise.all as well

…mise.all

Address PR feedback: the three key generation calls and three storage
calls are each independent, so run them concurrently via Promise.all
to reduce latency during room creation.

https://claude.ai/code/session_01UASxoGUhQFs33RpjEbwWRB
@wmxscott wmxscott merged commit ce8f158 into main Mar 23, 2026
2 checks passed
@wmxscott wmxscott deleted the claude/implement-phase-5-lgEgq branch March 23, 2026 03:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants