Skip to content

feat(gateway): implement TLS auth handshake and session token flow#6

Merged
aaronolds merged 4 commits intomainfrom
003-gateway-tls-auth-handshake-and-session-token
Feb 25, 2026
Merged

feat(gateway): implement TLS auth handshake and session token flow#6
aaronolds merged 4 commits intomainfrom
003-gateway-tls-auth-handshake-and-session-token

Conversation

@aaronolds
Copy link
Copy Markdown
Owner

  • Add WebSocket over TLS with self-signed cert generation and user cert support
  • Implement HMAC-SHA256 device authentication (deviceId + nonce + timestamp)
  • Add nonce replay protection with configurable TTL window
  • Add device approval registry with JSON persistence
  • Implement JWT session token issuance on connect and refresh on heartbeat
  • Add append-only JSONL audit log for security events
  • Add CLI with --insecure flag for explicit plaintext mode
  • Update shared protocol schemas with timestamp, signature, and heartbeat types
  • Add unit tests for auth, state, and TLS primitives
  • Add integration tests for connect auth and heartbeat refresh

Closes #3

- Add WebSocket over TLS with self-signed cert generation and user cert support
- Implement HMAC-SHA256 device authentication (deviceId + nonce + timestamp)
- Add nonce replay protection with configurable TTL window
- Add device approval registry with JSON persistence
- Implement JWT session token issuance on connect and refresh on heartbeat
- Add append-only JSONL audit log for security events
- Add CLI with --insecure flag for explicit plaintext mode
- Update shared protocol schemas with timestamp, signature, and heartbeat types
- Add unit tests for auth, state, and TLS primitives
- Add integration tests for connect auth and heartbeat refresh

Closes #3
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements a secured gateway WebSocket handshake/session flow and updates the shared protocol to support timestamped/HMAC-authenticated connect messages plus heartbeat-based session refresh.

Changes:

  • Extends shared handshake protocol with timestamp, signature, and heartbeat request/ack types + JSON schema generation and tests.
  • Adds gateway modules for TLS (self-signed or user-provided certs), HMAC verification, nonce replay protection, device registry persistence, audit logging, and JWT session tokens.
  • Introduces gateway CLI/config parsing plus unit/integration tests for connect auth and heartbeat refresh.

Reviewed changes

Copilot reviewed 39 out of 40 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks new gateway deps (Fastify/WebSocket, JWT, self-signed TLS).
packages/shared/src/protocol/json-schema.ts Adds JSON schema generation for heartbeat request/ack.
packages/shared/src/protocol/index.ts Re-exports new handshake types/schemas.
packages/shared/src/protocol/handshake.ts Adds connect timestamp/signature fields and heartbeat schemas.
packages/shared/src/tests/validate.test.ts Updates validation test data for new connect fields.
packages/shared/src/tests/json-schema.test.ts Asserts heartbeat schemas are generated.
packages/shared/src/tests/handshake.test.ts Adds schema tests for connect+heartbeat types.
packages/gateway/package.json Adds CLI bin + gateway runtime deps and start script.
packages/gateway/README.md Documents CLI flags, TLS behavior, connect auth flow, session tokens.
packages/gateway/test/unit/session-token.test.ts Unit tests for JWT issue/verify/refresh behavior.
packages/gateway/test/unit/nonce-store.test.ts Unit tests for nonce replay window/eviction.
packages/gateway/test/unit/hmac-auth.test.ts Unit tests for HMAC computation/verification + timestamp skew.
packages/gateway/test/unit/device-registry.test.ts Unit tests for JSON persistence + approval/shared secret access.
packages/gateway/test/unit/certificate-manager.test.ts Unit tests for cert generation/loading and error cases.
packages/gateway/test/smoke.test.ts Ensures key gateway APIs are exported from package entry.
packages/gateway/test/integration/connect-auth.integration.test.ts Integration coverage for connect auth (HMAC, skew, nonce replay, approval).
packages/gateway/test/integration/heartbeat-refresh.integration.test.ts Integration coverage for heartbeat ack + token refresh/invalid token close.
packages/gateway/test/helpers/test-gateway.ts Test harness to spin up an in-memory gateway and WS helpers.
packages/gateway/src/tls/tls-options.ts Builds Fastify TLS options via cert manager; supports insecure mode.
packages/gateway/src/tls/index.ts TLS module barrel exports.
packages/gateway/src/tls/certificate-manager.ts Loads user certs or generates/persists a managed self-signed cert.
packages/gateway/src/state/types.ts Adds state record types (device, nonce, audit event).
packages/gateway/src/state/nonce-store.ts In-memory nonce replay protection store with TTL eviction.
packages/gateway/src/state/index.ts State module barrel exports.
packages/gateway/src/state/device-registry.ts JSON device registry with approval and shared-secret lookup.
packages/gateway/src/server/register-websocket-routes.ts Implements WS connect handshake validation and post-handshake routing.
packages/gateway/src/server/index.ts Server module barrel exports.
packages/gateway/src/server/heartbeat-handler.ts Verifies/refreshes session tokens and returns heartbeat ack.
packages/gateway/src/server/create-gateway-server.ts Creates/starts Fastify server with WS routes and generated JWT secret fallback.
packages/gateway/src/server/connection-context.ts Tracks active connections and session token state.
packages/gateway/src/index.ts Expands package exports to include new gateway modules.
packages/gateway/src/config/parse-cli.ts Parses CLI flags into a GatewayConfig.
packages/gateway/src/config/gateway-config.ts Defines gateway config shape and defaults.
packages/gateway/src/cli.ts CLI entrypoint for starting the gateway.
packages/gateway/src/auth/session-token.ts JWT issue/verify/refresh helpers with AuthError mapping.
packages/gateway/src/auth/index.ts Auth module barrel exports.
packages/gateway/src/auth/hmac-auth.ts HMAC SHA-256 compute/verify + timestamp skew check.
packages/gateway/src/auth/errors.ts Defines gateway auth error codes and error type.
packages/gateway/src/audit/index.ts Audit module barrel exports.
packages/gateway/src/audit/audit-log.ts Append-only JSONL audit logger and event helpers.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/gateway/test/helpers/test-gateway.ts
Comment thread packages/gateway/test/helpers/test-gateway.ts Outdated
Comment thread packages/gateway/src/state/nonce-store.ts
Comment thread packages/gateway/src/tls/certificate-manager.ts
Comment on lines +337 to +338
server.get("/ws", { websocket: true }, (socket) => {
void processHandshake(socket, deps);
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

The WebSocket route handler passed to @fastify/websocket is treated as a ws socket, but the plugin’s route handler typically receives a connection wrapper (e.g., connection.socket). If socket here is the wrapper, processHandshake will call send/close/on on the wrong object and fail at runtime. Adjust the handler to pass the actual WebSocket instance (connection.socket) into processHandshake.

Suggested change
server.get("/ws", { websocket: true }, (socket) => {
void processHandshake(socket, deps);
server.get("/ws", { websocket: true }, (connection) => {
void processHandshake(connection.socket, deps);

Copilot uses AI. Check for mistakes.
Comment thread packages/gateway/src/server/register-websocket-routes.ts
jwtSecret: string,
): SessionTokenPayload {
try {
const decoded = verify(token, jwtSecret);
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

verifySessionToken() calls jsonwebtoken.verify(token, jwtSecret) without restricting accepted algorithms. To reduce algorithm-confusion risk and keep verification aligned with issueSessionToken defaults, pass an explicit algorithms allow-list (e.g., only HS256).

Suggested change
const decoded = verify(token, jwtSecret);
const decoded = verify(token, jwtSecret, { algorithms: ["HS256"] });

Copilot uses AI. Check for mistakes.
@aaronolds
Copy link
Copy Markdown
Owner Author

@copilot open a new pull request to apply changes based on the comments in this thread

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 25, 2026

@aaronolds I've opened a new pull request, #8, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 2 commits February 25, 2026 02:13
- Make DeviceRecord.name optional (no name required in test registrations)
- Handle all RawData types in waitForSocketMessage (string/Buffer/Buffer[]/ArrayBuffer)
- Replace NonceStore composite key with nested Map to prevent key collisions
- Add Subject Alternative Names (SAN) to self-signed cert generation
- Make ConnectSchema.authToken optional (gateway never validates it)
- Restrict verifySessionToken to HS256 algorithm only
- Update handshake tests to reflect authToken is now optional

Co-authored-by: aaronolds <1485194+aaronolds@users.noreply.github.com>
fix(gateway): address security and correctness issues from PR review
@aaronolds aaronolds merged commit ccb4599 into main Feb 25, 2026
aaronolds added a commit that referenced this pull request Mar 6, 2026
…, and audit enhancements

- JSONL TranscriptWriter with append-only per-session logs, fsync, and crash recovery
- SQLite OperationalStore replacing JSON DeviceRegistry with WAL mode and migrations
- EncryptedFileSecretStore (AES-256-GCM + scrypt) and KeychainSecretStore backends
- Typed audit events with automatic field redaction for sensitive data
- JSON-to-SQLite one-time device migration with idempotent .migrated marker
- Wire OperationalStore and SecretStore into gateway startup, auth, and RPC handlers
- Add secretsBackend, masterPassphrase, and fsyncWrites config fields
- Persistence barrel exports from gateway package

Closes #6
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.

3 participants