/** * @typedef {import('./token.js').SessionTokenPayload} SessionTokenPayload * @typedef {import('./stores/memory.js').SessionStore} SessionStore * @typedef {import('./stores/memory.js').SessionRecord} SessionRecord * @typedef {import('./cookie.js').CookieOptions} CookieOptions */ /** * @typedef {object} SessionEvents * @property {(session: Session) => void | Promise} [onIssue] * @property {(session: Session) => void | Promise} [onVerify] * @property {(oldId: string, session: Session) => void | Promise} [onRotate] * @property {(sessionId: string, reason?: string) => void | Promise} [onRevoke] * @property {(reason: string, req: any) => void | Promise} [onDeny] * @property {(payload: { userId: string | null, sessionId: string, reason: string, previous: object, current: object }) => void | Promise} [onSuspicious] */ /** * @typedef {object} SessionManagerConfig * @property {string | Buffer | Uint8Array | Array} secret * @property {string | number} ttl * @property {string | number} idleTtl * @property {CookieOptions & { name?: string }} [cookie] * @property {SessionStore} [store] * @property {boolean} [anonymous=false] * @property {string | number} [touchEvery] How often `verify` persists a * rolling `lastSeenAt` update to the * store. Defaults to 60s (or half the * idleTtl, whichever is smaller). * Larger values cut store write * traffic; the idle-timeout check * only ever errs by at most this * much. Must be shorter than idleTtl. * @property {number} [concurrentLimit] * @property {ReadonlyArray<'ip' | 'ua'>} [bindTo] Fingerprint binding. * @property {'strict' | 'soft'} [bindStrictness='strict'] Behaviour on fingerprint mismatch. * `strict` (default) — hard revoke, * verify returns null. * `soft` — fire `onSuspicious` with * `reason: 'fingerprint-mismatch'` but * let the request through. Useful for * mobile users who move between wifi * and 5G. * @property {boolean} [impersonation=false] Enable impersonate() API. * @property {string | number} [impersonationTtl='30m'] Absolute lifetime of an * impersonation session. Defaults to * 30 minutes — impersonation is a * high-risk mode; the tight window * matches AWS / GCP admin console * patterns and cuts audit surface. * @property {boolean} [deviceLabels=false] Auto-generate device labels from UA. * @property {SessionEvents} [events] Audit trail callbacks. * @property {boolean | { onDetected?: SessionEvents['onSuspicious'] }} [suspiciousActivity] * IP-change detection. */ /** * @typedef {object} IssueOptions * @property {string | null} [userId=null] * @property {object} [claims={}] * @property {string} [deviceLabel] * @property {boolean} [rememberMe=false] * @property {number} [now] * @property {any} [req] When present, used to sample fingerprint (ip/ua). */ /** * @typedef {object} Session * @property {string} id * @property {string | null} userId * @property {object} claims * @property {number} issuedAt * @property {number} expiresAt * @property {number} lastSeenAt * @property {number} [freshAt] * @property {string} [deviceLabel] * @property {string} [ip] * @property {string} [ua] * @property {string} [impersonatedBy] * @property {string} [impersonationReason] * @property {boolean} isAnonymous */ /** * @typedef {object} IssueResult * @property {string} token * @property {string} cookie * @property {Session} session */ /** * Create a session manager — the package's main entrypoint. Issues * sealed cookie/bearer tokens backed by a server-side store, and * exposes the verify / rotate / revoke lifecycle around them. * * The returned object's type is inferred — consumers get the full * method surface in the generated `.d.ts` without a hand-maintained * typedef drifting out of sync. * * @param {SessionManagerConfig} config */ export function createSessionManager(config: SessionManagerConfig): { issue: (options?: IssueOptions) => Promise; verify: (req: any, options?: { now?: number; }) => Promise; touch: (sessionId: string, options?: { now?: number; }) => Promise; rotate: (req: any, options?: {}) => Promise<{ token: string; cookie: string; session: Session; previousId: any; }>; markFresh: (req: any, options?: { now?: number; }) => Promise<{ freshAt: number; }>; requireFreshAuth: (req: any, options?: { maxAgeSeconds: number; now?: number; }) => Promise; impersonate: (adminReq: any, targetUserId: string, options?: { ttl?: string | number; claims?: object; reason?: string; now?: number; }) => Promise; revoke: (req: any, options?: { reason?: string; now?: number; }) => Promise<{ cookie: string; revoked: boolean; }>; revokeById: (sessionId: string, options?: { reason?: string; }) => Promise; revokeAllForUser: (userId: string, options?: { reason?: string; }) => Promise; revokeAllExceptCurrent: (req: any, options?: { reason?: string; now?: number; }) => Promise; listActive: (userId: string) => Promise; upgrade: (req: any, userId: string, options?: { mergeClaims?: object; now?: number; }) => Promise; readonly cookieName: string; readonly store: import("./stores/memory.js").SessionStore; extractToken: (req: any) => string | undefined; }; export type SessionTokenPayload = import("./token.js").SessionTokenPayload; export type SessionStore = import("./stores/memory.js").SessionStore; export type SessionRecord = import("./stores/memory.js").SessionRecord; export type CookieOptions = import("./cookie.js").CookieOptions; export type SessionEvents = { onIssue?: ((session: Session) => void | Promise) | undefined; onVerify?: ((session: Session) => void | Promise) | undefined; onRotate?: ((oldId: string, session: Session) => void | Promise) | undefined; onRevoke?: ((sessionId: string, reason?: string) => void | Promise) | undefined; onDeny?: ((reason: string, req: any) => void | Promise) | undefined; onSuspicious?: ((payload: { userId: string | null; sessionId: string; reason: string; previous: object; current: object; }) => void | Promise) | undefined; }; export type SessionManagerConfig = { secret: string | Buffer | Uint8Array | Array; ttl: string | number; idleTtl: string | number; cookie?: (import("@exortek/shared/cookie").CookieOptions & { name?: string; }) | undefined; store?: import("./stores/memory.js").SessionStore | undefined; anonymous?: boolean | undefined; /** * How often `verify` persists a * rolling `lastSeenAt` update to the * store. Defaults to 60s (or half the * idleTtl, whichever is smaller). * Larger values cut store write * traffic; the idle-timeout check * only ever errs by at most this * much. Must be shorter than idleTtl. */ touchEvery?: string | number | undefined; concurrentLimit?: number | undefined; /** * Fingerprint binding. */ bindTo?: readonly ("ip" | "ua")[] | undefined; /** * Behaviour on fingerprint mismatch. * `strict` (default) — hard revoke, * verify returns null. * `soft` — fire `onSuspicious` with * `reason: 'fingerprint-mismatch'` but * let the request through. Useful for * mobile users who move between wifi * and 5G. */ bindStrictness?: "strict" | "soft" | undefined; /** * Enable impersonate() API. */ impersonation?: boolean | undefined; /** * Absolute lifetime of an * impersonation session. Defaults to * 30 minutes — impersonation is a * high-risk mode; the tight window * matches AWS / GCP admin console * patterns and cuts audit surface. */ impersonationTtl?: string | number | undefined; /** * Auto-generate device labels from UA. */ deviceLabels?: boolean | undefined; /** * Audit trail callbacks. */ events?: SessionEvents | undefined; /** * IP-change detection. */ suspiciousActivity?: boolean | { onDetected?: SessionEvents["onSuspicious"]; } | undefined; }; export type IssueOptions = { userId?: string | null | undefined; claims?: object | undefined; deviceLabel?: string | undefined; rememberMe?: boolean | undefined; now?: number | undefined; /** * When present, used to sample fingerprint (ip/ua). */ req?: any; }; export type Session = { id: string; userId: string | null; claims: object; issuedAt: number; expiresAt: number; lastSeenAt: number; freshAt?: number | undefined; deviceLabel?: string | undefined; ip?: string | undefined; ua?: string | undefined; impersonatedBy?: string | undefined; impersonationReason?: string | undefined; isAnonymous: boolean; }; export type IssueResult = { token: string; cookie: string; session: Session; };