# @exortek/session

## 1.4.3

### Patch Changes

- 89aea87: Ship self-contained TypeScript declarations. Every package's emitted `.d.ts` referenced `@exortek/shared`
  (e.g. `import { BaseError } from '@exortek/shared/errors'`), but `@exortek/shared` is a private, never-published
  workspace package that is inlined into each bundle at build time. A TypeScript consumer therefore hit
  `Cannot find module '@exortek/shared/…'` (with `skipLibCheck` off) or silently degraded error-class types like
  `ApiKeyError` — losing its constructor signature and `.code` / `.message` — with `skipLibCheck` on.

  The build now runs a declaration-bundling pass (`rollup-plugin-dts`) after `tsc`, flattening each entry's `.d.ts` and
  inlining the `@exortek/shared` types so the shipped declarations are fully self-contained. Runtime deps and `node:*`
  stay external. No runtime or API change — types only.

- Updated dependencies [89aea87]
  - @exortek/crypto@1.1.1

## 1.4.2

### Patch Changes

- b9e0647: Publish only the package-root README, CHANGELOG and LICENSE.

  The `files` list matched those names at any depth rather than just the root, so a nested document was published
  alongside them — `@exortek/oauth2` shipped its `examples/README.md`. The entries are now anchored to the package root.

- 0a94f13: Smaller bundles — the internal argument-guard helpers are now tree-shakeable.

  Each package bundles the guard helpers it uses. They were previously built as one object holding all fourteen, which a
  bundler cannot take apart, so every package shipped all of them regardless of how many it called. They are now
  individually importable, and each package pulls in only what it uses.

  No API change: the errors, codes and messages raised by argument validation are identical. Published bundles shrink by
  roughly 7-18% depending on the package.

- Updated dependencies [b827c5c]
- Updated dependencies [b9e0647]
- Updated dependencies [0a94f13]
  - @exortek/crypto@1.1.0

## 1.4.1

### Patch Changes

- e53ae64: Fix polynomial-ReDoS (quadratic regex backtracking) surfaced by CodeQL, plus small code-quality fixes.
  Bundled `@exortek/shared` utilities (base32, base64, duration) are inlined into the packages listed above, so each
  ships the fix.

  - Replace `/=+$/` trailing-padding strips (base32 / base64 decode, OTP provisioning URI) with linear scans —
    `"=".repeat(n) + "x"` was O(n²).
  - Rework the duration parser regex so surrounding whitespace can no longer cause O(n²) matching (trim first, single
    interior `\s*`).
  - Strip trailing spaces/dots in filename sanitisation with a linear scan — upload filenames are attacker-controlled.
  - Cap email length before the regex in magic-link.
  - Thread the JWT decode label into error messages (removes dead arguments and gives failures which segment broke).
  - Drop a duplicate character in a UA browser-detection regex character class.

  DoS-hardening and hygiene only — no API or behavioural change for valid input.

- Updated dependencies [e53ae64]
  - @exortek/crypto@1.0.9

## 1.4.0

### Minor Changes

- 46fd779: Add `sessionStore.custom(impl)` (also exported as `customStore`) — wrap your own `SessionStore`
  implementation with validation (fails at construction time if a required method is missing) and transparent sync/async
  wrapping, instead of hand-assembling the interface yourself.

### Patch Changes

- 40fc707: Remove a stale Hono/Elysia mention left over from their removal in an earlier major (the published
  `description` field still advertised the removed adapters); fix a Turkish word that leaked into an English code
  comment in the README.

## 1.3.1

### Patch Changes

- 689cf87: Remove workspace-only `devDependencies` from published package.json — `@exortek/shared` no longer appears as
  `"0.0.0"` on the npm registry.
- 689cf87: Replace `fastify-plugin` npm dependency with `@exortek/shared/fastify-plugin` — a built-in drop-in that
  covers skip-override, display-name, plugin-meta, version constraints, and encapsulate. Users no longer need to
  `npm i fastify-plugin` alongside fastify.
- Updated dependencies [689cf87]
  - @exortek/crypto@1.0.8

## 1.3.0

### Minor Changes

- 48f1b5e: `verify()` now rejects tokens missing `fp` when `bindTo` is configured (fail-closed). Previously, tokens
  without a fingerprint silently bypassed binding checks. `impersonate()` now sets `fp` from the admin request, matching
  `issue()` and `rotate()`.

  Note: existing impersonation sessions issued before this fix will be rejected on next verify when `bindTo` is
  configured — affected users will need to re-authenticate.

- 48f1b5e: Remove 10 dead `ErrorCode` members that were never thrown (MISSING_TOKEN, IDLE_TIMEOUT, REVOKED,
  TOKEN_ROTATION_REQUIRED, FINGERPRINT_MISMATCH, SUSPICIOUS_ACTIVITY, CONCURRENT_LIMIT_EXCEEDED, FRESH_AUTH_REQUIRED,
  IMPERSONATION_INVALID, MISSING_PEER_DEP).

## 1.2.1

### Patch Changes

- 31223e4: Consolidate duplicated store internals into @exortek/shared utilities (redis-helpers, incr-store,
  record-store). No public API changes.

  apikey: fix Redis store race condition where a concurrent update() could silently un-revoke a key — revocations now
  use a tombstone key that update() never touches.

  apikey: fix memory store put() storing by reference instead of copying — now consistent with getById()'s copy-on-read
  contract.

## 1.2.0

### Minor Changes

- **Hono + Elysia adapters removed.** The `@exortek/session/hono` and `@exortek/session/elysia` subpaths (added in
  `1.0.0`) no longer exist. Positioning is the classic Node stack — Express and Fastify only. Elysia is Bun-native in
  practice and Hono's edge-runtime pitch is a domain this stack doesn't target. Removed from `exports`,
  `peerDependencies`, and `peerDependenciesMeta`. `@exortek/session/express` and `@exortek/session/fastify` are
  unchanged. Consumers on those frameworks need to pin `< 1.2.0` (unmaintained) or write their own adapter — session's
  public API stays framework-agnostic.

## 1.1.0

### Minor Changes

- f659550: `deriveCsrfToken` now requires a secret of at least **32 bytes**, matching the floor `@exortek/security`'s
  CSRF module has always enforced. Shorter secrets throw `INVALID_ARGUMENT`. A 32-byte HMAC-SHA-256 secret is the
  smallest value that resists offline brute-forcing of the derived token.

  Callers passing shorter secrets (previously accepted without complaint) must lengthen them — a
  `crypto.randomBytes(32).toString('base64url')` value or any 32-plus-character string is sufficient.

## 1.0.3

### Patch Changes

- 09b6ff7: **Fail-closed when `bindTo` is set but `issue()` is called without `req`.** Previously the manager would
  happily mint a session with no fingerprint, and `verify()` — seeing `payload.fp` undefined — would silently skip the
  binding check. Any application that relied on `bindTo` for defence-in-depth could lose it if a code path called
  `issue()` without threading the request through (admin scripts, background jobs, refactored middlewares). The manager
  now throws `SessionError { code: INVALID_ARGUMENT }` with a message naming the missing `options.req` and explaining
  why.

## 1.0.2

### Patch Changes

- **Fix broken install: `1.0.1` shipped with `"@exortek/crypto": "workspace:^"` in its `dependencies`, which
  `npm install` cannot resolve (`EUNSUPPORTEDPROTOCOL`).** The root release script now goes through `yarn npm publish`,
  which rewrites Yarn's workspace protocol to a real semver range at pack time. This release lists `@exortek/crypto` as
  `^1.0.6` and installs cleanly. No code changed — please upgrade `1.0.1 → 1.0.2` to unblock installs.

## 1.0.1

### Patch Changes

- de29e24: - Fastify adapter JSDoc referred to `reply.setSession` / `reply.clearSession`; the actual decorated methods
  are `reply.setSessionCookie` / `reply.clearSessionCookie`. JSDoc realigned so IDE hints match the runtime API.
  - Build hygiene: `build` / `clean` now also remove `tsconfig.tsbuildinfo` so `tsc --incremental` cannot leave stale
    `.d.ts` artifacts behind.
- Updated dependencies [eaf7921]
  - @exortek/crypto@1.0.6

## 1.0.0

### Major Changes

- 77007e2: Initial release of `@exortek/session`.

  Sealed-cookie session manager built on `@exortek/crypto.seal`. Ships every session concern a backend actually needs,
  opt-in via config flags so callers pay only for what they use.

  **Core (default on):**

  - Sealed cookie via `crypto.seal` — stateless verify hot-path
  - Cookie + `Authorization: Bearer` header extraction
  - Absolute + rolling idle TTL
  - Multi-secret rotation
  - In-process memory store with LRU eviction and background sweep
  - Per-request cache — 3 `verify(req)` calls → 1 decrypt
  - `issue` / `verify` / `rotate` / `touch` / `revoke` / `revokeById` / `revokeAllForUser` / `revokeAllExceptCurrent` /
    `listActive`
  - `deriveCsrfToken` / `verifyCsrfToken` — session-bound synchroniser tokens
  - `SessionError` + 14 stable error codes

  **Opt-in features:**

  - **Anonymous / guest sessions** with `anonymous: true`
  - **Concurrent session limit** — 3 devices max, oldest kicked
  - **Fingerprint binding** — `bindTo: ['ip', 'ua']`; mismatch → hard revoke
  - **Impersonation** — admin-as-user with `impersonatedBy` audit trail
  - **Device labels** — `iPhone 14 · Chrome` from the UA
  - **Session events** — `onIssue`, `onVerify`, `onRotate`, `onRevoke`, `onDeny`, `onSuspicious` callbacks
  - **Suspicious activity detection** — IP change flagging
  - **Sudo mode / step-up authentication** — `requireFreshAuth` + `markFresh`
  - **Remember-me** — doubles the absolute TTL on issue

  **Subpaths:**

  - `@exortek/session/stores/redis` — Redis session store with optional `publishRevocations: true` pub/sub for
    cross-worker cache invalidation. Works with `ioredis` or `node-redis@4+`.
  - `@exortek/session/trusted-device` — separate long-lived HMAC-authenticated cookie for "remember this device" 2FA
    skip. Multi-secret rotation.
  - `@exortek/session/fastify` — plugin with `req.session` + `req.sessions` + `reply.setSessionCookie` /
    `reply.clearSessionCookie`
  - `@exortek/session/express` — middleware with the same shape
  - `@exortek/session/hono` — middleware setting `c.get('session')`, `c.get('sessions')`
  - `@exortek/session/elysia` — plugin using `derive` to inject `session` + `sessions` onto the context

  **Compliance impact** — the following OWASP ASVS V3 rows on `docs/compliance.md` move from 🟡 to ✅:

  - V3.2 Session token via CSPRNG
  - V3.4 Secure / HttpOnly / SameSite cookie flags
  - V3.5 `__Host-` prefix binding
  - V3.7 Server-side invalidation on logout

  **144 unit tests** — errors, cookie, header, token round-trip, memory + Redis stores, manager
  (issue/verify/revoke/rotate/touch), sudo mode, fingerprint, device labels, impersonation, events, suspicious activity,
  CSRF derivation, trusted-device, and the four framework adapters via mocks.

### Patch Changes

- Updated dependencies [31a1159]
  - @exortek/crypto@1.0.5
