UNPKG

@carere/gel-better-auth

Version:

Adapter for Better Auth and Gel/EdgeDB

1,571 lines (1,556 loc) 55.5 kB
import { PostgresPool, MysqlPool, Dialect, Kysely, Migration } from 'kysely'; import * as better_call from 'better-call'; import { CookieOptions, EndpointContext, Endpoint, Middleware, InputContext } from 'better-call'; import * as z from 'zod/v4'; import { ZodSchema } from 'zod/v4'; import { L, e, D, O } from './better-auth.ZSfSbnQT.js'; import { S, a, b } from './better-auth.ClXlabtY.js'; import { Database } from 'better-sqlite3'; import { Database as Database$1 } from 'bun:sqlite'; import { AdapterDebugLogs as AdapterDebugLogs$1 } from 'better-auth/adapters'; import { Client } from 'gel'; type BetterAuthDbSchema = Record<string, { /** * The name of the table in the database */ modelName: string; /** * The fields of the table */ fields: Record<string, FieldAttribute>; /** * Whether to disable migrations for this table * @default false */ disableMigrations?: boolean; /** * The order of the table */ order?: number; }>; declare const getAuthTables: (options: BetterAuthOptions) => BetterAuthDbSchema; type HookEndpointContext = EndpointContext<string, any> & Omit<InputContext<string, any>, "method"> & { context: AuthContext & { returned?: unknown; responseHeaders?: Headers; }; headers?: Headers; }; type GenericEndpointContext = EndpointContext<string, any> & { context: AuthContext; }; declare function createCookieGetter(options: BetterAuthOptions): (cookieName: string, overrideAttributes?: Partial<CookieOptions>) => { name: string; attributes: CookieOptions; }; declare function getCookies(options: BetterAuthOptions): { sessionToken: { name: string; options: CookieOptions; }; /** * This cookie is used to store the session data in the cookie * This is useful for when you want to cache the session in the cookie */ sessionData: { name: string; options: CookieOptions; }; dontRememberToken: { name: string; options: CookieOptions; }; }; type BetterAuthCookies = ReturnType<typeof getCookies>; type LogLevel = "info" | "success" | "warn" | "error" | "debug"; interface Logger { disabled?: boolean; level?: Exclude<LogLevel, "success">; log?: (level: Exclude<LogLevel, "success">, message: string, ...args: any[]) => void; } type LogHandlerParams = Parameters<NonNullable<Logger["log"]>> extends [ LogLevel, ...infer Rest ] ? Rest : never; declare const createLogger: (options?: Logger) => Record<LogLevel, (...params: LogHandlerParams) => void>; declare function checkPassword(userId: string, c: GenericEndpointContext): Promise<boolean>; type AuthContext = { options: BetterAuthOptions; appName: string; baseURL: string; trustedOrigins: string[]; /** * New session that will be set after the request * meaning: there is a `set-cookie` header that will set * the session cookie. This is the fetched session. And it's set * by `setNewSession` method. */ newSession: { session: Session & Record<string, any>; user: User & Record<string, any>; } | null; session: { session: Session & Record<string, any>; user: User & Record<string, any>; } | null; setNewSession: (session: { session: Session & Record<string, any>; user: User & Record<string, any>; } | null) => void; socialProviders: a[]; authCookies: BetterAuthCookies; logger: ReturnType<typeof createLogger>; rateLimit: { enabled: boolean; window: number; max: number; storage: "memory" | "database" | "secondary-storage"; } & BetterAuthOptions["rateLimit"]; adapter: Adapter; internalAdapter: ReturnType<typeof createInternalAdapter>; createAuthCookie: ReturnType<typeof createCookieGetter>; secret: string; sessionConfig: { updateAge: number; expiresIn: number; freshAge: number; }; generateId: (options: { model: e<Models, string>; size?: number; }) => string; secondaryStorage: SecondaryStorage | undefined; password: { hash: (password: string) => Promise<string>; verify: (data: { password: string; hash: string; }) => Promise<boolean>; config: { minPasswordLength: number; maxPasswordLength: number; }; checkPassword: typeof checkPassword; }; tables: ReturnType<typeof getAuthTables>; runMigrations: () => Promise<void>; }; declare const createAuthMiddleware: { <Options extends better_call.MiddlewareOptions, R>(options: Options, handler: (ctx: better_call.MiddlewareContext<Options, AuthContext & { returned?: unknown; responseHeaders?: Headers; }>) => Promise<R>): (inputContext: better_call.MiddlewareInputContext<Options>) => Promise<R>; <Options extends better_call.MiddlewareOptions, R_1>(handler: (ctx: better_call.MiddlewareContext<Options, AuthContext & { returned?: unknown; responseHeaders?: Headers; }>) => Promise<R_1>): (inputContext: better_call.MiddlewareInputContext<Options>) => Promise<R_1>; }; type AuthMiddleware = ReturnType<typeof createAuthMiddleware>; type FieldType = "string" | "number" | "boolean" | "date" | `${"string" | "number"}[]` | Array<L>; type Primitive = string | number | boolean | Date | null | undefined | string[] | number[]; type FieldAttributeConfig<T extends FieldType = FieldType> = { /** * If the field should be required on a new record. * @default true */ required?: boolean; /** * If the value should be returned on a response body. * @default true */ returned?: boolean; /** * If a value should be provided when creating a new record. * @default true */ input?: boolean; /** * Default value for the field * * Note: This will not create a default value on the database level. It will only * be used when creating a new record. */ defaultValue?: Primitive | (() => Primitive); /** * transform the value before storing it. */ transform?: { input?: (value: Primitive) => Primitive | Promise<Primitive>; output?: (value: Primitive) => Primitive | Promise<Primitive>; }; /** * Reference to another model. */ references?: { /** * The model to reference. */ model: string; /** * The field on the referenced model. */ field: string; /** * The action to perform when the reference is deleted. * @default "cascade" */ onDelete?: "no action" | "restrict" | "cascade" | "set null" | "set default"; }; unique?: boolean; /** * If the field should be a bigint on the database instead of integer. */ bigint?: boolean; /** * A zod schema to validate the value. */ validator?: { input?: ZodSchema; output?: ZodSchema; }; /** * The name of the field on the database. */ fieldName?: string; /** * If the field should be sortable. * * applicable only for `text` type. * It's useful to mark fields varchar instead of text. */ sortable?: boolean; }; type FieldAttribute<T extends FieldType = FieldType> = { type: T; } & FieldAttributeConfig<T>; type AuthPluginSchema = { [table in string]: { fields: { [field in string]: FieldAttribute; }; disableMigration?: boolean; modelName?: string; }; }; type BetterAuthPlugin = { id: L; /** * The init function is called when the plugin is initialized. * You can return a new context or modify the existing context. */ init?: (ctx: AuthContext) => { context?: D<Omit<AuthContext, "options">>; options?: Partial<BetterAuthOptions>; } | void; endpoints?: { [key: string]: Endpoint; }; middlewares?: { path: string; middleware: Middleware; }[]; onRequest?: (request: Request, ctx: AuthContext) => Promise<{ response: Response; } | { request: Request; } | void>; onResponse?: (response: Response, ctx: AuthContext) => Promise<{ response: Response; } | void>; hooks?: { before?: { matcher: (context: HookEndpointContext) => boolean; handler: AuthMiddleware; }[]; after?: { matcher: (context: HookEndpointContext) => boolean; handler: AuthMiddleware; }[]; }; /** * Schema the plugin needs * * This will also be used to migrate the database. If the fields are dynamic from the plugins * configuration each time the configuration is changed a new migration will be created. * * NOTE: If you want to create migrations manually using * migrations option or any other way you * can disable migration per table basis. * * @example * ```ts * schema: { * user: { * fields: { * email: { * type: "string", * }, * emailVerified: { * type: "boolean", * defaultValue: false, * }, * }, * } * } as AuthPluginSchema * ``` */ schema?: AuthPluginSchema; /** * The migrations of the plugin. If you define schema that will automatically create * migrations for you. * * ⚠️ Only uses this if you dont't want to use the schema option and you disabled migrations for * the tables. */ migrations?: Record<string, Migration>; /** * The options of the plugin */ options?: Record<string, any>; /** * types to be inferred */ $Infer?: Record<string, any>; /** * The rate limit rules to apply to specific paths. */ rateLimit?: { window: number; max: number; pathMatcher: (path: string) => boolean; }[]; /** * The error codes returned by the plugin */ $ERROR_CODES?: Record<string, string>; }; /** * Adapter where clause */ type Where = { operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "contains" | "starts_with" | "ends_with"; value: string | number | boolean | string[] | number[] | Date | null; field: string; connector?: "AND" | "OR"; }; /** * Adapter Interface */ type Adapter = { id: string; create: <T extends Record<string, any>, R = T>(data: { model: string; data: Omit<T, "id">; select?: string[]; /** * By default, any `id` provided in `data` will be ignored. * * If you want to force the `id` to be the same as the `data.id`, set this to `true`. */ forceAllowId?: boolean; }) => Promise<R>; findOne: <T>(data: { model: string; where: Where[]; select?: string[]; }) => Promise<T | null>; findMany: <T>(data: { model: string; where?: Where[]; limit?: number; sortBy?: { field: string; direction: "asc" | "desc"; }; offset?: number; }) => Promise<T[]>; count: (data: { model: string; where?: Where[]; }) => Promise<number>; /** * ⚠︎ Update may not return the updated data * if multiple where clauses are provided */ update: <T>(data: { model: string; where: Where[]; update: Record<string, any>; }) => Promise<T | null>; updateMany: (data: { model: string; where: Where[]; update: Record<string, any>; }) => Promise<number>; delete: <T>(data: { model: string; where: Where[]; }) => Promise<void>; deleteMany: (data: { model: string; where: Where[]; }) => Promise<number>; /** * * @param options * @param file - file path if provided by the user */ createSchema?: (options: BetterAuthOptions, file?: string) => Promise<AdapterSchemaCreation>; options?: Record<string, any>; }; type AdapterSchemaCreation = { /** * Code to be inserted into the file */ code: string; /** * Path to the file, including the file name and extension. * Relative paths are supported, with the current working directory of the developer's project as the base. */ path: string; /** * Append the file if it already exists. * Note: This will not apply if `overwrite` is set to true. */ append?: boolean; /** * Overwrite the file if it already exists */ overwrite?: boolean; }; interface AdapterInstance { (options: BetterAuthOptions): Adapter; } interface SecondaryStorage { /** * * @param key - Key to get * @returns - Value of the key */ get: (key: string) => Promise<string | null> | string | null; set: ( /** * Key to store */ key: string, /** * Value to store */ value: string, /** * Time to live in seconds */ ttl?: number) => Promise<void | null | string> | void; /** * * @param key - Key to delete */ delete: (key: string) => Promise<void | null | string> | void; } type KyselyDatabaseType = "postgres" | "mysql" | "sqlite" | "mssql"; declare const accountSchema: z.ZodObject<{ id: z.ZodString; providerId: z.ZodString; accountId: z.ZodString; userId: z.ZodCoercedString<unknown>; accessToken: z.ZodOptional<z.ZodNullable<z.ZodString>>; refreshToken: z.ZodOptional<z.ZodNullable<z.ZodString>>; idToken: z.ZodOptional<z.ZodNullable<z.ZodString>>; accessTokenExpiresAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>; refreshTokenExpiresAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>; scope: z.ZodOptional<z.ZodNullable<z.ZodString>>; password: z.ZodOptional<z.ZodNullable<z.ZodString>>; createdAt: z.ZodDefault<z.ZodDate>; updatedAt: z.ZodDefault<z.ZodDate>; }, z.core.$strip>; declare const userSchema: z.ZodObject<{ id: z.ZodString; email: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>; emailVerified: z.ZodDefault<z.ZodBoolean>; name: z.ZodString; image: z.ZodOptional<z.ZodNullable<z.ZodString>>; createdAt: z.ZodDefault<z.ZodDate>; updatedAt: z.ZodDefault<z.ZodDate>; }, z.core.$strip>; declare const sessionSchema: z.ZodObject<{ id: z.ZodString; userId: z.ZodCoercedString<unknown>; expiresAt: z.ZodDate; createdAt: z.ZodDefault<z.ZodDate>; updatedAt: z.ZodDefault<z.ZodDate>; token: z.ZodString; ipAddress: z.ZodOptional<z.ZodNullable<z.ZodString>>; userAgent: z.ZodOptional<z.ZodNullable<z.ZodString>>; }, z.core.$strip>; declare const verificationSchema: z.ZodObject<{ id: z.ZodString; value: z.ZodString; createdAt: z.ZodDefault<z.ZodDate>; updatedAt: z.ZodDefault<z.ZodDate>; expiresAt: z.ZodDate; identifier: z.ZodString; }, z.core.$strip>; type Models = "user" | "account" | "session" | "verification" | "rate-limit" | "organization" | "member" | "invitation" | "jwks" | "passkey" | "two-factor"; interface RateLimit { /** * The key to use for rate limiting */ key: string; /** * The number of requests made */ count: number; /** * The last request time in milliseconds */ lastRequest: number; } type User = z.infer<typeof userSchema>; type Account = z.infer<typeof accountSchema>; type Session = z.infer<typeof sessionSchema>; type Verification = z.infer<typeof verificationSchema>; type AdapterDebugLogs = boolean | { /** * Useful when you want to log only certain conditions. */ logCondition?: (() => boolean) | undefined; create?: boolean; update?: boolean; updateMany?: boolean; findOne?: boolean; findMany?: boolean; delete?: boolean; deleteMany?: boolean; count?: boolean; } | { /** * Only used for adapter tests to show debug logs if a test fails. * * @deprecated Not actually deprecated. Doing this for IDEs to show this option at the very bottom and stop end-users from using this. */ isRunningAdapterTests: boolean; }; type BetterAuthOptions = { /** * The name of the application * * process.env.APP_NAME * * @default "Better Auth" */ appName?: string; /** * Base URL for the Better Auth. This is typically the * root URL where your application server is hosted. * If not explicitly set, * the system will check the following environment variable: * * process.env.BETTER_AUTH_URL * * If not set it will throw an error. */ baseURL?: string; /** * Base path for the Better Auth. This is typically * the path where the * Better Auth routes are mounted. * * @default "/api/auth" */ basePath?: string; /** * The secret to use for encryption, * signing and hashing. * * By default Better Auth will look for * the following environment variables: * process.env.BETTER_AUTH_SECRET, * process.env.AUTH_SECRET * If none of these environment * variables are set, * it will default to * "better-auth-secret-123456789". * * on production if it's not set * it will throw an error. * * you can generate a good secret * using the following command: * @example * ```bash * openssl rand -base64 32 * ``` */ secret?: string; /** * Database configuration */ database?: PostgresPool | MysqlPool | Database | Dialect | AdapterInstance | Database$1 | { dialect: Dialect; type: KyselyDatabaseType; /** * casing for table names * * @default "camel" */ casing?: "snake" | "camel"; /** * Enable debug logs for the adapter * * @default false */ debugLogs?: AdapterDebugLogs; } | { /** * Kysely instance */ db: Kysely<any>; /** * Database type between postgres, mysql and sqlite */ type: KyselyDatabaseType; /** * casing for table names * * @default "camel" */ casing?: "snake" | "camel"; /** * Enable debug logs for the adapter * * @default false */ debugLogs?: AdapterDebugLogs; }; /** * Secondary storage configuration * * This is used to store session and rate limit data. */ secondaryStorage?: SecondaryStorage; /** * Email verification configuration */ emailVerification?: { /** * Send a verification email * @param data the data object * @param request the request object */ sendVerificationEmail?: ( /** * @param user the user to send the * verification email to * @param url the URL to send the verification email to * it contains the token as well * @param token the token to send the verification email to */ data: { user: User; url: string; token: string; }, /** * The request object */ request?: Request) => Promise<void>; /** * Send a verification email automatically * after sign up * * @default false */ sendOnSignUp?: boolean; /** * Send a verification email automatically * on sign in when the user's email is not verified * * @default false */ sendOnSignIn?: boolean; /** * Auto signin the user after they verify their email */ autoSignInAfterVerification?: boolean; /** * Number of seconds the verification token is * valid for. * @default 3600 seconds (1 hour) */ expiresIn?: number; /** * A function that is called when a user verifies their email * @param user the user that verified their email * @param request the request object */ onEmailVerification?: (user: User, request?: Request) => Promise<void>; /** * A function that is called when a user's email is updated to verified * @param user the user that verified their email * @param request the request object */ afterEmailVerification?: (user: User, request?: Request) => Promise<void>; }; /** * Email and password authentication */ emailAndPassword?: { /** * Enable email and password authentication * * @default false */ enabled: boolean; /** * Disable email and password sign up * * @default false */ disableSignUp?: boolean; /** * Require email verification before a session * can be created for the user. * * if the user is not verified, the user will not be able to sign in * and on sign in attempts, the user will be prompted to verify their email. */ requireEmailVerification?: boolean; /** * The maximum length of the password. * * @default 128 */ maxPasswordLength?: number; /** * The minimum length of the password. * * @default 8 */ minPasswordLength?: number; /** * send reset password */ sendResetPassword?: ( /** * @param user the user to send the * reset password email to * @param url the URL to send the reset password email to * @param token the token to send to the user (could be used instead of sending the url * if you need to redirect the user to custom route) */ data: { user: User; url: string; token: string; }, /** * The request object */ request?: Request) => Promise<void>; /** * Number of seconds the reset password token is * valid for. * @default 1 hour (60 * 60) */ resetPasswordTokenExpiresIn?: number; /** * A callback function that is triggered * when a user's password is changed successfully. */ onPasswordReset?: (data: { user: User; }, request?: Request) => Promise<void>; /** * Password hashing and verification * * By default Scrypt is used for password hashing and * verification. You can provide your own hashing and * verification function. if you want to use a * different algorithm. */ password?: { hash?: (password: string) => Promise<string>; verify?: (data: { hash: string; password: string; }) => Promise<boolean>; }; /** * Automatically sign in the user after sign up * * @default true */ autoSignIn?: boolean; /** * Whether to revoke all other sessions when resetting password * @default false */ revokeSessionsOnPasswordReset?: boolean; }; /** * list of social providers */ socialProviders?: S; /** * List of Better Auth plugins */ plugins?: BetterAuthPlugin[]; /** * User configuration */ user?: { /** * The model name for the user. Defaults to "user". */ modelName?: string; /** * Map fields * * @example * ```ts * { * userId: "user_id" * } * ``` */ fields?: Partial<Record<keyof O<User>, string>>; /** * Additional fields for the session */ additionalFields?: { [key: string]: FieldAttribute; }; /** * Changing email configuration */ changeEmail?: { /** * Enable changing email * @default false */ enabled: boolean; /** * Send a verification email when the user changes their email. * @param data the data object * @param request the request object */ sendChangeEmailVerification?: (data: { user: User; newEmail: string; url: string; token: string; }, request?: Request) => Promise<void>; }; /** * User deletion configuration */ deleteUser?: { /** * Enable user deletion */ enabled?: boolean; /** * Send a verification email when the user deletes their account. * * if this is not set, the user will be deleted immediately. * @param data the data object * @param request the request object */ sendDeleteAccountVerification?: (data: { user: User; url: string; token: string; }, request?: Request) => Promise<void>; /** * A function that is called before a user is deleted. * * to interrupt with error you can throw `APIError` */ beforeDelete?: (user: User, request?: Request) => Promise<void>; /** * A function that is called after a user is deleted. * * This is useful for cleaning up user data */ afterDelete?: (user: User, request?: Request) => Promise<void>; /** * The expiration time for the delete token. * * @default 1 day (60 * 60 * 24) in seconds */ deleteTokenExpiresIn?: number; }; }; session?: { /** * The model name for the session. * * @default "session" */ modelName?: string; /** * Map fields * * @example * ```ts * { * userId: "user_id" * } */ fields?: Partial<Record<keyof O<Session>, string>>; /** * Expiration time for the session token. The value * should be in seconds. * @default 7 days (60 * 60 * 24 * 7) */ expiresIn?: number; /** * How often the session should be refreshed. The value * should be in seconds. * If set 0 the session will be refreshed every time it is used. * @default 1 day (60 * 60 * 24) */ updateAge?: number; /** * Disable session refresh so that the session is not updated * regardless of the `updateAge` option. * * @default false */ disableSessionRefresh?: boolean; /** * Additional fields for the session */ additionalFields?: { [key: string]: FieldAttribute; }; /** * By default if secondary storage is provided * the session is stored in the secondary storage. * * Set this to true to store the session in the database * as well. * * Reads are always done from the secondary storage. * * @default false */ storeSessionInDatabase?: boolean; /** * By default, sessions are deleted from the database when secondary storage * is provided when session is revoked. * * Set this to true to preserve session records in the database, * even if they are deleted from the secondary storage. * * @default false */ preserveSessionInDatabase?: boolean; /** * Enable caching session in cookie */ cookieCache?: { /** * max age of the cookie * @default 5 minutes (5 * 60) */ maxAge?: number; /** * Enable caching session in cookie * @default false */ enabled?: boolean; }; /** * The age of the session to consider it fresh. * * This is used to check if the session is fresh * for sensitive operations. (e.g. deleting an account) * * If the session is not fresh, the user should be prompted * to sign in again. * * If set to 0, the session will be considered fresh every time. (⚠︎ not recommended) * * @default 1 day (60 * 60 * 24) */ freshAge?: number; }; account?: { /** * The model name for the account. Defaults to "account". */ modelName?: string; /** * Map fields */ fields?: Partial<Record<keyof O<Account>, string>>; /** * When enabled (true), the user account data (accessToken, idToken, refreshToken, etc.) * will be updated on sign in with the latest data from the provider. * * @default true */ updateAccountOnSignIn?: boolean; /** * Configuration for account linking. */ accountLinking?: { /** * Enable account linking * * @default true */ enabled?: boolean; /** * List of trusted providers */ trustedProviders?: Array<e<b[number] | "email-password", string>>; /** * If enabled (true), this will allow users to manually linking accounts with different email addresses than the main user. * * @default false * * ⚠️ Warning: enabling this might lead to account takeovers, so proceed with caution. */ allowDifferentEmails?: boolean; /** * If enabled (true), this will allow users to unlink all accounts. * * @default false */ allowUnlinkingAll?: boolean; /** * If enabled (true), this will update the user information based on the newly linked account * * @default false */ updateUserInfoOnLink?: boolean; }; /** * Encrypt OAuth tokens * * By default, OAuth tokens (access tokens, refresh tokens, ID tokens) are stored in plain text in the database. * This poses a security risk if your database is compromised, as attackers could gain access to user accounts * on external services. * * When enabled, tokens are encrypted using AES-256-GCM before storage, providing protection against: * - Database breaches and unauthorized access to raw token data * - Internal threats from database administrators or compromised credentials * - Token exposure in database backups and logs * @default false */ encryptOAuthTokens?: boolean; }; /** * Verification configuration */ verification?: { /** * Change the modelName of the verification table */ modelName?: string; /** * Map verification fields */ fields?: Partial<Record<keyof O<Verification>, string>>; /** * disable cleaning up expired values when a verification value is * fetched */ disableCleanup?: boolean; }; /** * List of trusted origins. */ trustedOrigins?: string[] | ((request: Request) => string[] | Promise<string[]>); /** * Rate limiting configuration */ rateLimit?: { /** * By default, rate limiting is only * enabled on production. */ enabled?: boolean; /** * Default window to use for rate limiting. The value * should be in seconds. * * @default 10 seconds */ window?: number; /** * The default maximum number of requests allowed within the window. * * @default 100 requests */ max?: number; /** * Custom rate limit rules to apply to * specific paths. */ customRules?: { [key: string]: { /** * The window to use for the custom rule. */ window: number; /** * The maximum number of requests allowed within the window. */ max: number; } | ((request: Request) => { window: number; max: number; } | Promise<{ window: number; max: number; }>); }; /** * Storage configuration * * By default, rate limiting is stored in memory. If you passed a * secondary storage, rate limiting will be stored in the secondary * storage. * * @default "memory" */ storage?: "memory" | "database" | "secondary-storage"; /** * If database is used as storage, the name of the table to * use for rate limiting. * * @default "rateLimit" */ modelName?: string; /** * Custom field names for the rate limit table */ fields?: Record<keyof RateLimit, string>; /** * custom storage configuration. * * NOTE: If custom storage is used storage * is ignored */ customStorage?: { get: (key: string) => Promise<RateLimit | undefined>; set: (key: string, value: RateLimit) => Promise<void>; }; }; /** * Advanced options */ advanced?: { /** * Ip address configuration */ ipAddress?: { /** * List of headers to use for ip address * * Ip address is used for rate limiting and session tracking * * @example ["x-client-ip", "x-forwarded-for", "cf-connecting-ip"] * * @default * @link https://github.com/better-auth/better-auth/blob/main/packages/better-auth/src/utils/get-request-ip.ts#L8 */ ipAddressHeaders?: string[]; /** * Disable ip tracking * * ⚠︎ This is a security risk and it may expose your application to abuse */ disableIpTracking?: boolean; }; /** * Use secure cookies * * @default false */ useSecureCookies?: boolean; /** * Disable trusted origins check * * ⚠︎ This is a security risk and it may expose your application to CSRF attacks */ disableCSRFCheck?: boolean; /** * Configure cookies to be cross subdomains */ crossSubDomainCookies?: { /** * Enable cross subdomain cookies */ enabled: boolean; /** * Additional cookies to be shared across subdomains */ additionalCookies?: string[]; /** * The domain to use for the cookies * * By default, the domain will be the root * domain from the base URL. */ domain?: string; }; cookies?: { [key: string]: { name?: string; attributes?: CookieOptions; }; }; defaultCookieAttributes?: CookieOptions; /** * Prefix for cookies. If a cookie name is provided * in cookies config, this will be overridden. * * @default * ```txt * "appName" -> which defaults to "better-auth" * ``` */ cookiePrefix?: string; /** * Database configuration. */ database?: { /** * The default number of records to return from the database * when using the `findMany` adapter method. * * @default 100 */ defaultFindManyLimit?: number; /** * If your database auto increments number ids, set this to `true`. * * Note: If enabled, we will not handle ID generation (including if you use `generateId`), and it would be expected that your database will provide the ID automatically. * * @default false */ useNumberId?: boolean; /** * Custom generateId function. * * If not provided, random ids will be generated. * If set to false, the database's auto generated id will be used. */ generateId?: ((options: { model: e<Models, string>; size?: number; }) => string) | false; }; /** * Custom generateId function. * * If not provided, random ids will be generated. * If set to false, the database's auto generated id will be used. * * @deprecated Please use `database.generateId` instead. This will be potentially removed in future releases. */ generateId?: ((options: { model: e<Models, string>; size?: number; }) => string) | false; }; logger?: Logger; /** * allows you to define custom hooks that can be * executed during lifecycle of core database * operations. */ databaseHooks?: { /** * User hooks */ user?: { create?: { /** * Hook that is called before a user is created. * if the hook returns false, the user will not be created. * If the hook returns an object, it'll be used instead of the original data */ before?: (user: User, context?: GenericEndpointContext) => Promise<boolean | void | { data: Partial<User> & Record<string, any>; }>; /** * Hook that is called after a user is created. */ after?: (user: User, context?: GenericEndpointContext) => Promise<void>; }; update?: { /** * Hook that is called before a user is updated. * if the hook returns false, the user will not be updated. * If the hook returns an object, it'll be used instead of the original data */ before?: (user: Partial<User>, context?: GenericEndpointContext) => Promise<boolean | void | { data: Partial<User & Record<string, any>>; }>; /** * Hook that is called after a user is updated. */ after?: (user: User, context?: GenericEndpointContext) => Promise<void>; }; }; /** * Session Hook */ session?: { create?: { /** * Hook that is called before a session is created. * if the hook returns false, the session will not be created. * If the hook returns an object, it'll be used instead of the original data */ before?: (session: Session, context?: GenericEndpointContext) => Promise<boolean | void | { data: Partial<Session> & Record<string, any>; }>; /** * Hook that is called after a session is created. */ after?: (session: Session, context?: GenericEndpointContext) => Promise<void>; }; /** * Update hook */ update?: { /** * Hook that is called before a user is updated. * if the hook returns false, the session will not be updated. * If the hook returns an object, it'll be used instead of the original data */ before?: (session: Partial<Session>, context?: GenericEndpointContext) => Promise<boolean | void | { data: Session & Record<string, any>; }>; /** * Hook that is called after a session is updated. */ after?: (session: Session, context?: GenericEndpointContext) => Promise<void>; }; }; /** * Account Hook */ account?: { create?: { /** * Hook that is called before a account is created. * If the hook returns false, the account will not be created. * If the hook returns an object, it'll be used instead of the original data */ before?: (account: Account, context?: GenericEndpointContext) => Promise<boolean | void | { data: Partial<Account> & Record<string, any>; }>; /** * Hook that is called after a account is created. */ after?: (account: Account, context?: GenericEndpointContext) => Promise<void>; }; /** * Update hook */ update?: { /** * Hook that is called before a account is update. * If the hook returns false, the user will not be updated. * If the hook returns an object, it'll be used instead of the original data */ before?: (account: Partial<Account>, context?: GenericEndpointContext) => Promise<boolean | void | { data: Partial<Account & Record<string, any>>; }>; /** * Hook that is called after a account is updated. */ after?: (account: Account, context?: GenericEndpointContext) => Promise<void>; }; }; /** * Verification Hook */ verification?: { create?: { /** * Hook that is called before a verification is created. * if the hook returns false, the verification will not be created. * If the hook returns an object, it'll be used instead of the original data */ before?: (verification: Verification, context?: GenericEndpointContext) => Promise<boolean | void | { data: Partial<Verification> & Record<string, any>; }>; /** * Hook that is called after a verification is created. */ after?: (verification: Verification, context?: GenericEndpointContext) => Promise<void>; }; update?: { /** * Hook that is called before a verification is updated. * if the hook returns false, the verification will not be updated. * If the hook returns an object, it'll be used instead of the original data */ before?: (verification: Partial<Verification>, context?: GenericEndpointContext) => Promise<boolean | void | { data: Partial<Verification & Record<string, any>>; }>; /** * Hook that is called after a verification is updated. */ after?: (verification: Verification, context?: GenericEndpointContext) => Promise<void>; }; }; }; /** * API error handling */ onAPIError?: { /** * Throw an error on API error * * @default false */ throw?: boolean; /** * Custom error handler * * @param error * @param ctx - Auth context */ onError?: (error: unknown, ctx: AuthContext) => void | Promise<void>; /** * The URL to redirect to on error * * When errorURL is provided, the error will be added to the URL as a query parameter * and the user will be redirected to the errorURL. * * @default - "/api/auth/error" */ errorURL?: string; }; /** * Hooks */ hooks?: { /** * Before a request is processed */ before?: AuthMiddleware; /** * After a request is processed */ after?: AuthMiddleware; }; /** * Disabled paths * * Paths you want to disable. */ disabledPaths?: string[]; }; declare const createInternalAdapter: (adapter: Adapter, ctx: { options: BetterAuthOptions; hooks: Exclude<BetterAuthOptions["databaseHooks"], undefined>[]; generateId: AuthContext["generateId"]; }) => { createOAuthUser: (user: Omit<User, "id" | "createdAt" | "updatedAt"> & Partial<User>, account: Omit<Account, "userId" | "id" | "createdAt" | "updatedAt"> & Partial<Account>, context?: GenericEndpointContext) => Promise<{ user: any; account: any; }>; createUser: <T>(user: Omit<User, "id" | "createdAt" | "updatedAt" | "emailVerified"> & Partial<User> & Record<string, any>, context?: GenericEndpointContext) => Promise<T & { id: string; email: string; emailVerified: boolean; name: string; createdAt: Date; updatedAt: Date; image?: string | null | undefined; }>; createAccount: <T>(account: Omit<Account, "id" | "createdAt" | "updatedAt"> & Partial<Account> & Record<string, any>, context?: GenericEndpointContext) => Promise<T & { id: string; providerId: string; accountId: string; userId: string; createdAt: Date; updatedAt: Date; accessToken?: string | null | undefined; refreshToken?: string | null | undefined; idToken?: string | null | undefined; accessTokenExpiresAt?: Date | null | undefined; refreshTokenExpiresAt?: Date | null | undefined; scope?: string | null | undefined; password?: string | null | undefined; }>; listSessions: (userId: string) => Promise<{ id: string; userId: string; expiresAt: Date; createdAt: Date; updatedAt: Date; token: string; ipAddress?: string | null | undefined; userAgent?: string | null | undefined; }[]>; listUsers: (limit?: number, offset?: number, sortBy?: { field: string; direction: "asc" | "desc"; }, where?: Where[]) => Promise<{ id: string; email: string; emailVerified: boolean; name: string; createdAt: Date; updatedAt: Date; image?: string | null | undefined; }[]>; countTotalUsers: (where?: Where[]) => Promise<number>; deleteUser: (userId: string) => Promise<void>; createSession: (userId: string, ctx: GenericEndpointContext, dontRememberMe?: boolean, override?: Partial<Session> & Record<string, any>, overrideAll?: boolean) => Promise<{ id: string; userId: string; expiresAt: Date; createdAt: Date; updatedAt: Date; token: string; ipAddress?: string | null | undefined; userAgent?: string | null | undefined; }>; findSession: (token: string) => Promise<{ session: Session & Record<string, any>; user: User & Record<string, any>; } | null>; findSessions: (sessionTokens: string[]) => Promise<{ session: Session; user: User; }[]>; updateSession: (sessionToken: string, session: Partial<Session> & Record<string, any>, context?: GenericEndpointContext) => Promise<any>; deleteSession: (token: string) => Promise<void>; deleteAccounts: (userId: string) => Promise<void>; deleteAccount: (accountId: string) => Promise<void>; deleteSessions: (userIdOrSessionTokens: string | string[]) => Promise<void>; findOAuthUser: (email: string, accountId: string, providerId: string) => Promise<{ user: { id: string; email: string; emailVerified: boolean; name: string; createdAt: Date; updatedAt: Date; image?: string | null | undefined; }; accounts: { id: string; providerId: string; accountId: string; userId: string; createdAt: Date; updatedAt: Date; accessToken?: string | null | undefined; refreshToken?: string | null | undefined; idToken?: string | null | undefined; accessTokenExpiresAt?: Date | null | undefined; refreshTokenExpiresAt?: Date | null | undefined; scope?: string | null | undefined;