@better-auth/core 1.7.0-rc.3 → 1.7.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -123,6 +123,7 @@ declare const createAuthMiddleware: {
123
123
  updateAge: number;
124
124
  expiresIn: number;
125
125
  freshAge: number;
126
+ cookieCacheSigner?: import("@better-auth/core").CookieCacheSigner | undefined;
126
127
  cookieRefreshCache: false | {
127
128
  enabled: true;
128
129
  updateAge: number;
@@ -252,6 +253,7 @@ declare const createAuthMiddleware: {
252
253
  updateAge: number;
253
254
  expiresIn: number;
254
255
  freshAge: number;
256
+ cookieCacheSigner?: import("@better-auth/core").CookieCacheSigner | undefined;
255
257
  cookieRefreshCache: false | {
256
258
  enabled: true;
257
259
  updateAge: number;
@@ -2,7 +2,7 @@
2
2
  const symbol = Symbol.for("better-auth:global");
3
3
  let bind = null;
4
4
  const __context = {};
5
- const __betterAuthVersion = "1.7.0-rc.3";
5
+ const __betterAuthVersion = "1.7.0-rc.4";
6
6
  /**
7
7
  * We store context instance in the globalThis.
8
8
  *
@@ -198,7 +198,7 @@ const buildAuthTables = (options) => {
198
198
  account: {
199
199
  modelName: options.account?.modelName || "account",
200
200
  indexes: mergeTableIndexes([{
201
- fields: ["issuer", "providerAccountId"],
201
+ fields: ["issuer", "accountId"],
202
202
  unique: true
203
203
  }], account?.indexes),
204
204
  fields: {
@@ -207,10 +207,10 @@ const buildAuthTables = (options) => {
207
207
  required: true,
208
208
  fieldName: options.account?.fields?.issuer || "issuer"
209
209
  },
210
- providerAccountId: {
210
+ accountId: {
211
211
  type: "string",
212
212
  required: true,
213
- fieldName: options.account?.fields?.providerAccountId || "providerAccountId"
213
+ fieldName: options.account?.fields?.accountId || "accountId"
214
214
  },
215
215
  providerId: {
216
216
  type: "string",
@@ -10,7 +10,7 @@ declare const accountSchema: z.ZodObject<{
10
10
  updatedAt: z.ZodDefault<z.ZodDate>;
11
11
  providerId: z.ZodString;
12
12
  issuer: z.ZodString;
13
- providerAccountId: z.ZodString;
13
+ accountId: z.ZodString;
14
14
  userId: z.ZodCoercedString<unknown>;
15
15
  accessToken: z.ZodOptional<z.ZodNullable<z.ZodString>>;
16
16
  refreshToken: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -22,7 +22,7 @@ declare const accountSchema: z.ZodObject<{
22
22
  }, z.core.$strip>;
23
23
  type BaseAccount = z.infer<typeof accountSchema>;
24
24
  /** The stable provider-side key used to recognize an account. */
25
- type AccountKey = Readonly<Pick<BaseAccount, "issuer" | "providerAccountId">>;
25
+ type AccountKey = Readonly<Pick<BaseAccount, "issuer" | "accountId">>;
26
26
  /**
27
27
  * Creates the synthetic issuer used by providers without an issuer of their own.
28
28
  */
@@ -4,7 +4,7 @@ import * as z from "zod";
4
4
  const accountSchema = coreSchema.extend({
5
5
  providerId: z.string(),
6
6
  issuer: z.string(),
7
- providerAccountId: z.string(),
7
+ accountId: z.string(),
8
8
  userId: z.coerce.string(),
9
9
  accessToken: z.string().nullish(),
10
10
  refreshToken: z.string().nullish(),
package/dist/index.d.mts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { Awaitable, AwaitableFunction, LiteralString, LiteralUnion, Prettify, Primitive, UnionToIntersection } from "./types/helper.mjs";
2
2
  import { BetterAuthPlugin, BetterAuthPluginErrorCodePart, HookEndpointContext } from "./types/plugin.mjs";
3
3
  import { BaseURLConfig, BetterAuthAdvancedOptions, BetterAuthDBOptions, BetterAuthOptions, BetterAuthRateLimitOptions, BetterAuthRateLimitRule, BetterAuthRateLimitStorage, DynamicBaseURLConfig, GenerateIdFn, StoreIdentifierOption, UserProvisioningSource, ValidateUserInfoAction, ValidateUserInfoMethod, ValidateUserInfoOAuthInfo, ValidateUserInfoResult, ValidateUserInfoSSOInfo, ValidateUserInfoSource } from "./types/init-options.mjs";
4
- import { BetterAuthCookie, BetterAuthCookies } from "./types/cookie.mjs";
4
+ import { BetterAuthCookie, BetterAuthCookies, CookieCachePayload } from "./types/cookie.mjs";
5
5
  import { SecretConfig } from "./types/secret.mjs";
6
- import { AuthContext, BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, GenericEndpointContext, InfoContext, InternalAdapter, PluginContext } from "./types/context.mjs";
6
+ import { AuthContext, BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, CookieCacheSigner, GenericEndpointContext, InfoContext, InternalAdapter, PluginContext } from "./types/context.mjs";
7
7
  import { BetterAuthClientOptions, BetterAuthClientPlugin, ClientAtomListener, ClientFetchOption, ClientStore } from "./types/plugin-client.mjs";
8
8
  import { StandardSchemaV1 } from "./types/index.mjs";
9
- export type { AuthContext, Awaitable, AwaitableFunction, BaseURLConfig, BetterAuthAdvancedOptions, BetterAuthClientOptions, BetterAuthClientPlugin, BetterAuthCookie, BetterAuthCookies, BetterAuthDBOptions, BetterAuthOptions, BetterAuthPlugin, BetterAuthPluginErrorCodePart, BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, BetterAuthRateLimitOptions, BetterAuthRateLimitRule, BetterAuthRateLimitStorage, ClientAtomListener, ClientFetchOption, ClientStore, DynamicBaseURLConfig, GenerateIdFn, GenericEndpointContext, HookEndpointContext, InfoContext, InternalAdapter, LiteralString, LiteralUnion, PluginContext, Prettify, Primitive, SecretConfig, StandardSchemaV1, StoreIdentifierOption, UnionToIntersection, UserProvisioningSource, ValidateUserInfoAction, ValidateUserInfoMethod, ValidateUserInfoOAuthInfo, ValidateUserInfoResult, ValidateUserInfoSSOInfo, ValidateUserInfoSource };
9
+ export type { AuthContext, Awaitable, AwaitableFunction, BaseURLConfig, BetterAuthAdvancedOptions, BetterAuthClientOptions, BetterAuthClientPlugin, BetterAuthCookie, BetterAuthCookies, BetterAuthDBOptions, BetterAuthOptions, BetterAuthPlugin, BetterAuthPluginErrorCodePart, BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, BetterAuthRateLimitOptions, BetterAuthRateLimitRule, BetterAuthRateLimitStorage, ClientAtomListener, ClientFetchOption, ClientStore, CookieCachePayload, CookieCacheSigner, DynamicBaseURLConfig, GenerateIdFn, GenericEndpointContext, HookEndpointContext, InfoContext, InternalAdapter, LiteralString, LiteralUnion, PluginContext, Prettify, Primitive, SecretConfig, StandardSchemaV1, StoreIdentifierOption, UnionToIntersection, UserProvisioningSource, ValidateUserInfoAction, ValidateUserInfoMethod, ValidateUserInfoOAuthInfo, ValidateUserInfoResult, ValidateUserInfoSSOInfo, ValidateUserInfoSource };
@@ -2,7 +2,7 @@ import { ATTR_HTTP_RESPONSE_STATUS_CODE } from "./attributes.mjs";
2
2
  import { getOpenTelemetryAPI } from "./api.mjs";
3
3
  //#region src/instrumentation/tracer.ts
4
4
  const INSTRUMENTATION_SCOPE = "better-auth";
5
- const INSTRUMENTATION_VERSION = "1.7.0-rc.3";
5
+ const INSTRUMENTATION_VERSION = "1.7.0-rc.4";
6
6
  /**
7
7
  * Better-auth uses `throw ctx.redirect(url)` for flow control (e.g. OAuth
8
8
  * callbacks). These are APIErrors with 3xx status codes and should not be
@@ -70,7 +70,7 @@ declare const apple: (options: AppleOptions) => {
70
70
  name: string;
71
71
  accountSubject: ({ profile }: OAuthAccountKeyContext<AppleProfile>) => string;
72
72
  accountIssuer: string;
73
- createAuthorizationURL({ state, scopes, redirectURI, additionalParams }: {
73
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams, codeVerifier }: {
74
74
  state: string;
75
75
  codeVerifier: string;
76
76
  scopes?: string[] | undefined;
@@ -14,7 +14,7 @@ const apple = (options) => {
14
14
  name: "Apple",
15
15
  accountSubject: ({ profile }) => profile.sub,
16
16
  accountIssuer: "https://appleid.apple.com",
17
- async createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
17
+ async createAuthorizationURL({ state, scopes, redirectURI, additionalParams, codeVerifier }) {
18
18
  if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
19
19
  logger.error("Client ID and client secret are required for Apple. Make sure to provide them in the options.");
20
20
  throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
@@ -29,6 +29,7 @@ const apple = (options) => {
29
29
  scopes: _scope,
30
30
  state,
31
31
  redirectURI,
32
+ codeVerifier,
32
33
  responseMode: "form_post",
33
34
  responseType: "code id_token",
34
35
  additionalParams
@@ -45,7 +45,7 @@ declare const socialProviders: {
45
45
  name: string;
46
46
  accountSubject: ({ profile }: OAuthAccountKeyContext<AppleProfile>) => string;
47
47
  accountIssuer: string;
48
- createAuthorizationURL({ state, scopes, redirectURI, additionalParams }: {
48
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams, codeVerifier }: {
49
49
  state: string;
50
50
  codeVerifier: string;
51
51
  scopes?: string[] | undefined;
@@ -9,7 +9,7 @@ import { BetterAuthPlugin } from "./plugin.mjs";
9
9
  import { BetterAuthOptions, BetterAuthRateLimitOptions, UserProvisioningSource } from "./init-options.mjs";
10
10
  import { Account, AccountKey } from "../db/schema/account.mjs";
11
11
  import "../db/index.mjs";
12
- import { BetterAuthCookie, BetterAuthCookies } from "./cookie.mjs";
12
+ import { BetterAuthCookie, BetterAuthCookies, CookieCachePayload } from "./cookie.mjs";
13
13
  import { SecretConfig } from "./secret.mjs";
14
14
  import { OAuthProvider } from "../oauth2/oauth-provider.mjs";
15
15
  import "../oauth2/index.mjs";
@@ -54,6 +54,16 @@ type BetterAuthPluginRegistryIdentifier = keyof BetterAuthPluginRegistry<unknown
54
54
  type GenericEndpointContext<Options extends BetterAuthOptions = BetterAuthOptions> = EndpointContext<string, any> & {
55
55
  context: AuthContext<Options>;
56
56
  };
57
+ /**
58
+ * Signs and verifies session cookie-cache values.
59
+ */
60
+ type CookieCacheSigner = {
61
+ sign: (ctx: GenericEndpointContext, payload: CookieCachePayload, expiresIn: number) => Promise<string>;
62
+ verify: (ctx: GenericEndpointContext, token: string) => Promise<{
63
+ payload: CookieCachePayload;
64
+ expiresAt: number;
65
+ } | null>;
66
+ };
57
67
  interface InternalAdapter<_Options extends BetterAuthOptions = BetterAuthOptions> {
58
68
  createOAuthUser(user: Omit<User, "id" | "createdAt" | "updatedAt">, account: Omit<Account, "userId" | "id" | "createdAt" | "updatedAt"> & Partial<Account>): Promise<{
59
69
  user: User;
@@ -261,6 +271,7 @@ type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> = Plugin
261
271
  updateAge: number;
262
272
  expiresIn: number;
263
273
  freshAge: number;
274
+ cookieCacheSigner?: CookieCacheSigner | undefined;
264
275
  cookieRefreshCache: false | {
265
276
  enabled: true;
266
277
  updateAge: number;
@@ -330,4 +341,4 @@ type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> = Plugin
330
341
  runInBackgroundOrAwait: (promise: Promise<unknown> | void) => Awaitable<unknown>;
331
342
  };
332
343
  //#endregion
333
- export { AuthContext, BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, GenericEndpointContext, InfoContext, InternalAdapter, PluginContext };
344
+ export { AuthContext, BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, CookieCacheSigner, GenericEndpointContext, InfoContext, InternalAdapter, PluginContext };
@@ -1,3 +1,6 @@
1
+ import { Session } from "../db/schema/session.mjs";
2
+ import { User } from "../db/schema/user.mjs";
3
+ import "../db/index.mjs";
1
4
  import { CookieOptions } from "better-call";
2
5
  //#region src/types/cookie.d.ts
3
6
  type BetterAuthCookie = {
@@ -10,5 +13,14 @@ type BetterAuthCookies = {
10
13
  accountData: BetterAuthCookie;
11
14
  dontRememberToken: BetterAuthCookie;
12
15
  };
16
+ /**
17
+ * A validated cookie-cache payload, including legacy payloads without a version.
18
+ */
19
+ type CookieCachePayload = {
20
+ session: Session & Record<string, unknown>;
21
+ user: User & Record<string, unknown>;
22
+ updatedAt: number;
23
+ version?: string | undefined;
24
+ };
13
25
  //#endregion
14
- export { BetterAuthCookie, BetterAuthCookies };
26
+ export { BetterAuthCookie, BetterAuthCookies, CookieCachePayload };
@@ -1,9 +1,9 @@
1
1
  import { Awaitable, AwaitableFunction, LiteralString, LiteralUnion, Prettify, Primitive, UnionToIntersection } from "./helper.mjs";
2
2
  import { BetterAuthPlugin, BetterAuthPluginErrorCodePart, HookEndpointContext } from "./plugin.mjs";
3
3
  import { BaseURLConfig, BetterAuthAdvancedOptions, BetterAuthDBOptions, BetterAuthOptions, BetterAuthRateLimitOptions, BetterAuthRateLimitRule, BetterAuthRateLimitStorage, DynamicBaseURLConfig, GenerateIdFn, StoreIdentifierOption, UserProvisioningSource, ValidateUserInfoAction, ValidateUserInfoMethod, ValidateUserInfoOAuthInfo, ValidateUserInfoResult, ValidateUserInfoSSOInfo, ValidateUserInfoSource } from "./init-options.mjs";
4
- import { BetterAuthCookie, BetterAuthCookies } from "./cookie.mjs";
4
+ import { BetterAuthCookie, BetterAuthCookies, CookieCachePayload } from "./cookie.mjs";
5
5
  import { SecretConfig } from "./secret.mjs";
6
- import { AuthContext, BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, GenericEndpointContext, InfoContext, InternalAdapter, PluginContext } from "./context.mjs";
6
+ import { AuthContext, BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, CookieCacheSigner, GenericEndpointContext, InfoContext, InternalAdapter, PluginContext } from "./context.mjs";
7
7
  import { BetterAuthClientOptions, BetterAuthClientPlugin, ClientAtomListener, ClientFetchOption, ClientStore } from "./plugin-client.mjs";
8
8
  import { StandardSchemaV1 as StandardSchemaV1$1 } from "@standard-schema/spec";
9
- export type { AuthContext, Awaitable, AwaitableFunction, BaseURLConfig, BetterAuthAdvancedOptions, BetterAuthClientOptions, BetterAuthClientPlugin, BetterAuthCookie, BetterAuthCookies, BetterAuthDBOptions, BetterAuthOptions, BetterAuthPlugin, BetterAuthPluginErrorCodePart, BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, BetterAuthRateLimitOptions, BetterAuthRateLimitRule, BetterAuthRateLimitStorage, ClientAtomListener, ClientFetchOption, ClientStore, DynamicBaseURLConfig, GenerateIdFn, GenericEndpointContext, HookEndpointContext, InfoContext, InternalAdapter, LiteralString, LiteralUnion, PluginContext, Prettify, Primitive, SecretConfig, StandardSchemaV1$1 as StandardSchemaV1, StoreIdentifierOption, UnionToIntersection, UserProvisioningSource, ValidateUserInfoAction, ValidateUserInfoMethod, ValidateUserInfoOAuthInfo, ValidateUserInfoResult, ValidateUserInfoSSOInfo, ValidateUserInfoSource };
9
+ export type { AuthContext, Awaitable, AwaitableFunction, BaseURLConfig, BetterAuthAdvancedOptions, BetterAuthClientOptions, BetterAuthClientPlugin, BetterAuthCookie, BetterAuthCookies, BetterAuthDBOptions, BetterAuthOptions, BetterAuthPlugin, BetterAuthPluginErrorCodePart, BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, BetterAuthRateLimitOptions, BetterAuthRateLimitRule, BetterAuthRateLimitStorage, ClientAtomListener, ClientFetchOption, ClientStore, CookieCachePayload, CookieCacheSigner, DynamicBaseURLConfig, GenerateIdFn, GenericEndpointContext, HookEndpointContext, InfoContext, InternalAdapter, LiteralString, LiteralUnion, PluginContext, Prettify, Primitive, SecretConfig, StandardSchemaV1$1 as StandardSchemaV1, StoreIdentifierOption, UnionToIntersection, UserProvisioningSource, ValidateUserInfoAction, ValidateUserInfoMethod, ValidateUserInfoOAuthInfo, ValidateUserInfoResult, ValidateUserInfoSSOInfo, ValidateUserInfoSource };
@@ -978,20 +978,6 @@ type BetterAuthOptions = {
978
978
  * @default "compact"
979
979
  */
980
980
  strategy?: "compact" | "jwt" | "jwe";
981
- /**
982
- * JWT-specific configuration for `strategy: "jwt"`.
983
- */
984
- jwt?: {
985
- /**
986
- * Which signing key is used for cookie-cache JWTs.
987
- *
988
- * - `"secret"`: uses the Better Auth secret with HS256.
989
- * - `"jwt-plugin"`: uses the installed `jwt()` plugin's asymmetric signing keys.
990
- *
991
- * @default "secret"
992
- */
993
- signingKey?: "secret" | "jwt-plugin";
994
- };
995
981
  /**
996
982
  * Controls stateless cookie cache refresh behavior.
997
983
  *
@@ -0,0 +1,19 @@
1
+ //#region src/utils/email.d.ts
2
+ interface PlaceholderEmailOptions {
3
+ /**
4
+ * A stable identifier that distinguishes the account within the namespace.
5
+ */
6
+ identifier: string;
7
+ /**
8
+ * A namespace that distinguishes identical identifiers from different sources.
9
+ */
10
+ namespace: string;
11
+ }
12
+ /**
13
+ * Creates a stable, non-routable email address for an account without an email.
14
+ *
15
+ * @throws TypeError if the generated email is invalid.
16
+ */
17
+ declare function createPlaceholderEmail({ identifier, namespace }: PlaceholderEmailOptions): string;
18
+ //#endregion
19
+ export { PlaceholderEmailOptions, createPlaceholderEmail };
@@ -0,0 +1,18 @@
1
+ import * as z from "zod";
2
+ //#region src/utils/email.ts
3
+ /**
4
+ * @see https://www.rfc-editor.org/rfc/rfc6761.html#section-6.4
5
+ */
6
+ const PLACEHOLDER_EMAIL_DOMAIN = "placeholder.invalid";
7
+ /**
8
+ * Creates a stable, non-routable email address for an account without an email.
9
+ *
10
+ * @throws TypeError if the generated email is invalid.
11
+ */
12
+ function createPlaceholderEmail({ identifier, namespace }) {
13
+ const result = z.email().safeParse(`${identifier}@${namespace}.${PLACEHOLDER_EMAIL_DOMAIN}`);
14
+ if (!result.success) throw new TypeError("Invalid placeholder email");
15
+ return result.data;
16
+ }
17
+ //#endregion
18
+ export { createPlaceholderEmail };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/core",
3
- "version": "1.7.0-rc.3",
3
+ "version": "1.7.0-rc.4",
4
4
  "description": "The most comprehensive authentication framework for TypeScript.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -164,8 +164,8 @@
164
164
  "@better-fetch/fetch": "1.3.1",
165
165
  "@cloudflare/workers-types": "^4.20250121.0",
166
166
  "@opentelemetry/api": "^1.9.1",
167
- "@opentelemetry/sdk-trace-base": "^2.8.0",
168
- "@opentelemetry/sdk-trace-node": "^2.8.0",
167
+ "@opentelemetry/sdk-trace-base": "^2.10.0",
168
+ "@opentelemetry/sdk-trace-node": "^2.10.0",
169
169
  "better-call": "1.3.7",
170
170
  "jose": "^6.2.3",
171
171
  "kysely": "^0.28.17 || ^0.29.0",
@@ -253,7 +253,7 @@ const buildAuthTables = (options: BetterAuthOptions): BetterAuthDBSchema => {
253
253
  indexes: mergeTableIndexes(
254
254
  [
255
255
  {
256
- fields: ["issuer", "providerAccountId"],
256
+ fields: ["issuer", "accountId"],
257
257
  unique: true,
258
258
  },
259
259
  ],
@@ -265,11 +265,10 @@ const buildAuthTables = (options: BetterAuthOptions): BetterAuthDBSchema => {
265
265
  required: true,
266
266
  fieldName: options.account?.fields?.issuer || "issuer",
267
267
  },
268
- providerAccountId: {
268
+ accountId: {
269
269
  type: "string",
270
270
  required: true,
271
- fieldName:
272
- options.account?.fields?.providerAccountId || "providerAccountId",
271
+ fieldName: options.account?.fields?.accountId || "accountId",
273
272
  },
274
273
  providerId: {
275
274
  type: "string",
@@ -10,7 +10,7 @@ import { coreSchema } from "./shared";
10
10
  export const accountSchema = coreSchema.extend({
11
11
  providerId: z.string(),
12
12
  issuer: z.string(),
13
- providerAccountId: z.string(),
13
+ accountId: z.string(),
14
14
  userId: z.coerce.string(),
15
15
  accessToken: z.string().nullish(),
16
16
  refreshToken: z.string().nullish(),
@@ -39,9 +39,7 @@ export const accountSchema = coreSchema.extend({
39
39
  export type BaseAccount = z.infer<typeof accountSchema>;
40
40
 
41
41
  /** The stable provider-side key used to recognize an account. */
42
- export type AccountKey = Readonly<
43
- Pick<BaseAccount, "issuer" | "providerAccountId">
44
- >;
42
+ export type AccountKey = Readonly<Pick<BaseAccount, "issuer" | "accountId">>;
45
43
 
46
44
  function encodeAccountIssuerProviderId(providerId: string): string {
47
45
  return encodeURIComponent(providerId);
@@ -9,7 +9,7 @@ import { coreSchema } from "./shared";
9
9
  export const userSchema = coreSchema.extend({
10
10
  // TODO(#9124): widen to nullish in v2. OAuth providers (Discord phone-only,
11
11
  // Apple subsequent sign-ins, etc.) can legitimately omit email; identity
12
- // must key on (issuer, providerAccountId) per OpenID Connect Core §5.7.
12
+ // must key on (issuer, accountId) per OpenID Connect Core §5.7.
13
13
  email: z.string().transform((val) => val.toLowerCase()),
14
14
  emailVerified: z.boolean().default(false),
15
15
  name: z.string(),
@@ -89,6 +89,7 @@ export const apple = (options: AppleOptions) => {
89
89
  scopes,
90
90
  redirectURI,
91
91
  additionalParams,
92
+ codeVerifier,
92
93
  }) {
93
94
  if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
94
95
  logger.error(
@@ -106,6 +107,7 @@ export const apple = (options: AppleOptions) => {
106
107
  scopes: _scope,
107
108
  state,
108
109
  redirectURI,
110
+ codeVerifier,
109
111
  responseMode: "form_post",
110
112
  responseType: "code id_token",
111
113
  additionalParams,
@@ -12,7 +12,11 @@ import type { DBAdapter, Where } from "../db/adapter";
12
12
  import type { AccountKey } from "../db/schema/account";
13
13
  import type { createLogger } from "../env";
14
14
  import type { OAuthProvider } from "../oauth2";
15
- import type { BetterAuthCookie, BetterAuthCookies } from "./cookie";
15
+ import type {
16
+ BetterAuthCookie,
17
+ BetterAuthCookies,
18
+ CookieCachePayload,
19
+ } from "./cookie";
16
20
  import type { Awaitable, LiteralString } from "./helper";
17
21
  import type {
18
22
  BetterAuthOptions,
@@ -86,6 +90,24 @@ export type GenericEndpointContext<
86
90
  context: AuthContext<Options>;
87
91
  };
88
92
 
93
+ /**
94
+ * Signs and verifies session cookie-cache values.
95
+ */
96
+ export type CookieCacheSigner = {
97
+ sign: (
98
+ ctx: GenericEndpointContext,
99
+ payload: CookieCachePayload,
100
+ expiresIn: number,
101
+ ) => Promise<string>;
102
+ verify: (
103
+ ctx: GenericEndpointContext,
104
+ token: string,
105
+ ) => Promise<{
106
+ payload: CookieCachePayload;
107
+ expiresAt: number;
108
+ } | null>;
109
+ };
110
+
89
111
  export interface InternalAdapter<
90
112
  _Options extends BetterAuthOptions = BetterAuthOptions,
91
113
  > {
@@ -403,6 +425,7 @@ export type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> =
403
425
  updateAge: number;
404
426
  expiresIn: number;
405
427
  freshAge: number;
428
+ cookieCacheSigner?: CookieCacheSigner | undefined;
406
429
  cookieRefreshCache:
407
430
  | false
408
431
  | {
@@ -1,4 +1,5 @@
1
1
  import type { CookieOptions } from "better-call";
2
+ import type { Session, User } from "../db";
2
3
 
3
4
  export type BetterAuthCookie = { name: string; attributes: CookieOptions };
4
5
 
@@ -8,3 +9,13 @@ export type BetterAuthCookies = {
8
9
  accountData: BetterAuthCookie;
9
10
  dontRememberToken: BetterAuthCookie;
10
11
  };
12
+
13
+ /**
14
+ * A validated cookie-cache payload, including legacy payloads without a version.
15
+ */
16
+ export type CookieCachePayload = {
17
+ session: Session & Record<string, unknown>;
18
+ user: User & Record<string, unknown>;
19
+ updatedAt: number;
20
+ version?: string | undefined;
21
+ };
@@ -3,6 +3,7 @@ export type {
3
3
  AuthContext,
4
4
  BetterAuthPluginRegistry,
5
5
  BetterAuthPluginRegistryIdentifier,
6
+ CookieCacheSigner,
6
7
  GenericEndpointContext,
7
8
  InfoContext,
8
9
  InternalAdapter,
@@ -11,6 +12,7 @@ export type {
11
12
  export type {
12
13
  BetterAuthCookie,
13
14
  BetterAuthCookies,
15
+ CookieCachePayload,
14
16
  } from "./cookie";
15
17
  export type * from "./helper";
16
18
  export type {
@@ -1089,20 +1089,6 @@ export type BetterAuthOptions = {
1089
1089
  * @default "compact"
1090
1090
  */
1091
1091
  strategy?: "compact" | "jwt" | "jwe";
1092
- /**
1093
- * JWT-specific configuration for `strategy: "jwt"`.
1094
- */
1095
- jwt?: {
1096
- /**
1097
- * Which signing key is used for cookie-cache JWTs.
1098
- *
1099
- * - `"secret"`: uses the Better Auth secret with HS256.
1100
- * - `"jwt-plugin"`: uses the installed `jwt()` plugin's asymmetric signing keys.
1101
- *
1102
- * @default "secret"
1103
- */
1104
- signingKey?: "secret" | "jwt-plugin";
1105
- };
1106
1092
  /**
1107
1093
  * Controls stateless cookie cache refresh behavior.
1108
1094
  *
@@ -0,0 +1,36 @@
1
+ import * as z from "zod";
2
+
3
+ /**
4
+ * @see https://www.rfc-editor.org/rfc/rfc6761.html#section-6.4
5
+ */
6
+ const PLACEHOLDER_EMAIL_DOMAIN = "placeholder.invalid";
7
+
8
+ export interface PlaceholderEmailOptions {
9
+ /**
10
+ * A stable identifier that distinguishes the account within the namespace.
11
+ */
12
+ identifier: string;
13
+ /**
14
+ * A namespace that distinguishes identical identifiers from different sources.
15
+ */
16
+ namespace: string;
17
+ }
18
+
19
+ /**
20
+ * Creates a stable, non-routable email address for an account without an email.
21
+ *
22
+ * @throws TypeError if the generated email is invalid.
23
+ */
24
+ export function createPlaceholderEmail({
25
+ identifier,
26
+ namespace,
27
+ }: PlaceholderEmailOptions): string {
28
+ const result = z
29
+ .email()
30
+ .safeParse(`${identifier}@${namespace}.${PLACEHOLDER_EMAIL_DOMAIN}`);
31
+ if (!result.success) {
32
+ throw new TypeError("Invalid placeholder email");
33
+ }
34
+
35
+ return result.data;
36
+ }