/** * Runtime recovery for Better Auth JWKS keys orphaned by an auth-secret * rotation. * * Better Auth encrypts the persisted JWT signing key with the current * `BETTER_AUTH_SECRET`. Rotating that secret leaves the newest `jwks` row * undecryptable, and the JWT plugin's optional response hook signs it on every * `/get-session` response when enabled — so a rotation can turn every session * check into a 500 and sign the whole deployment out (2026-08-28 outage on * the hosted design/clips apps). The release migration * `better-auth-jwks-key-rotation-recovery` already expires stale rows, but * release migrations do not reach every deployed database, so the same * recovery must also run where the failure is actually observed. */ import { getCurrentAdapter } from "better-auth"; import type { Jwk } from "better-auth/plugins/jwt"; /** * Better Auth's default JWKS read is an unordered `findMany` capped at 100 * rows. Once expiry leaves more than 100 stale rows, that window can hold no * live key: `/jwks` publishes `{"keys":[]}` after the grace period, and every * signature mints another key the next read cannot see (2026-09-01 the plan * app grew 18k keys this way; 2026-10-01 its JWKS went empty). Reading newest * first keeps the live and recently rotated keys in the window. Token * verification reads the same window; tokens live 15 minutes and are signed by * the newest live key, so the keys that can still verify one are always the * newest ones. */ export declare function readNewestJwks(adapter: Parameters[0]): Promise; export declare function isJwksDecryptError(error: unknown): boolean; export declare function resetJwksSecretRotationStateForTests(): void; /** * Verify the newest active JWKS key against the current secret and, only when * it genuinely cannot be decrypted, expire the stale rows so Better Auth * mints a fresh key on the next JWT it signs. Returns true when rows were * expired (callers may retry the failed request once), false otherwise. */ export declare function healUndecryptableJwks(): Promise; type AuthHookLike = { matcher: (context: any) => boolean; handler: (context: any) => Promise; }; type AuthPluginLike = { hooks?: { before?: AuthHookLike[]; after?: AuthHookLike[]; }; }; export declare function withJwksRotationRecovery

(plugin: P): P; export {};