@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.
- package/dist/api/index.d.mts +2 -0
- package/dist/context/global.mjs +1 -1
- package/dist/db/get-tables.mjs +3 -3
- package/dist/db/schema/account.d.mts +2 -2
- package/dist/db/schema/account.mjs +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/instrumentation/tracer.mjs +1 -1
- package/dist/social-providers/apple.d.mts +1 -1
- package/dist/social-providers/apple.mjs +2 -1
- package/dist/social-providers/index.d.mts +1 -1
- package/dist/types/context.d.mts +13 -2
- package/dist/types/cookie.d.mts +13 -1
- package/dist/types/index.d.mts +3 -3
- package/dist/types/init-options.d.mts +0 -14
- package/dist/utils/email.d.mts +19 -0
- package/dist/utils/email.mjs +18 -0
- package/package.json +3 -3
- package/src/db/get-tables.ts +3 -4
- package/src/db/schema/account.ts +2 -4
- package/src/db/schema/user.ts +1 -1
- package/src/social-providers/apple.ts +2 -0
- package/src/types/context.ts +24 -1
- package/src/types/cookie.ts +11 -0
- package/src/types/index.ts +2 -0
- package/src/types/init-options.ts +0 -14
- package/src/utils/email.ts +36 -0
package/dist/api/index.d.mts
CHANGED
|
@@ -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;
|
package/dist/context/global.mjs
CHANGED
package/dist/db/get-tables.mjs
CHANGED
|
@@ -198,7 +198,7 @@ const buildAuthTables = (options) => {
|
|
|
198
198
|
account: {
|
|
199
199
|
modelName: options.account?.modelName || "account",
|
|
200
200
|
indexes: mergeTableIndexes([{
|
|
201
|
-
fields: ["issuer", "
|
|
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
|
-
|
|
210
|
+
accountId: {
|
|
211
211
|
type: "string",
|
|
212
212
|
required: true,
|
|
213
|
-
fieldName: options.account?.fields?.
|
|
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
|
-
|
|
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" | "
|
|
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
|
-
|
|
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.
|
|
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;
|
package/dist/types/context.d.mts
CHANGED
|
@@ -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 };
|
package/dist/types/cookie.d.mts
CHANGED
|
@@ -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 };
|
package/dist/types/index.d.mts
CHANGED
|
@@ -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
|
+
"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.
|
|
168
|
-
"@opentelemetry/sdk-trace-node": "^2.
|
|
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",
|
package/src/db/get-tables.ts
CHANGED
|
@@ -253,7 +253,7 @@ const buildAuthTables = (options: BetterAuthOptions): BetterAuthDBSchema => {
|
|
|
253
253
|
indexes: mergeTableIndexes(
|
|
254
254
|
[
|
|
255
255
|
{
|
|
256
|
-
fields: ["issuer", "
|
|
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
|
-
|
|
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",
|
package/src/db/schema/account.ts
CHANGED
|
@@ -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
|
-
|
|
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);
|
package/src/db/schema/user.ts
CHANGED
|
@@ -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,
|
|
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,
|
package/src/types/context.ts
CHANGED
|
@@ -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 {
|
|
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
|
| {
|
package/src/types/cookie.ts
CHANGED
|
@@ -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
|
+
};
|
package/src/types/index.ts
CHANGED
|
@@ -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
|
+
}
|