effect-auth

Add Refresh Tokens

Add rotating refresh tokens with focused durable Drizzle storage.

Refresh-token rotation requires durable family state and atomic reuse detection. The migration and generated Drizzle table are available by selecting refresh-tokens in the schema generator.

Focused direct RefreshTokenStore adapters support local Effect Drizzle SQLite, Cloudflare D1, and Effect Drizzle PostgreSQL. SQLite and PostgreSQL commit the old-token update and replacement insert in one native transaction. D1 performs the same rotation in one native batch and gates the replacement insert on the old-token update, so a concurrent loser cannot create a replacement.

For D1, provide the focused store Layer from the same scoped database Layer used by the rest of the application:

src/auth/refresh-token-storage.ts
import * as Layer from "effect/Layer";
import { layer as d1RefreshTokenStoreLayer } from "@effect-auth/core/DrizzleD1RefreshTokenStore";
import { DrizzleD1DatabaseLive } from "@effect-auth/core/DrizzleD1Sqlite";
import type { DrizzleD1DatabaseLike } from "@effect-auth/core/DrizzleD1Sqlite";

declare const binding: DrizzleD1DatabaseLike;

const drizzleD1DatabaseLayer = DrizzleD1DatabaseLive(binding);
const refreshTokenStoreLayer = d1RefreshTokenStoreLayer().pipe(
  Layer.provide(drizzleD1DatabaseLayer)
);

export const authRefreshTokenStorageLayer = refreshTokenStoreLayer;

For Bun or Node SQLite, construct makeDrizzleSqliteRefreshTokenStore with the application-owned transaction-capable database. PostgreSQL applications can use makeDrizzlePostgresRefreshTokenStore directly or its lowercase layer export with DrizzlePostgresDatabase. Use the typed table modules when the canonical auth_refresh_token table name is not appropriate.

Run rotation and reuse races against the production engine and transaction mode. Do not use memory storage for production refresh tokens.