Files
zeekay be1d26a502 Let a recipient without an account sign
The socket the app's pages call over (/zap) opened only for a session, so
a recipient signing by their link, who has no account, could not reach
the routes that sign a field or complete a document: every field they
clicked failed with "An error occurred while signing the field". tRPC had
served those as public procedures; the port to ZAP kept their bodies and
lost the door.

A connection from the app's own pages with no session now opens as
anonymous and reaches only the routes tRPC served publicly, each
authorised by the token it carries; every other route answers it 401
before its handler runs. The operation API still refuses a request
without a token.
2026-10-20 12:41:58 -07:00

54 lines
2.0 KiB
TypeScript

// esign ZAP RPC — per-connection context.
//
// The minted context the ZAP server threads into every handler. It is the
// subset of the tRPC `TrpcContext` the migrated handlers actually consume:
// the authenticated user, the (optional) session, the resolved teamId, the
// request metadata, and a logger. Auth is established ONCE at the WebSocket
// upgrade (see mint.ts), exactly like tRPC's authenticatedMiddleware ran per
// request — the difference is the ZAP context is per-connection, set when the
// socket opens.
import type { Session } from '@prisma/client';
import type { Logger } from 'pino';
import type { ApiRequestMetadata } from '@hanzo/esign-lib/universal/extract-request-metadata';
/**
* The authenticated principal common to both auth paths. Session auth yields a
* full SessionUser and API-token auth yields the narrower apiToken.user select;
* the migrated handlers only read id/name/email, the intersection of the two,
* mirroring what tRPC's `ctx.user` exposed across its session/api branches.
*/
export interface ZapUser {
id: number;
name: string | null;
email: string;
}
/**
* A connection opened without a session, from the app's own pages: a recipient
* signing by the token in their link, a visitor on a direct template link. It
* reaches only the routes in `publicRoutes` (routes.ts), each authorised by the
* token it carries, exactly as tRPC served them as public procedures.
*/
export interface AnonymousZapContext {
user: null;
session: null;
teamId: -1;
metadata: ApiRequestMetadata;
logger: Logger;
}
/** What a connection is minted as: signed in, or anonymous. */
export type ZapConnectionContext = ZapContext | AnonymousZapContext;
/** Authenticated per-connection context for ZAP handlers. */
export interface ZapContext {
user: ZapUser;
/** Present for session auth, null for API-token auth (mirrors tRPC). */
session: Session | null;
/** Resolved team id; -1 when a session has no team (tRPC convention). */
teamId: number;
metadata: ApiRequestMetadata;
logger: Logger;
}