Skip to content

epic: strengthen core so new framework adapters reuse more #72

Description

@Bccorb

Summary (epic)

The core/adapter boundary is clean (zero Express imports in core), but core is under-weighted: it owns per-endpoint upstream calls yet not the delivery, cookie-format, service-token, proxy, or contract logic every adapter needs. Strengthening core lets us spin off new framework adapters (Fastify, Hono, Koa, Next.js) with far less reimplementation. This is a tracking epic; sub-items land as separate small PRs.

Direction

Core owns the request/response contract. Adapters supply primitives (read cookie, set cookie, send) plus a route table, and core defines what applying a result means. Sub-items 1 and 6 below both reduce to the same gap: core returns a result but does not define how to apply one, so every adapter reimplements that knowledge.

Ordering

Re-measured against the current code and ordered by lines a second adapter would not have to rewrite:

Item Adapter lines saved Risk
proxyRequest (1) ~285 in createServer.ts, 33 routes Medium
Move deliverAuthMessage (2) 198 Very low
Cookie apply + result render (6) ~135 across 9 files Low
Contract constants (4) ~10 Very low
Core surface tidy (5) 0 (DX, not reuse) Trivial
Session helpers (3) 0 Medium
Injectable logger (7) 0 Low

Sub-items

  • Step 0: unify the failure contract. result.error meant the whole upstream body on 12 sites and a short code on 8, and FinishLoginResult declared error?: string while assigning the body. No single applyResult was possible until a failure had one meaning. Split into errorCode and errorBody on an exported ResultFailure. Landed in refactor(core,express)!: split result error into errorCode and errorBody #124.
  • PR 1: move message delivery into core. Relocate packages/express/src/internal/deliverAuthMessage.ts beside packages/core/src/authMessaging.ts. Its imports are types only, so it is a lift and shift with no framework coupling. The duplicate packages/express/src/messaging.ts is already gone (refactor(express): take messaging types from core #122), and the types are re-exported from core.
  • PR 2: applyResult and adapter primitives. Core defines how a result becomes a response; adapters supply setCookie and send. Absorbs the cookieSigner block copied across 9 files, the set-cookie loop, handle(res, result) in 3 files, and routeParam.
  • PR 3: core.proxyRequest({ authServerUrl, path, method, authorization, forwardedClientIp, query, body }) -> { status, body }. The 33 organizations/step-up/totp/users passthrough routes exist only in proxyWithIdentity (packages/express/src/createServer.ts) with no core equivalent. Fold in the querystring builder duplicated 3x (createServer.ts:113, core/src/handlers/admin.ts:29, core/src/handlers/internalMetrics.ts:24).
  • PR 4: centralize the adapter contract. Route path strings, the x-seamless-auth-delivery-mode: "external" literal, and the external-delivery token identity (buildInternalServiceAuthorization), so cross-repo changes with seamless-auth-api are a single edit.
  • PR 5: tidy core's public surface. Export admin, sessions, internalMetrics, systemConfig, verifySignedAuthResponse, and verifyRefreshCookie from packages/core/src/index.ts (currently subpath-only).

Corrections to the original list

Recorded so the deltas are not rediscovered later:

  • The messaging sub-item was half done by refactor(express): take messaging types from core #122, which deleted the byte-identical packages/express/src/messaging.ts. Only the deliverAuthMessage relocation remains, now PR 1.
  • The public-surface sub-item listed bootstrapAdminInvite, which was removed entirely in fa1c736. Dropped from the list.
  • The contract sub-item placed the x-seamless-auth-delivery-mode literal at register.ts:41 "and 3 more" in the adapter. It is 3 sites and all three are in core (register.ts, requestOtpHandler.ts, requestMagicLinkHandler.ts), not the adapter. Smaller than described.
  • The logger sub-item described core as writing to console directly. There is one call site, in verifySignedAuthResponse.ts, not a pervasive pattern.

Moved out of this epic

Both are worthwhile, but neither saves a new adapter any lines, so they should not compete for slots here:

  • Extract the two copy-pasted core session helpers (verifySignedAuthResponse plus sub-check, and the session-cookie payload build, each repeated ~7x) and introduce a typed UpstreamSessionResponse, since upstream fields are read as any today. Core hygiene.
  • Injectable logger in core, which matters for serverless and edge adapters. DX.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions