import { GenericEndpointContext } from "better-auth"; import { createAuthMiddleware } from "better-auth/api"; import { createFetch } from "@better-fetch/fetch"; import { BetterAuthRateLimitRule } from "@better-auth/core"; import { APIError as APIError$2, Endpoint, EndpointOptions } from "better-call"; //#region src/identification.d.ts interface IPLocation { lat: number; lng: number; city: string | null; region: string | null; postalCode: string | null; country: { code: string; name: string; } | null; timezone: string | null; } interface Identification { visitorId: string; requestId: string; timestamp: number; url: string; ip: string | null; location: IPLocation | null; browser: { name: string | null; version: string | null; os: string | null; osVersion: string | null; device: string | null; userAgent: string | null; }; confidence: number; incognito: boolean; bot: "notDetected" | "detected" | "unknown"; } //#endregion //#region src/sentinel/security.d.ts type SecurityAction = "log" | "block" | "challenge"; interface ThresholdConfig { challenge?: number; block?: number; } interface SecurityOptions { unknownDeviceNotification?: boolean; credentialStuffing?: { enabled: boolean; action?: SecurityAction; thresholds?: ThresholdConfig; windowSeconds?: number; cooldownSeconds?: number; }; impossibleTravel?: { enabled: boolean; maxSpeedKmh?: number; action?: SecurityAction; }; geoBlocking?: { allowList?: string[]; denyList?: string[]; action?: "block" | "challenge"; }; botBlocking?: boolean | { action: SecurityAction; }; suspiciousIpBlocking?: boolean | { action: SecurityAction; }; velocity?: { enabled: boolean; thresholds?: ThresholdConfig; maxSignupsPerVisitor?: number; maxPasswordResetsPerIp?: number; maxSignInsPerIp?: number; windowSeconds?: number; action?: SecurityAction; }; freeTrialAbuse?: { enabled: boolean; thresholds?: ThresholdConfig; maxAccountsPerVisitor?: number; action?: SecurityAction; }; compromisedPassword?: { enabled: boolean; action?: SecurityAction; minBreachCount?: number; }; emailValidation?: { enabled?: boolean; strictness?: "low" | "medium" | "high"; action?: SecurityAction; domainAllowlist?: string[]; }; emailNormalization?: { enabled?: boolean; }; staleUsers?: { enabled: boolean; staleDays?: number; action?: SecurityAction; notifyUser?: boolean; notifyAdmin?: boolean; adminEmail?: string; }; challengeDifficulty?: number; } interface SecurityVerdict { action: "allow" | "challenge" | "block"; challenge?: string; reason?: string; details?: Record; /** Set when the request included a valid, consumed PoW solution. */ powVerified?: boolean; } interface CredentialStuffingResult { blocked: boolean; challenged?: boolean; challenge?: string; reason?: string; details?: Record; } interface ImpossibleTravelResult { isImpossible: boolean; action?: "allow" | "challenge" | "block"; challenged?: boolean; challenge?: string; powVerified?: boolean; distance?: number; timeElapsedHours?: number; speedRequired?: number; from?: { city: string | null; country: string | null; } | null; to?: { city: string | null; country: string | null; } | null; } interface CompromisedPasswordResult { compromised: boolean; breachCount?: number; action?: SecurityAction; } interface StaleUserResult { isStale: boolean; daysSinceLastActive?: number; staleDays?: number; lastActiveAt?: string | null; action?: SecurityAction; notifyUser?: boolean; notifyAdmin?: boolean; } interface SecurityEvent { type: SecurityEventType; timestamp: number; userId: string | null; visitorId: string | null; ip: string | null; country: string | null; details: Record; action: "logged" | "blocked" | "challenged"; } type SecurityEventType = "unknown_device" | "credential_stuffing" | "impossible_travel" | "geo_blocked" | "bot_blocked" | "suspicious_ip_detected" | "velocity_exceeded" | "free_trial_abuse" | "compromised_password" | "stale_account_reactivation"; //#endregion //#region src/sentinel/sentinel.d.ts declare const sentinel: (options?: SentinelOptions) => { id: "sentinel"; init(ctx: import("better-auth").AuthContext): { context: { rateLimit: { enabled: boolean; window: number; max: number; storage: "memory" | "database" | "secondary-storage"; } & Omit; }; options: { rateLimit: { customRules: { [key: string]: false | BetterAuthRateLimitRule | ((request: Request, currentRule: BetterAuthRateLimitRule) => import("@better-auth/core").Awaitable); }; }; databaseHooks: { user: { create: { before(user: { id: string; createdAt: Date; updatedAt: Date; email: string; emailVerified: boolean; name: string; image?: string | null | undefined; } & Record, _ctx: import("better-auth").GenericEndpointContext | null): Promise<{ data: { email: string; id: string; createdAt: Date; updatedAt: Date; emailVerified: boolean; name: string; image?: string | null | undefined; }; } | undefined>; after(user: { id: string; createdAt: Date; updatedAt: Date; email: string; emailVerified: boolean; name: string; image?: string | null | undefined; } & Record, _ctx: import("better-auth").GenericEndpointContext | null): Promise; }; update: { before(user: Partial<{ id: string; createdAt: Date; updatedAt: Date; email: string; emailVerified: boolean; name: string; image?: string | null | undefined; }> & Record, ctx: import("better-auth").GenericEndpointContext | null): Promise<{ data: { email: string; id?: string | undefined; createdAt?: Date | undefined; updatedAt?: Date | undefined; emailVerified?: boolean | undefined; name?: string | undefined; image?: string | null | undefined; }; } | undefined>; }; }; session: { create: { before(session: { id: string; createdAt: Date; updatedAt: Date; userId: string; expiresAt: Date; token: string; ipAddress?: string | null | undefined; userAgent?: string | null | undefined; } & Record, _ctx: import("better-auth").GenericEndpointContext | null): Promise; after(session: { id: string; createdAt: Date; updatedAt: Date; userId: string; expiresAt: Date; token: string; ipAddress?: string | null | undefined; userAgent?: string | null | undefined; } & Record, _ctx: import("better-auth").GenericEndpointContext | null): Promise; }; }; }; emailNormalization?: { enabled?: boolean; } | undefined; emailValidation?: { enabled?: boolean; strictness?: "low" | "medium" | "high"; action?: SecurityAction; domainAllowlist?: string[]; } | undefined; }; }; hooks: { before: { matcher: (context: import("better-auth").HookEndpointContext) => boolean; handler: import("better-auth/api").AuthMiddleware; }[]; after: { matcher: (ctx: import("better-auth").HookEndpointContext) => boolean; handler: import("better-call").Middleware) => Promise>; }[]; }; }; //#endregion //#region src/types.d.ts /** * Retry/backoff for identify HTTP calls. */ interface KvRetryOptions { /** * Max retry index after a thrown failure. `0` = single attempt. * @default 2 */ attempts?: number; /** * Base delay (ms) before the first retry. * @default 400 */ baseDelay?: number; /** * Cap for exponential retry delay (ms). * @default 600 */ maxDelay?: number; } /** * Resolved {@link KvRetryOptions} with defaults applied. */ type KvRetryOptionsResolved = Required; /** * Dash API HTTP client options. */ interface ApiOptions { /** * Timeout for Dash API HTTP requests (milliseconds). * @default 3000 */ timeout?: number; /** * Extra headers on every Dash API request. * Cannot override built-in headers (`user-agent`, `x-api-key`). */ headers?: Record; } /** * Resolved {@link ApiOptions} with defaults applied. */ type ApiOptionsResolved = { timeout: number; headers?: Record; }; /** * KV HTTP client options. */ interface KvOptions { /** * Timeout for KV HTTP requests (milliseconds). * @default 1000 */ timeout?: number; /** * Retry/backoff for KV identify lookups. * @default { attempts: 2, baseDelay: 400, maxDelay: 600 } */ retry?: KvRetryOptions; /** * Extra headers on every KV request. * Cannot override built-in headers (`user-agent`, `x-api-key`). */ headers?: Record; } /** * Resolved {@link KvOptions} with defaults applied. */ interface KvOptionsResolved { timeout: number; retry: KvRetryOptionsResolved; headers?: Record; } /** * Shared connection options used by infra plugins. */ interface InfraPluginConnectionOptions { /** * The URL of the Better Auth Dash API * @default "https://dash.better-auth.com" */ apiUrl?: string; /** * The URL of the KV storage service * @default "https://kv.better-auth.com" */ kvUrl?: string; /** * Your Better Auth Dash API key * @default process.env.BETTER_AUTH_API_KEY */ apiKey?: string; /** * Dash API HTTP client options. */ apiOptions?: ApiOptions; /** * KV HTTP client options. */ kvOptions?: KvOptions; /** * Timeout for Dash API HTTP requests (milliseconds). * @default 3000 * @deprecated Use `apiOptions.timeout` instead. */ apiTimeout?: number; /** * Timeout for KV HTTP requests (milliseconds). * @default 1000 * @deprecated Use `kvOptions.timeout` instead. */ kvTimeout?: number; } /** * Configuration options for the dash plugin. */ interface DashOptions extends InfraPluginConnectionOptions { /** * User activity tracking configuration */ activityTracking?: { /** * Whether to enable user activity tracking * * This requires a database schema change to the user table. * @default false */ enabled?: boolean; /** * Interval in milliseconds to update lastActiveAt for active users * Set to 0 to disable interval-based tracking * @default 300000 (5 minutes) */ updateInterval?: number; }; /** * Opt into the 1.7+ managed directory-sync control plane. * * Enable together with `scim({ managedConnections })` when the dashboard * should reserve and manage SCIM connections. This does not replace or * disable legacy pre-1.7 SCIM provider APIs. */ managedDirectorySync?: { /** * Whether to enable managed directory-sync reservation tables/APIs. * * Adds `directorySyncConnection` and * `directorySyncMembershipProvenance` via plugin schema. * @default false */ enabled?: boolean; /** * Install SSO `resolveUser` / `guardProviderMutation` for directory * pairing. Required only when creating paired directories. Needs * `sso({})` (or richer options) so callbacks can be installed. * @default true */ ssoPairing?: boolean; /** * SCIM → organization membership projection. * * When enabled, dash installs SCIM `projection.reconcileUser` so * provisioned users are projected into organization memberships. */ membershipProjection?: { /** * Whether to install membership projection. * @default true */ enabled?: boolean; /** * Role assigned when projection creates organization memberships. * @default "member" */ role?: string; }; }; } /** * Configuration options for the sentinel plugin. */ interface SentinelOptions extends InfraPluginConnectionOptions { /** * Security features configuration */ security?: SecurityOptions; } /** * Internal connection options with required fields resolved. */ interface InfraPluginConnectionOptionsInternal extends Omit { apiUrl: string; kvUrl: string; apiKey: string; apiOptions: ApiOptionsResolved; kvOptions: KvOptionsResolved; } /** * Internal options with required fields resolved */ interface DashOptionsInternal extends Omit, InfraPluginConnectionOptionsInternal { /** * Shared Dash HTTP client from {@link createAPI}; injected by {@link dash} when wiring endpoints. * * @internal */ $api: ReturnType; } /** * Resolved dash options from {@link resolveDashOptions} / plugin-stored config; excludes injected `$api`. */ type DashOptionsResolved = Omit; /** * Internal sentinel options with required fields resolved. */ interface SentinelOptionsInternal extends Omit, InfraPluginConnectionOptionsInternal {} /** * Location/geo data used across events, audit logs, and request context. */ interface LocationData { ipAddress?: string | null; city?: string | null; country?: string | null; countryCode?: string | null; } /** @deprecated Use LocationData instead */ type LocationDataContext = LocationData; /** * Plugin-attached AuthContext fields. better-auth types AuthContext as a fixed * object; prefer {@link createAuthMiddleware} for hooks, {@link asInfraContext} * for database hooks / getCurrentAuthContext. */ type InfraAuthContext = GenericEndpointContext["context"] & { identification?: Identification | null; visitorId: string | null; requestId: string | null; ip: string | null; untrustedVisitorId: string | null; location: LocationData | undefined; infraRidCookieSet?: boolean; freeTrialReservationId?: string; returned?: unknown; payload?: unknown; }; type InfraEndpointContextValue = Omit & { context: InfraAuthContext; }; type InfraEndpointContext = InfraEndpointContextValue | undefined; /** Type-only cast — does not wrap or mutate ctx. */ declare function asInfraContext(ctx: T): Omit & { context: InfraAuthContext; }; /** * better-auth createAuthMiddleware with InfraAuthContext on `ctx.context`. * Pass-through at runtime — type-only (does not wrap the handler). * * Prefer this for dash/sentinel/identification hooks. Keep better-auth's * createAuthMiddleware where middleware return values must extend AuthContext * (e.g. JWT `payload` via `use: [...]`). */ declare const createAuthMiddleware$1: { (handler: (ctx: InfraEndpointContextValue) => R | Promise): ReturnType; , R>(options: Options, handler: (ctx: InfraEndpointContextValue) => R | Promise): ReturnType; }; //#endregion export { SecurityOptions as A, createAuthMiddleware$1 as C, ImpossibleTravelResult as D, CredentialStuffingResult as E, StaleUserResult as M, ThresholdConfig as N, SecurityEvent as O, asInfraContext as S, CompromisedPasswordResult as T, KvRetryOptionsResolved as _, DashOptionsInternal as a, SentinelOptions as b, EndpointOptions as c, InfraEndpointContextValue as d, InfraPluginConnectionOptions as f, KvRetryOptions as g, KvOptionsResolved as h, DashOptions as i, SecurityVerdict as j, SecurityEventType as k, InfraAuthContext as l, KvOptions as m, ApiOptions as n, DashOptionsResolved as o, InfraPluginConnectionOptionsInternal as p, ApiOptionsResolved as r, Endpoint as s, APIError$2 as t, InfraEndpointContext as u, LocationData as v, sentinel as w, SentinelOptionsInternal as x, LocationDataContext as y };