Skip to content

SessionTether API ​

ts
import { SessionTether, SessionStatus } from 'session-tether';

Use the exported SessionStatus enum for status comparisons, such as result.status === SessionStatus.GRACE. Its values remain strings (valid, grace, rotated, missing, revoked, consumed, expired) for serialization.

The core manages refresh sessions. It does not create or verify JWT access tokens. All durations below are in milliseconds.

Constructor ​

ts
new SessionTether({ store, expiryTTL, reuseGraceTTL: 30_000 })
OptionTypeMeaning
storeSessionStoreRequired persistence adapter
expiryTTLnumberRequired lifetime for new and replacement tokens
reuseGraceTTLnumberOptional consumed-token retry window; defaults to 30,000 ms. Set to 0 for immediate reuse rejection.

reuseGraceTTL must be a non-negative safe integer or the constructor throws RangeError. The constructor does not validate expiryTTL; provide a finite positive lifetime appropriate for your application. There is no default expiry TTL.

createSession ​

ts
createSession(input: {
  userId: UUID;
  sessionName?: string;
}): Promise<string>

Creates a family and initial refresh token atomically in both SQL adapters. If either insert fails, the operation throws and neither record is committed. Returns the raw refresh-token string, not an object with access and refresh tokens. UUID is Node's node:crypto UUID type. SQLite defaults a missing session name to an empty string.

validateRefreshToken ​

ts
validateRefreshToken(refreshToken: string): Promise<
  | { status: SessionStatus.VALID; userId: UUID; familyId: string }
  | { status: SessionStatus.MISSING }
  | { status: SessionStatus.REVOKED }
  | { status: SessionStatus.CONSUMED }
  | { status: SessionStatus.EXPIRED }
>

Checks existence and revocation first. A consumed token is accepted as valid while less than reuseGraceTTL milliseconds have elapsed since its original consumedAt, provided it has not expired. At or after that boundary, reuse revokes the family and returns consumed. An invalid expiration string is treated as expired. Retries do not extend the grace window or expiration.

rotateRefreshToken ​

ts
rotateRefreshToken(refreshToken: string): Promise<
  | { status: SessionStatus.ROTATED; refreshToken: string; userId: UUID }
  | { status: SessionStatus.GRACE; userId: UUID }
  | { status: SessionStatus.MISSING }
  | { status: SessionStatus.REVOKED }
  | { status: SessionStatus.CONSUMED }
  | { status: SessionStatus.EXPIRED }
>

Validates first, then generates and stores a replacement. SQL adapters set replacement expiration to the locked consumption decision timestamp plus expiryTTL. Replace the client's credential on rotated. A consumed-token retry within grace returns grace without another replacement or store mutation. Preserve the replacement received from the successful rotation; the store retains only hashes and cannot recover that credential if its response was lost. Both SQL adapters conditionally consume an unconsumed predecessor and insert its successor in one transaction. Only the winning update inserts a successor; an insertion failure rolls back consumption. Core catches TokenAlreadyConsumedError from a losing attempt, rechecks current token state, and applies the grace/reuse policy. Other storage errors propagate. Rotation now locks an active family before consuming its token, ordering the transaction with family and user-wide revocation. Expiration is rechecked under the family lock using a fresh application-clock timestamp as the consumption decision point. At or after expiry, TokenExpiredError maps to expired without consuming the token or creating a successor. No expiration grace applies; expiry after the decision does not abort commit.

revokeSession ​

ts
revokeSession(familyTokenHash: string): Promise<void>

Pass the stable family identifier from validateRefreshToken().familyId or a session listing's tokenHash. The current source names this parameter refreshToken, but forwards it without hashing; SQLite expects the family identifier. Passing a raw token will not select the intended family.

revokeAllSessionsForUser ​

ts
revokeAllSessionsForUser(userId: UUID): Promise<void>

Delegates to store.revokeAllForUser(). SQLite marks all unrevoked families for the user as revoked. Issued access tokens are unaffected.

listAllSessionsForUser ​

ts
listAllSessionsForUser(userId: UUID): Promise<
  ReadonlyArray<{ tokenHash: string; session_name: string }>
>

Returns all retained families, including revoked and expired ones. Note the snake-case session_name field.

listActiveSessionsForUser ​

ts
listActiveSessionsForUser(userId: UUID): Promise<
  ReadonlyArray<{ tokenHash: string; sessionName: string }>
>

Returns non-revoked families containing an unconsumed, unexpired token. Note the camel-case sessionName field.

pruneExpiredSessions ​

ts
pruneExpiredSessions(options?: {
  retentionTTL?: number;
}): Promise<{ deletedFamilies: number; deletedTokens: number }>

Defaults to seven days of retention. Invalid retention values throw RangeError synchronously. See cleanup for eligibility rules.

Errors and exported types ​

Database errors propagate as exceptions; status results are validation outcomes, not a complete error channel. Core handles TokenAlreadyConsumedError by rechecking token state. The result shapes above describe the implementation; named ValidationResult and RotationResult aliases are not exported by the root entry point.

ExportPurpose
SessionTetherSession lifecycle manager
SessionStatusValidation and rotation status enum
TokenAlreadyConsumedErrorStore conflict signal handled by the core
SessionRevokedErrorStore revocation signal mapped to SessionStatus.REVOKED
SessionStorePersistence interface, type only
RefreshTokenDetailsToken lookup shape, type only
SessionSummaryActive family summary, type only
PruneResultCleanup counts, type only

Built for explicit authentication boundaries. Powered by VitePress.