@better-auth/core 1.7.0-beta.1 → 1.7.0-beta.3

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.
Files changed (63) hide show
  1. package/dist/api/index.mjs +29 -3
  2. package/dist/context/global.mjs +1 -1
  3. package/dist/db/adapter/factory.mjs +2 -3
  4. package/dist/db/adapter/get-id-field.mjs +1 -1
  5. package/dist/instrumentation/api.mjs +12 -0
  6. package/dist/instrumentation/noop.mjs +42 -0
  7. package/dist/instrumentation/pure.index.d.mts +7 -0
  8. package/dist/instrumentation/pure.index.mjs +7 -0
  9. package/dist/instrumentation/tracer.mjs +6 -3
  10. package/dist/oauth2/index.d.mts +2 -2
  11. package/dist/oauth2/index.mjs +2 -2
  12. package/dist/oauth2/utils.d.mts +10 -1
  13. package/dist/oauth2/utils.mjs +13 -1
  14. package/dist/social-providers/apple.d.mts +19 -3
  15. package/dist/social-providers/apple.mjs +7 -1
  16. package/dist/social-providers/atlassian.mjs +1 -1
  17. package/dist/social-providers/cognito.d.mts +1 -1
  18. package/dist/social-providers/cognito.mjs +3 -2
  19. package/dist/social-providers/discord.d.mts +1 -1
  20. package/dist/social-providers/facebook.d.mts +3 -3
  21. package/dist/social-providers/facebook.mjs +8 -1
  22. package/dist/social-providers/figma.mjs +1 -1
  23. package/dist/social-providers/github.d.mts +1 -1
  24. package/dist/social-providers/google.d.mts +1 -1
  25. package/dist/social-providers/google.mjs +3 -2
  26. package/dist/social-providers/linkedin.d.mts +2 -2
  27. package/dist/social-providers/linkedin.mjs +1 -1
  28. package/dist/social-providers/microsoft-entra-id.d.mts +3 -3
  29. package/dist/social-providers/microsoft-entra-id.mjs +6 -1
  30. package/dist/social-providers/paybin.mjs +1 -1
  31. package/dist/social-providers/paypal.mjs +1 -1
  32. package/dist/social-providers/salesforce.mjs +1 -1
  33. package/dist/types/context.d.mts +7 -1
  34. package/dist/utils/async.d.mts +22 -0
  35. package/dist/utils/async.mjs +32 -0
  36. package/dist/utils/host.d.mts +147 -0
  37. package/dist/utils/host.mjs +291 -0
  38. package/dist/utils/is-api-error.d.mts +6 -0
  39. package/dist/utils/is-api-error.mjs +8 -0
  40. package/package.json +10 -1
  41. package/src/api/index.ts +39 -5
  42. package/src/db/adapter/factory.ts +3 -3
  43. package/src/db/adapter/get-id-field.ts +2 -2
  44. package/src/db/get-tables.ts +2 -0
  45. package/src/db/schema/user.ts +3 -0
  46. package/src/instrumentation/api.ts +17 -0
  47. package/src/instrumentation/noop.ts +74 -0
  48. package/src/instrumentation/pure.index.ts +31 -0
  49. package/src/instrumentation/tracer.ts +8 -3
  50. package/src/oauth2/index.ts +5 -1
  51. package/src/oauth2/utils.ts +13 -0
  52. package/src/social-providers/apple.ts +11 -3
  53. package/src/social-providers/cognito.ts +3 -2
  54. package/src/social-providers/discord.ts +1 -1
  55. package/src/social-providers/facebook.ts +13 -4
  56. package/src/social-providers/github.ts +1 -1
  57. package/src/social-providers/google.ts +3 -2
  58. package/src/social-providers/linkedin.ts +3 -3
  59. package/src/social-providers/microsoft-entra-id.ts +14 -4
  60. package/src/types/context.ts +8 -1
  61. package/src/utils/async.ts +53 -0
  62. package/src/utils/host.ts +401 -0
  63. package/src/utils/is-api-error.ts +10 -0
@@ -1,6 +1,23 @@
1
1
  import { runWithEndpointContext } from "../context/endpoint-context.mjs";
2
- import { createEndpoint, createMiddleware } from "better-call";
2
+ import { isAPIError } from "../utils/is-api-error.mjs";
3
+ import { createEndpoint, createMiddleware, kAPIErrorHeaderSymbol } from "better-call";
3
4
  //#region src/api/index.ts
5
+ /**
6
+ * Better-call's createEndpoint re-throws APIError without exposing the headers
7
+ * accumulated on ctx.responseHeaders (e.g. Set-Cookie from deleteSessionCookie
8
+ * before throw). Attach them to the error via kAPIErrorHeaderSymbol — matching
9
+ * better-call's createMiddleware contract so the outer pipeline can merge them
10
+ * into the response.
11
+ */
12
+ function attachResponseHeadersToAPIError(responseHeaders, e) {
13
+ if (!isAPIError(e) || !responseHeaders) return;
14
+ Object.defineProperty(e, kAPIErrorHeaderSymbol, {
15
+ enumerable: false,
16
+ configurable: true,
17
+ value: responseHeaders,
18
+ writable: false
19
+ });
20
+ }
4
21
  const optionsMiddleware = createMiddleware(async () => {
5
22
  /**
6
23
  * This will be passed on the instance of
@@ -17,14 +34,23 @@ function createAuthEndpoint(pathOrOptions, handlerOrOptions, handlerOrNever) {
17
34
  const path = typeof pathOrOptions === "string" ? pathOrOptions : void 0;
18
35
  const options = typeof handlerOrOptions === "object" ? handlerOrOptions : pathOrOptions;
19
36
  const handler = typeof handlerOrOptions === "function" ? handlerOrOptions : handlerOrNever;
37
+ const wrapped = async (ctx) => {
38
+ const runtimeCtx = ctx;
39
+ try {
40
+ return await runWithEndpointContext(ctx, () => handler(ctx));
41
+ } catch (e) {
42
+ attachResponseHeadersToAPIError(runtimeCtx.responseHeaders, e);
43
+ throw e;
44
+ }
45
+ };
20
46
  if (path) return createEndpoint(path, {
21
47
  ...options,
22
48
  use: [...options?.use || [], ...use]
23
- }, async (ctx) => runWithEndpointContext(ctx, () => handler(ctx)));
49
+ }, wrapped);
24
50
  return createEndpoint({
25
51
  ...options,
26
52
  use: [...options?.use || [], ...use]
27
- }, async (ctx) => runWithEndpointContext(ctx, () => handler(ctx)));
53
+ }, wrapped);
28
54
  }
29
55
  //#endregion
30
56
  export { createAuthEndpoint, createAuthMiddleware, optionsMiddleware };
@@ -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-beta.1";
5
+ const __betterAuthVersion = "1.7.0-beta.3";
6
6
  /**
7
7
  * We store context instance in the globalThis.
8
8
  *
@@ -1,9 +1,7 @@
1
+ import { BetterAuthError } from "../../error/index.mjs";
1
2
  import { getAuthTables } from "../get-tables.mjs";
2
3
  import { getColorDepth } from "../../env/color-depth.mjs";
3
4
  import { TTY_COLORS, createLogger } from "../../env/logger.mjs";
4
- import { BetterAuthError } from "../../error/index.mjs";
5
- import { ATTR_DB_COLLECTION_NAME, ATTR_DB_OPERATION_NAME } from "../../instrumentation/attributes.mjs";
6
- import { withSpan } from "../../instrumentation/tracer.mjs";
7
5
  import { safeJSONParse } from "../../utils/json.mjs";
8
6
  import { initGetDefaultModelName } from "./get-default-model-name.mjs";
9
7
  import { initGetDefaultFieldName } from "./get-default-field-name.mjs";
@@ -12,6 +10,7 @@ import { initGetFieldAttributes } from "./get-field-attributes.mjs";
12
10
  import { initGetFieldName } from "./get-field-name.mjs";
13
11
  import { initGetModelName } from "./get-model-name.mjs";
14
12
  import { withApplyDefault } from "./utils.mjs";
13
+ import { ATTR_DB_COLLECTION_NAME, ATTR_DB_OPERATION_NAME, withSpan } from "@better-auth/core/instrumentation";
15
14
  //#region src/db/adapter/factory.ts
16
15
  let debugLogs = [];
17
16
  let transactionId = -1;
@@ -40,12 +40,12 @@ const initGetIdField = ({ usePlural, schema, disableIdGeneration, options, custo
40
40
  if (useUUIDs) {
41
41
  if (shouldGenerateId && !forceAllowId) return value;
42
42
  if (disableIdGeneration) return void 0;
43
- if (supportsUUIDs) return void 0;
44
43
  if (forceAllowId && typeof value === "string") if (/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)) return value;
45
44
  else {
46
45
  const stack = (/* @__PURE__ */ new Error()).stack?.split("\n").filter((_, i) => i !== 1).join("\n").replace("Error:", "");
47
46
  logger.warn("[Adapter Factory] - Invalid UUID value for field `id` provided when `forceAllowId` is true. Generating a new UUID.", stack);
48
47
  }
48
+ if (supportsUUIDs) return void 0;
49
49
  if (typeof value !== "string" && !supportsUUIDs) return crypto.randomUUID();
50
50
  return;
51
51
  }
@@ -0,0 +1,12 @@
1
+ import { noopOpenTelemetryAPI } from "./noop.mjs";
2
+ //#region src/instrumentation/api.ts
3
+ let openTelemetryAPIPromise;
4
+ let openTelemetryAPI;
5
+ function getOpenTelemetryAPI() {
6
+ if (!openTelemetryAPIPromise) openTelemetryAPIPromise = import("@opentelemetry/api").then((mod) => {
7
+ openTelemetryAPI = mod;
8
+ }).catch(() => void 0);
9
+ return openTelemetryAPI ?? noopOpenTelemetryAPI;
10
+ }
11
+ //#endregion
12
+ export { getOpenTelemetryAPI };
@@ -0,0 +1,42 @@
1
+ //#region src/instrumentation/noop.ts
2
+ function createNoopSpan() {
3
+ const span = {
4
+ end() {},
5
+ setAttribute(_key, _value) {},
6
+ setStatus(_status) {},
7
+ recordException(_exception) {},
8
+ updateName(_name) {
9
+ return span;
10
+ }
11
+ };
12
+ return span;
13
+ }
14
+ function createNoopTracer(noopSpan) {
15
+ function startActiveSpan(_name, ...rest) {
16
+ const fn = rest[rest.length - 1];
17
+ return fn(noopSpan);
18
+ }
19
+ return { startActiveSpan };
20
+ }
21
+ function createNoopTraceAPI() {
22
+ const noopTracer = createNoopTracer(createNoopSpan());
23
+ return {
24
+ getTracer(_name, _version) {
25
+ return noopTracer;
26
+ },
27
+ getActiveSpan() {}
28
+ };
29
+ }
30
+ function createNoopOpenTelemetryAPI() {
31
+ return {
32
+ SpanStatusCode: {
33
+ UNSET: 0,
34
+ OK: 1,
35
+ ERROR: 2
36
+ },
37
+ trace: createNoopTraceAPI()
38
+ };
39
+ }
40
+ const noopOpenTelemetryAPI = createNoopOpenTelemetryAPI();
41
+ //#endregion
42
+ export { noopOpenTelemetryAPI };
@@ -0,0 +1,7 @@
1
+ import { ATTR_CONTEXT, ATTR_DB_COLLECTION_NAME, ATTR_DB_OPERATION_NAME, ATTR_HOOK_TYPE, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, ATTR_OPERATION_ID } from "./attributes.mjs";
2
+
3
+ //#region src/instrumentation/pure.index.d.ts
4
+ declare function withSpan<T>(name: string, attributes: Record<string, string | number | boolean>, fn: () => T): T;
5
+ declare function withSpan<T>(name: string, attributes: Record<string, string | number | boolean>, fn: () => Promise<T>): Promise<T>;
6
+ //#endregion
7
+ export { ATTR_CONTEXT, ATTR_DB_COLLECTION_NAME, ATTR_DB_OPERATION_NAME, ATTR_HOOK_TYPE, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, ATTR_OPERATION_ID, withSpan };
@@ -0,0 +1,7 @@
1
+ import { ATTR_CONTEXT, ATTR_DB_COLLECTION_NAME, ATTR_DB_OPERATION_NAME, ATTR_HOOK_TYPE, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, ATTR_OPERATION_ID } from "./attributes.mjs";
2
+ //#region src/instrumentation/pure.index.ts
3
+ function withSpan(_name, _attributes, fn) {
4
+ return fn();
5
+ }
6
+ //#endregion
7
+ export { ATTR_CONTEXT, ATTR_DB_COLLECTION_NAME, ATTR_DB_OPERATION_NAME, ATTR_HOOK_TYPE, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, ATTR_OPERATION_ID, withSpan };
@@ -1,7 +1,8 @@
1
1
  import { ATTR_HTTP_RESPONSE_STATUS_CODE } from "./attributes.mjs";
2
- import { SpanStatusCode, trace } from "@opentelemetry/api";
2
+ import { getOpenTelemetryAPI } from "./api.mjs";
3
3
  //#region src/instrumentation/tracer.ts
4
- const tracer = trace.getTracer("better-auth", "1.7.0-beta.1");
4
+ const INSTRUMENTATION_SCOPE = "better-auth";
5
+ const INSTRUMENTATION_VERSION = "1.7.0-beta.3";
5
6
  /**
6
7
  * Better-auth uses `throw ctx.redirect(url)` for flow control (e.g. OAuth
7
8
  * callbacks). These are APIErrors with 3xx status codes and should not be
@@ -15,6 +16,7 @@ function isRedirectError(err) {
15
16
  return false;
16
17
  }
17
18
  function endSpanWithError(span, err) {
19
+ const { SpanStatusCode } = getOpenTelemetryAPI();
18
20
  if (isRedirectError(err)) {
19
21
  span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, err.statusCode);
20
22
  span.setStatus({ code: SpanStatusCode.OK });
@@ -28,7 +30,8 @@ function endSpanWithError(span, err) {
28
30
  span.end();
29
31
  }
30
32
  function withSpan(name, attributes, fn) {
31
- return tracer.startActiveSpan(name, { attributes }, (span) => {
33
+ const { trace } = getOpenTelemetryAPI();
34
+ return trace.getTracer(INSTRUMENTATION_SCOPE, INSTRUMENTATION_VERSION).startActiveSpan(name, { attributes }, (span) => {
32
35
  try {
33
36
  const result = fn();
34
37
  if (result instanceof Promise) return result.then((value) => {
@@ -3,7 +3,7 @@ import { OAuth2Tokens, OAuth2UserInfo, OAuthProvider, ProviderOptions } from "./
3
3
  import { clientCredentialsToken, clientCredentialsTokenRequest, createClientCredentialsTokenRequest } from "./client-credentials-token.mjs";
4
4
  import { createAuthorizationURL } from "./create-authorization-url.mjs";
5
5
  import { createRefreshAccessTokenRequest, refreshAccessToken, refreshAccessTokenRequest } from "./refresh-access-token.mjs";
6
- import { generateCodeChallenge, getOAuth2Tokens } from "./utils.mjs";
6
+ import { generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId } from "./utils.mjs";
7
7
  import { authorizationCodeRequest, createAuthorizationCodeRequest, validateAuthorizationCode, validateToken } from "./validate-authorization-code.mjs";
8
8
  import { getJwks, verifyAccessToken, verifyJwsAccessToken } from "./verify.mjs";
9
- export { ASSERTION_SIGNING_ALGORITHMS, type AssertionSigningAlgorithm, CLIENT_ASSERTION_TYPE, type ClientAssertionConfig, type OAuth2Tokens, type OAuth2UserInfo, type OAuthProvider, type ProviderOptions, authorizationCodeRequest, clientCredentialsToken, clientCredentialsTokenRequest, createAuthorizationCodeRequest, createAuthorizationURL, createClientCredentialsTokenRequest, createRefreshAccessTokenRequest, generateCodeChallenge, getJwks, getOAuth2Tokens, refreshAccessToken, refreshAccessTokenRequest, resolveAssertionParams, signClientAssertion, validateAuthorizationCode, validateToken, verifyAccessToken, verifyJwsAccessToken };
9
+ export { ASSERTION_SIGNING_ALGORITHMS, type AssertionSigningAlgorithm, CLIENT_ASSERTION_TYPE, type ClientAssertionConfig, type OAuth2Tokens, type OAuth2UserInfo, type OAuthProvider, type ProviderOptions, authorizationCodeRequest, clientCredentialsToken, clientCredentialsTokenRequest, createAuthorizationCodeRequest, createAuthorizationURL, createClientCredentialsTokenRequest, createRefreshAccessTokenRequest, generateCodeChallenge, getJwks, getOAuth2Tokens, getPrimaryClientId, refreshAccessToken, refreshAccessTokenRequest, resolveAssertionParams, signClientAssertion, validateAuthorizationCode, validateToken, verifyAccessToken, verifyJwsAccessToken };
@@ -1,8 +1,8 @@
1
1
  import { ASSERTION_SIGNING_ALGORITHMS, CLIENT_ASSERTION_TYPE, resolveAssertionParams, signClientAssertion } from "./client-assertion.mjs";
2
2
  import { clientCredentialsToken, clientCredentialsTokenRequest, createClientCredentialsTokenRequest } from "./client-credentials-token.mjs";
3
- import { generateCodeChallenge, getOAuth2Tokens } from "./utils.mjs";
3
+ import { generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId } from "./utils.mjs";
4
4
  import { createAuthorizationURL } from "./create-authorization-url.mjs";
5
5
  import { createRefreshAccessTokenRequest, refreshAccessToken, refreshAccessTokenRequest } from "./refresh-access-token.mjs";
6
6
  import { authorizationCodeRequest, createAuthorizationCodeRequest, validateAuthorizationCode, validateToken } from "./validate-authorization-code.mjs";
7
7
  import { getJwks, verifyAccessToken, verifyJwsAccessToken } from "./verify.mjs";
8
- export { ASSERTION_SIGNING_ALGORITHMS, CLIENT_ASSERTION_TYPE, authorizationCodeRequest, clientCredentialsToken, clientCredentialsTokenRequest, createAuthorizationCodeRequest, createAuthorizationURL, createClientCredentialsTokenRequest, createRefreshAccessTokenRequest, generateCodeChallenge, getJwks, getOAuth2Tokens, refreshAccessToken, refreshAccessTokenRequest, resolveAssertionParams, signClientAssertion, validateAuthorizationCode, validateToken, verifyAccessToken, verifyJwsAccessToken };
8
+ export { ASSERTION_SIGNING_ALGORITHMS, CLIENT_ASSERTION_TYPE, authorizationCodeRequest, clientCredentialsToken, clientCredentialsTokenRequest, createAuthorizationCodeRequest, createAuthorizationURL, createClientCredentialsTokenRequest, createRefreshAccessTokenRequest, generateCodeChallenge, getJwks, getOAuth2Tokens, getPrimaryClientId, refreshAccessToken, refreshAccessTokenRequest, resolveAssertionParams, signClientAssertion, validateAuthorizationCode, validateToken, verifyAccessToken, verifyJwsAccessToken };
@@ -2,6 +2,15 @@ import { OAuth2Tokens } from "./oauth-provider.mjs";
2
2
 
3
3
  //#region src/oauth2/utils.d.ts
4
4
  declare function getOAuth2Tokens(data: Record<string, any>): OAuth2Tokens;
5
+ /**
6
+ * Return the provider's primary Client ID: the single string, or the entry at
7
+ * array index 0 for the cross-platform form used by ID token audience
8
+ * verification. Index 0 is the designated primary and pairs with
9
+ * `clientSecret` for the authorization code flow; later array entries are
10
+ * only used as additional accepted audiences. Returns `undefined` when the
11
+ * primary value is missing or an empty string.
12
+ */
13
+ declare function getPrimaryClientId(clientId: unknown): string | undefined;
5
14
  declare function generateCodeChallenge(codeVerifier: string): Promise<string>;
6
15
  //#endregion
7
- export { generateCodeChallenge, getOAuth2Tokens };
16
+ export { generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId };
@@ -16,10 +16,22 @@ function getOAuth2Tokens(data) {
16
16
  raw: data
17
17
  };
18
18
  }
19
+ /**
20
+ * Return the provider's primary Client ID: the single string, or the entry at
21
+ * array index 0 for the cross-platform form used by ID token audience
22
+ * verification. Index 0 is the designated primary and pairs with
23
+ * `clientSecret` for the authorization code flow; later array entries are
24
+ * only used as additional accepted audiences. Returns `undefined` when the
25
+ * primary value is missing or an empty string.
26
+ */
27
+ function getPrimaryClientId(clientId) {
28
+ const value = Array.isArray(clientId) ? clientId[0] : clientId;
29
+ return typeof value === "string" && value.length > 0 ? value : void 0;
30
+ }
19
31
  async function generateCodeChallenge(codeVerifier) {
20
32
  const data = new TextEncoder().encode(codeVerifier);
21
33
  const hash = await crypto.subtle.digest("SHA-256", data);
22
34
  return base64Url.encode(new Uint8Array(hash), { padding: false });
23
35
  }
24
36
  //#endregion
25
- export { generateCodeChallenge, getOAuth2Tokens };
37
+ export { generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId };
@@ -12,7 +12,7 @@ interface AppleProfile {
12
12
  * The email address is either the user's real email address or the proxy
13
13
  * address, depending on their status private email relay service.
14
14
  */
15
- email: string;
15
+ email?: string;
16
16
  /**
17
17
  * A string or Boolean value that indicates whether the service verifies
18
18
  * the email. The value can either be a string ("true" or "false") or a
@@ -60,7 +60,7 @@ interface AppleNonConformUser {
60
60
  email: string;
61
61
  }
62
62
  interface AppleOptions extends ProviderOptions<AppleProfile> {
63
- clientId: string;
63
+ clientId: string | string[];
64
64
  appBundleIdentifier?: string | undefined;
65
65
  audience?: (string | string[]) | undefined;
66
66
  }
@@ -93,7 +93,23 @@ declare const apple: (options: AppleOptions) => {
93
93
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
94
94
  getUserInfo(token: OAuth2Tokens & {
95
95
  user?: {
96
- name?: {
96
+ name /**
97
+ * An Integer value that indicates whether the user appears to be a real
98
+ * person. Use the value of this claim to mitigate fraud. The possible
99
+ * values are: 0 (or Unsupported), 1 (or Unknown), 2 (or LikelyReal). For
100
+ * more information, see ASUserDetectionStatus. This claim is present only
101
+ * in iOS 14 and later, macOS 11 and later, watchOS 7 and later, tvOS 14
102
+ * and later. The claim isn’t present or supported for web-based apps.
103
+ */?
104
+ /**
105
+ * An Integer value that indicates whether the user appears to be a real
106
+ * person. Use the value of this claim to mitigate fraud. The possible
107
+ * values are: 0 (or Unsupported), 1 (or Unknown), 2 (or LikelyReal). For
108
+ * more information, see ASUserDetectionStatus. This claim is present only
109
+ * in iOS 14 and later, macOS 11 and later, watchOS 7 and later, tvOS 14
110
+ * and later. The claim isn’t present or supported for web-based apps.
111
+ */
112
+ : {
97
113
  firstName?: string;
98
114
  lastName?: string;
99
115
  };
@@ -1,4 +1,6 @@
1
- import { APIError } from "../error/index.mjs";
1
+ import { APIError, BetterAuthError } from "../error/index.mjs";
2
+ import { logger } from "../env/logger.mjs";
3
+ import { getPrimaryClientId } from "../oauth2/utils.mjs";
2
4
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
3
5
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
4
6
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
@@ -11,6 +13,10 @@ const apple = (options) => {
11
13
  id: "apple",
12
14
  name: "Apple",
13
15
  async createAuthorizationURL({ state, scopes, redirectURI }) {
16
+ if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
17
+ logger.error("Client ID and client secret are required for Apple. Make sure to provide them in the options.");
18
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
19
+ }
14
20
  const _scope = options.disableDefaultScope ? [] : ["email", "name"];
15
21
  if (options.scope) _scope.push(...options.scope);
16
22
  if (scopes) _scope.push(...scopes);
@@ -1,5 +1,5 @@
1
- import { logger } from "../env/logger.mjs";
2
1
  import { BetterAuthError } from "../error/index.mjs";
2
+ import { logger } from "../env/logger.mjs";
3
3
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
4
4
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
5
5
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
@@ -19,7 +19,7 @@ interface CognitoProfile {
19
19
  [key: string]: any;
20
20
  }
21
21
  interface CognitoOptions extends ProviderOptions<CognitoProfile> {
22
- clientId: string;
22
+ clientId: string | string[];
23
23
  /**
24
24
  * The Cognito domain (e.g., "your-app.auth.us-east-1.amazoncognito.com")
25
25
  */
@@ -1,5 +1,6 @@
1
- import { logger } from "../env/logger.mjs";
2
1
  import { APIError, BetterAuthError } from "../error/index.mjs";
2
+ import { logger } from "../env/logger.mjs";
3
+ import { getPrimaryClientId } from "../oauth2/utils.mjs";
3
4
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
4
5
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
5
6
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
@@ -19,7 +20,7 @@ const cognito = (options) => {
19
20
  id: "cognito",
20
21
  name: "Cognito",
21
22
  async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
22
- if (!options.clientId) {
23
+ if (!getPrimaryClientId(options.clientId)) {
23
24
  logger.error("ClientId is required for Amazon Cognito. Make sure to provide them in the options.");
24
25
  throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
25
26
  }
@@ -38,7 +38,7 @@ interface DiscordProfile extends Record<string, any> {
38
38
  /** whether the email on this account has been verified */
39
39
  verified: boolean;
40
40
  /** the user's email */
41
- email: string;
41
+ email?: string | null;
42
42
  /**
43
43
  * the flags on a user's account:
44
44
  * https://discord.com/developers/docs/resources/user#user-object-user-flags
@@ -3,8 +3,8 @@ import { OAuth2Tokens, ProviderOptions } from "../oauth2/oauth-provider.mjs";
3
3
  interface FacebookProfile {
4
4
  id: string;
5
5
  name: string;
6
- email: string;
7
- email_verified: boolean;
6
+ email?: string;
7
+ email_verified?: boolean;
8
8
  picture: {
9
9
  data: {
10
10
  height: number;
@@ -15,7 +15,7 @@ interface FacebookProfile {
15
15
  };
16
16
  }
17
17
  interface FacebookOptions extends ProviderOptions<FacebookProfile> {
18
- clientId: string;
18
+ clientId: string | string[];
19
19
  /**
20
20
  * Extend list of fields to retrieve from the Facebook user profile.
21
21
  *
@@ -1,3 +1,6 @@
1
+ import { BetterAuthError } from "../error/index.mjs";
2
+ import { logger } from "../env/logger.mjs";
3
+ import { getPrimaryClientId } from "../oauth2/utils.mjs";
1
4
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
2
5
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
3
6
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
@@ -9,6 +12,10 @@ const facebook = (options) => {
9
12
  id: "facebook",
10
13
  name: "Facebook",
11
14
  async createAuthorizationURL({ state, scopes, redirectURI, loginHint }) {
15
+ if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
16
+ logger.error("Client ID and client secret are required for Facebook. Make sure to provide them in the options.");
17
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
18
+ }
12
19
  const _scopes = options.disableDefaultScope ? [] : ["email", "public_profile"];
13
20
  if (options.scope) _scopes.push(...options.scope);
14
21
  if (scopes) _scopes.push(...scopes);
@@ -104,7 +111,7 @@ const facebook = (options) => {
104
111
  name: profile.name,
105
112
  email: profile.email,
106
113
  image: profile.picture.data.url,
107
- emailVerified: profile.email_verified,
114
+ emailVerified: profile.email_verified ?? false,
108
115
  ...userMap
109
116
  },
110
117
  data: profile
@@ -1,5 +1,5 @@
1
- import { logger } from "../env/logger.mjs";
2
1
  import { BetterAuthError } from "../error/index.mjs";
2
+ import { logger } from "../env/logger.mjs";
3
3
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
4
4
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
5
5
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
@@ -23,7 +23,7 @@ interface GithubProfile {
23
23
  company: string;
24
24
  blog: string;
25
25
  location: string;
26
- email: string;
26
+ email: string | null;
27
27
  hireable: boolean;
28
28
  bio: string;
29
29
  twitter_username: string;
@@ -27,7 +27,7 @@ interface GoogleProfile {
27
27
  sub: string;
28
28
  }
29
29
  interface GoogleOptions extends ProviderOptions<GoogleProfile> {
30
- clientId: string;
30
+ clientId: string | string[];
31
31
  /**
32
32
  * The access type to use for the authorization code request
33
33
  */
@@ -1,5 +1,6 @@
1
- import { logger } from "../env/logger.mjs";
2
1
  import { APIError, BetterAuthError } from "../error/index.mjs";
2
+ import { logger } from "../env/logger.mjs";
3
+ import { getPrimaryClientId } from "../oauth2/utils.mjs";
3
4
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
4
5
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
5
6
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
@@ -11,7 +12,7 @@ const google = (options) => {
11
12
  id: "google",
12
13
  name: "Google",
13
14
  async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint, display }) {
14
- if (!options.clientId || !options.clientSecret) {
15
+ if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
15
16
  logger.error("Client Id and Client Secret is required for Google. Make sure to provide them in the options.");
16
17
  throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
17
18
  }
@@ -10,8 +10,8 @@ interface LinkedInProfile {
10
10
  country: string;
11
11
  language: string;
12
12
  };
13
- email: string;
14
- email_verified: boolean;
13
+ email?: string;
14
+ email_verified?: boolean;
15
15
  }
16
16
  interface LinkedInOptions extends ProviderOptions<LinkedInProfile> {
17
17
  clientId: string;
@@ -59,7 +59,7 @@ const linkedin = (options) => {
59
59
  id: profile.sub,
60
60
  name: profile.name,
61
61
  email: profile.email,
62
- emailVerified: profile.email_verified || false,
62
+ emailVerified: profile.email_verified ?? false,
63
63
  image: profile.picture,
64
64
  ...userMap
65
65
  },
@@ -25,7 +25,7 @@ interface MicrosoftEntraIDProfile extends Record<string, any> {
25
25
  /** The primary username that represents the user */
26
26
  preferred_username: string;
27
27
  /** User's email address */
28
- email: string;
28
+ email?: string;
29
29
  /** Human-readable value that identifies the subject of the token */
30
30
  name: string;
31
31
  /** Matches the parameter included in the original authorize request */
@@ -104,7 +104,7 @@ interface MicrosoftEntraIDProfile extends Record<string, any> {
104
104
  given_name: string;
105
105
  }
106
106
  interface MicrosoftOptions extends ProviderOptions<MicrosoftEntraIDProfile> {
107
- clientId: string;
107
+ clientId: string | string[];
108
108
  /**
109
109
  * The tenant ID of the Microsoft account
110
110
  * @default "common"
@@ -151,7 +151,7 @@ declare const microsoft: (options: MicrosoftOptions) => {
151
151
  user?: {
152
152
  name?: {
153
153
  firstName?: string;
154
- lastName?: string;
154
+ lastName? /** The primary username that represents the user */: string;
155
155
  };
156
156
  email?: string;
157
157
  } | undefined;
@@ -1,5 +1,6 @@
1
+ import { APIError, BetterAuthError } from "../error/index.mjs";
1
2
  import { logger } from "../env/logger.mjs";
2
- import { APIError } from "../error/index.mjs";
3
+ import { getPrimaryClientId } from "../oauth2/utils.mjs";
3
4
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
4
5
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
5
6
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
@@ -16,6 +17,10 @@ const microsoft = (options) => {
16
17
  id: "microsoft",
17
18
  name: "Microsoft EntraID",
18
19
  createAuthorizationURL(data) {
20
+ if (!getPrimaryClientId(options.clientId)) {
21
+ logger.error("Client Id is required for Microsoft Entra ID. Make sure to provide it in the options.");
22
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
23
+ }
19
24
  const scopes = options.disableDefaultScope ? [] : [
20
25
  "openid",
21
26
  "profile",
@@ -1,5 +1,5 @@
1
- import { logger } from "../env/logger.mjs";
2
1
  import { BetterAuthError } from "../error/index.mjs";
2
+ import { logger } from "../env/logger.mjs";
3
3
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
4
4
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
5
5
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
@@ -1,5 +1,5 @@
1
- import { logger } from "../env/logger.mjs";
2
1
  import { BetterAuthError } from "../error/index.mjs";
2
+ import { logger } from "../env/logger.mjs";
3
3
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
4
4
  import { decodeJwt } from "jose";
5
5
  import { base64 } from "@better-auth/utils/base64";
@@ -1,5 +1,5 @@
1
- import { logger } from "../env/logger.mjs";
2
1
  import { BetterAuthError } from "../error/index.mjs";
2
+ import { logger } from "../env/logger.mjs";
3
3
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
4
4
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
5
5
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
@@ -83,7 +83,12 @@ interface InternalAdapter<_Options extends BetterAuthOptions = BetterAuthOptions
83
83
  updateSession(sessionToken: string, session: Partial<Session> & Record<string, any>): Promise<Session | null>;
84
84
  deleteSession(token: string): Promise<void>;
85
85
  deleteAccounts(userId: string): Promise<void>;
86
- deleteAccount(accountId: string): Promise<void>;
86
+ /**
87
+ * Delete an account by its primary key.
88
+ *
89
+ * @param id - The account row's primary key (the `id` column, not the `accountId` column).
90
+ */
91
+ deleteAccount(id: string): Promise<void>;
87
92
  deleteSessions(userIdOrSessionTokens: string | string[]): Promise<void>;
88
93
  findOAuthUser(email: string, accountId: string, providerId: string): Promise<{
89
94
  user: User;
@@ -110,6 +115,7 @@ interface InternalAdapter<_Options extends BetterAuthOptions = BetterAuthOptions
110
115
  findVerificationValue(identifier: string): Promise<Verification | null>;
111
116
  deleteVerificationByIdentifier(identifier: string): Promise<void>;
112
117
  updateVerificationByIdentifier(identifier: string, data: Partial<Verification>): Promise<Verification>;
118
+ refreshUserSessions(user: User): Promise<void>;
113
119
  }
114
120
  type CreateCookieGetterFn = (cookieName: string, overrideAttributes?: Partial<CookieOptions> | undefined) => BetterAuthCookie;
115
121
  type CheckPasswordFn<Options extends BetterAuthOptions = BetterAuthOptions> = (userId: string, ctx: GenericEndpointContext<Options>) => Promise<boolean>;
@@ -0,0 +1,22 @@
1
+ import { Awaitable } from "../types/helper.mjs";
2
+
3
+ //#region src/utils/async.d.ts
4
+ interface MapConcurrentOptions {
5
+ /**
6
+ * Max in-flight mappers. Non-integer values are floored, then clamped
7
+ * to the range `[1, items.length]`. `NaN` falls back to 1.
8
+ */
9
+ concurrency: number;
10
+ /**
11
+ * Rejects with `signal.reason` when aborted. In-flight mappers keep
12
+ * running but their results are not returned.
13
+ */
14
+ signal?: AbortSignal;
15
+ }
16
+ /**
17
+ * Run an async mapper over items with bounded concurrency.
18
+ * Preserves input order in the result. Fails fast on the first rejection.
19
+ */
20
+ declare function mapConcurrent<T, R>(items: readonly T[], fn: (item: T, index: number) => Awaitable<R>, options: MapConcurrentOptions): Promise<R[]>;
21
+ //#endregion
22
+ export { MapConcurrentOptions, mapConcurrent };