@better-auth/core 1.7.0-beta.6 → 1.7.0-beta.8

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 (142) hide show
  1. package/dist/api/index.d.mts +3 -3
  2. package/dist/context/global.mjs +1 -1
  3. package/dist/db/get-tables.mjs +3 -3
  4. package/dist/db/schema/account.d.mts +1 -1
  5. package/dist/db/schema/account.mjs +1 -1
  6. package/dist/error/codes.d.mts +0 -5
  7. package/dist/error/codes.mjs +0 -5
  8. package/dist/instrumentation/tracer.mjs +1 -1
  9. package/dist/oauth2/create-authorization-url.d.mts +4 -5
  10. package/dist/oauth2/create-authorization-url.mjs +4 -5
  11. package/dist/oauth2/index.d.mts +3 -4
  12. package/dist/oauth2/index.mjs +2 -3
  13. package/dist/oauth2/oauth-provider.d.mts +44 -48
  14. package/dist/oauth2/refresh-access-token.mjs +17 -2
  15. package/dist/oauth2/utils.d.mts +6 -1
  16. package/dist/oauth2/utils.mjs +24 -2
  17. package/dist/oauth2/verify-id-token.d.mts +6 -5
  18. package/dist/oauth2/verify-id-token.mjs +2 -2
  19. package/dist/social-providers/apple.d.mts +3 -5
  20. package/dist/social-providers/apple.mjs +5 -5
  21. package/dist/social-providers/atlassian.d.mts +3 -5
  22. package/dist/social-providers/atlassian.mjs +4 -4
  23. package/dist/social-providers/cognito.d.mts +3 -5
  24. package/dist/social-providers/cognito.mjs +11 -18
  25. package/dist/social-providers/discord.d.mts +3 -5
  26. package/dist/social-providers/discord.mjs +6 -7
  27. package/dist/social-providers/dropbox.d.mts +3 -5
  28. package/dist/social-providers/dropbox.mjs +5 -5
  29. package/dist/social-providers/facebook.d.mts +3 -5
  30. package/dist/social-providers/facebook.mjs +5 -5
  31. package/dist/social-providers/figma.d.mts +3 -5
  32. package/dist/social-providers/figma.mjs +5 -5
  33. package/dist/social-providers/github.d.mts +3 -5
  34. package/dist/social-providers/github.mjs +4 -4
  35. package/dist/social-providers/gitlab.d.mts +3 -5
  36. package/dist/social-providers/gitlab.mjs +6 -6
  37. package/dist/social-providers/google.d.mts +10 -10
  38. package/dist/social-providers/google.mjs +12 -13
  39. package/dist/social-providers/huggingface.d.mts +3 -5
  40. package/dist/social-providers/huggingface.mjs +8 -8
  41. package/dist/social-providers/index.d.mts +105 -177
  42. package/dist/social-providers/kakao.d.mts +3 -5
  43. package/dist/social-providers/kakao.mjs +8 -8
  44. package/dist/social-providers/kick.d.mts +3 -5
  45. package/dist/social-providers/kick.mjs +4 -4
  46. package/dist/social-providers/line.d.mts +3 -5
  47. package/dist/social-providers/line.mjs +10 -10
  48. package/dist/social-providers/linear.d.mts +3 -5
  49. package/dist/social-providers/linear.mjs +4 -4
  50. package/dist/social-providers/linkedin.d.mts +3 -5
  51. package/dist/social-providers/linkedin.mjs +10 -10
  52. package/dist/social-providers/microsoft-entra-id.d.mts +3 -5
  53. package/dist/social-providers/microsoft-entra-id.mjs +10 -11
  54. package/dist/social-providers/naver.d.mts +3 -5
  55. package/dist/social-providers/naver.mjs +4 -4
  56. package/dist/social-providers/notion.d.mts +3 -5
  57. package/dist/social-providers/notion.mjs +4 -4
  58. package/dist/social-providers/paybin.d.mts +3 -5
  59. package/dist/social-providers/paybin.mjs +10 -10
  60. package/dist/social-providers/paypal.d.mts +3 -5
  61. package/dist/social-providers/paypal.mjs +2 -8
  62. package/dist/social-providers/polar.d.mts +3 -5
  63. package/dist/social-providers/polar.mjs +8 -8
  64. package/dist/social-providers/railway.d.mts +3 -5
  65. package/dist/social-providers/railway.mjs +9 -9
  66. package/dist/social-providers/reddit.d.mts +3 -5
  67. package/dist/social-providers/reddit.mjs +5 -5
  68. package/dist/social-providers/roblox.d.mts +3 -5
  69. package/dist/social-providers/roblox.mjs +5 -5
  70. package/dist/social-providers/salesforce.d.mts +3 -5
  71. package/dist/social-providers/salesforce.mjs +8 -8
  72. package/dist/social-providers/slack.d.mts +3 -5
  73. package/dist/social-providers/slack.mjs +9 -9
  74. package/dist/social-providers/spotify.d.mts +3 -5
  75. package/dist/social-providers/spotify.mjs +5 -5
  76. package/dist/social-providers/tiktok.d.mts +3 -5
  77. package/dist/social-providers/tiktok.mjs +5 -9
  78. package/dist/social-providers/twitch.d.mts +3 -5
  79. package/dist/social-providers/twitch.mjs +4 -4
  80. package/dist/social-providers/twitter.d.mts +3 -5
  81. package/dist/social-providers/twitter.mjs +9 -9
  82. package/dist/social-providers/vercel.d.mts +3 -5
  83. package/dist/social-providers/vercel.mjs +7 -4
  84. package/dist/social-providers/vk.d.mts +3 -5
  85. package/dist/social-providers/vk.mjs +5 -5
  86. package/dist/social-providers/wechat.d.mts +3 -5
  87. package/dist/social-providers/wechat.mjs +5 -9
  88. package/dist/social-providers/zoom.d.mts +3 -6
  89. package/dist/social-providers/zoom.mjs +9 -15
  90. package/dist/types/context.d.mts +6 -2
  91. package/dist/utils/host.d.mts +1 -1
  92. package/dist/utils/host.mjs +3 -0
  93. package/package.json +1 -1
  94. package/src/db/get-tables.ts +3 -8
  95. package/src/db/schema/account.ts +5 -14
  96. package/src/error/codes.ts +0 -5
  97. package/src/oauth2/create-authorization-url.ts +5 -1
  98. package/src/oauth2/index.ts +3 -12
  99. package/src/oauth2/oauth-provider.ts +46 -53
  100. package/src/oauth2/refresh-access-token.ts +30 -5
  101. package/src/oauth2/utils.ts +39 -1
  102. package/src/oauth2/verify-id-token.ts +9 -5
  103. package/src/social-providers/apple.ts +8 -13
  104. package/src/social-providers/atlassian.ts +8 -12
  105. package/src/social-providers/cognito.ts +11 -18
  106. package/src/social-providers/discord.ts +8 -19
  107. package/src/social-providers/dropbox.ts +7 -13
  108. package/src/social-providers/facebook.ts +9 -13
  109. package/src/social-providers/figma.ts +9 -13
  110. package/src/social-providers/github.ts +8 -12
  111. package/src/social-providers/gitlab.ts +8 -14
  112. package/src/social-providers/google.ts +23 -29
  113. package/src/social-providers/huggingface.ts +8 -12
  114. package/src/social-providers/kakao.ts +8 -16
  115. package/src/social-providers/kick.ts +7 -12
  116. package/src/social-providers/line.ts +10 -14
  117. package/src/social-providers/linear.ts +6 -12
  118. package/src/social-providers/linkedin.ts +10 -14
  119. package/src/social-providers/microsoft-entra-id.ts +8 -18
  120. package/src/social-providers/naver.ts +6 -12
  121. package/src/social-providers/notion.ts +6 -12
  122. package/src/social-providers/paybin.ts +11 -14
  123. package/src/social-providers/paypal.ts +8 -6
  124. package/src/social-providers/polar.ts +8 -12
  125. package/src/social-providers/railway.ts +9 -13
  126. package/src/social-providers/reddit.ts +7 -18
  127. package/src/social-providers/roblox.ts +7 -18
  128. package/src/social-providers/salesforce.ts +8 -12
  129. package/src/social-providers/slack.ts +9 -18
  130. package/src/social-providers/spotify.ts +7 -13
  131. package/src/social-providers/tiktok.ts +7 -13
  132. package/src/social-providers/twitch.ts +8 -12
  133. package/src/social-providers/twitter.ts +8 -17
  134. package/src/social-providers/vercel.ts +10 -16
  135. package/src/social-providers/vk.ts +7 -13
  136. package/src/social-providers/wechat.ts +8 -20
  137. package/src/social-providers/zoom.ts +6 -19
  138. package/src/types/context.ts +8 -2
  139. package/src/utils/host.ts +10 -1
  140. package/dist/oauth2/scopes.d.mts +0 -76
  141. package/dist/oauth2/scopes.mjs +0 -96
  142. package/src/oauth2/scopes.ts +0 -118
@@ -14,7 +14,6 @@ interface VercelOptions extends ProviderOptions<VercelProfile> {
14
14
  declare const vercel: (options: VercelOptions) => {
15
15
  id: "vercel";
16
16
  name: string;
17
- callbackPath: string;
18
17
  createAuthorizationURL({
19
18
  state,
20
19
  scopes,
@@ -28,11 +27,9 @@ declare const vercel: (options: VercelOptions) => {
28
27
  redirectURI: string;
29
28
  display?: string | undefined;
30
29
  loginHint?: string | undefined;
30
+ idTokenNonce?: string | undefined;
31
31
  additionalParams?: Record<string, string> | undefined;
32
- }): Promise<{
33
- url: URL;
34
- requestedScopes: string[];
35
- }>;
32
+ }): Promise<URL>;
36
33
  validateAuthorizationCode: ({
37
34
  code,
38
35
  codeVerifier,
@@ -44,6 +41,7 @@ declare const vercel: (options: VercelOptions) => {
44
41
  deviceId?: string | undefined;
45
42
  }) => Promise<OAuth2Tokens>;
46
43
  getUserInfo(token: OAuth2Tokens & {
44
+ expectedIdTokenNonce?: string | undefined;
47
45
  user?: {
48
46
  name?: {
49
47
  firstName?: string;
@@ -1,22 +1,25 @@
1
1
  import { BetterAuthError } from "../error/index.mjs";
2
- import { resolveRequestedScopes } from "../oauth2/scopes.mjs";
3
2
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
4
3
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
5
4
  import { betterFetch } from "@better-fetch/fetch";
6
5
  //#region src/social-providers/vercel.ts
7
- const VERCEL_DEFAULT_SCOPES = [];
8
6
  const vercel = (options) => {
9
7
  return {
10
8
  id: "vercel",
11
9
  name: "Vercel",
12
- callbackPath: "/callback/vercel",
13
10
  createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
14
11
  if (!codeVerifier) throw new BetterAuthError("codeVerifier is required for Vercel");
12
+ let _scopes = void 0;
13
+ if (options.scope !== void 0 || scopes !== void 0) {
14
+ _scopes = [];
15
+ if (options.scope) _scopes.push(...options.scope);
16
+ if (scopes) _scopes.push(...scopes);
17
+ }
15
18
  return createAuthorizationURL({
16
19
  id: "vercel",
17
20
  options,
18
21
  authorizationEndpoint: "https://vercel.com/oauth/authorize",
19
- scopes: resolveRequestedScopes(options, VERCEL_DEFAULT_SCOPES, scopes),
22
+ scopes: _scopes,
20
23
  state,
21
24
  codeVerifier,
22
25
  redirectURI,
@@ -20,7 +20,6 @@ interface VkOption extends ProviderOptions {
20
20
  declare const vk: (options: VkOption) => {
21
21
  id: "vk";
22
22
  name: string;
23
- callbackPath: string;
24
23
  createAuthorizationURL({
25
24
  state,
26
25
  scopes,
@@ -34,11 +33,9 @@ declare const vk: (options: VkOption) => {
34
33
  redirectURI: string;
35
34
  display?: string | undefined;
36
35
  loginHint?: string | undefined;
36
+ idTokenNonce?: string | undefined;
37
37
  additionalParams?: Record<string, string> | undefined;
38
- }): Promise<{
39
- url: URL;
40
- requestedScopes: string[];
41
- }>;
38
+ }): Promise<URL>;
42
39
  validateAuthorizationCode: ({
43
40
  code,
44
41
  codeVerifier,
@@ -52,6 +49,7 @@ declare const vk: (options: VkOption) => {
52
49
  }) => Promise<OAuth2Tokens>;
53
50
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
54
51
  getUserInfo(data: OAuth2Tokens & {
52
+ expectedIdTokenNonce?: string | undefined;
55
53
  user?: {
56
54
  name?: {
57
55
  firstName?: string;
@@ -1,22 +1,22 @@
1
- import { resolveRequestedScopes } from "../oauth2/scopes.mjs";
2
1
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
3
2
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
4
3
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
5
4
  import { betterFetch } from "@better-fetch/fetch";
6
5
  //#region src/social-providers/vk.ts
7
- const VK_DEFAULT_SCOPES = ["email", "phone"];
8
6
  const vk = (options) => {
9
7
  const tokenEndpoint = "https://id.vk.com/oauth2/auth";
10
8
  return {
11
9
  id: "vk",
12
10
  name: "VK",
13
- callbackPath: "/callback/vk",
14
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
11
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
12
+ const _scopes = options.disableDefaultScope ? [] : ["email", "phone"];
13
+ if (options.scope) _scopes.push(...options.scope);
14
+ if (scopes) _scopes.push(...scopes);
15
15
  return createAuthorizationURL({
16
16
  id: "vk",
17
17
  options,
18
18
  authorizationEndpoint: "https://id.vk.com/authorize",
19
- scopes: resolveRequestedScopes(options, VK_DEFAULT_SCOPES, scopes),
19
+ scopes: _scopes,
20
20
  state,
21
21
  redirectURI,
22
22
  codeVerifier,
@@ -53,7 +53,6 @@ interface WeChatOptions extends ProviderOptions<WeChatProfile> {
53
53
  declare const wechat: (options: WeChatOptions) => {
54
54
  id: "wechat";
55
55
  name: string;
56
- callbackPath: string;
57
56
  createAuthorizationURL({
58
57
  state,
59
58
  scopes,
@@ -66,11 +65,9 @@ declare const wechat: (options: WeChatOptions) => {
66
65
  redirectURI: string;
67
66
  display?: string | undefined;
68
67
  loginHint?: string | undefined;
68
+ idTokenNonce?: string | undefined;
69
69
  additionalParams?: Record<string, string> | undefined;
70
- }): {
71
- url: URL;
72
- requestedScopes: string[];
73
- };
70
+ }): URL;
74
71
  validateAuthorizationCode: ({
75
72
  code
76
73
  }: {
@@ -95,6 +92,7 @@ declare const wechat: (options: WeChatOptions) => {
95
92
  scopes: string[];
96
93
  }>);
97
94
  getUserInfo(token: OAuth2Tokens & {
95
+ expectedIdTokenNonce?: string | undefined;
98
96
  user?: {
99
97
  name?: {
100
98
  firstName?: string;
@@ -1,17 +1,16 @@
1
- import { resolveRequestedScopes } from "../oauth2/scopes.mjs";
2
1
  import { RESERVED_AUTHORIZATION_PARAMS_SET } from "../oauth2/create-authorization-url.mjs";
3
2
  import { betterFetch } from "@better-fetch/fetch";
4
3
  //#region src/social-providers/wechat.ts
5
- const WECHAT_DEFAULT_SCOPES = ["snsapi_login"];
6
4
  const wechat = (options) => {
7
5
  return {
8
6
  id: "wechat",
9
7
  name: "WeChat",
10
- callbackPath: "/callback/wechat",
11
8
  createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
12
- const requestedScopes = resolveRequestedScopes(options, WECHAT_DEFAULT_SCOPES, scopes);
9
+ const _scopes = options.disableDefaultScope ? [] : ["snsapi_login"];
10
+ options.scope && _scopes.push(...options.scope);
11
+ scopes && _scopes.push(...scopes);
13
12
  const url = new URL("https://open.weixin.qq.com/connect/qrconnect");
14
- url.searchParams.set("scope", requestedScopes.join(","));
13
+ url.searchParams.set("scope", _scopes.join(","));
15
14
  url.searchParams.set("response_type", "code");
16
15
  url.searchParams.set("appid", options.clientId);
17
16
  url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
@@ -23,10 +22,7 @@ const wechat = (options) => {
23
22
  url.searchParams.set(key, value);
24
23
  }
25
24
  url.hash = "wechat_redirect";
26
- return {
27
- url,
28
- requestedScopes
29
- };
25
+ return url;
30
26
  },
31
27
  validateAuthorizationCode: async ({ code }) => {
32
28
  const { data: tokenData, error } = await betterFetch("https://api.weixin.qq.com/sns/oauth2/access_token?" + new URLSearchParams({
@@ -116,10 +116,8 @@ interface ZoomOptions extends ProviderOptions<ZoomProfile> {
116
116
  declare const zoom: (userOptions: ZoomOptions) => {
117
117
  id: "zoom";
118
118
  name: string;
119
- callbackPath: string;
120
119
  createAuthorizationURL: ({
121
120
  state,
122
- scopes,
123
121
  redirectURI,
124
122
  codeVerifier,
125
123
  additionalParams
@@ -130,11 +128,9 @@ declare const zoom: (userOptions: ZoomOptions) => {
130
128
  redirectURI: string;
131
129
  display?: string | undefined;
132
130
  loginHint?: string | undefined;
131
+ idTokenNonce?: string | undefined;
133
132
  additionalParams?: Record<string, string> | undefined;
134
- }) => Promise<{
135
- url: URL;
136
- requestedScopes: string[];
137
- }>;
133
+ }) => Promise<URL>;
138
134
  validateAuthorizationCode: ({
139
135
  code,
140
136
  redirectURI,
@@ -147,6 +143,7 @@ declare const zoom: (userOptions: ZoomOptions) => {
147
143
  }) => Promise<OAuth2Tokens>;
148
144
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
149
145
  getUserInfo(token: OAuth2Tokens & {
146
+ expectedIdTokenNonce?: string | undefined;
150
147
  user?: {
151
148
  name?: {
152
149
  firstName?: string;
@@ -1,10 +1,8 @@
1
- import { resolveRequestedScopes } from "../oauth2/scopes.mjs";
2
1
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
3
2
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
4
3
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
5
4
  import { betterFetch } from "@better-fetch/fetch";
6
5
  //#region src/social-providers/zoom.ts
7
- const ZOOM_DEFAULT_SCOPES = [];
8
6
  const zoom = (userOptions) => {
9
7
  const options = {
10
8
  pkce: true,
@@ -13,19 +11,15 @@ const zoom = (userOptions) => {
13
11
  return {
14
12
  id: "zoom",
15
13
  name: "Zoom",
16
- callbackPath: "/callback/zoom",
17
- createAuthorizationURL: ({ state, scopes, redirectURI, codeVerifier, additionalParams }) => {
18
- return createAuthorizationURL({
19
- id: "zoom",
20
- options,
21
- authorizationEndpoint: "https://zoom.us/oauth/authorize",
22
- scopes: resolveRequestedScopes(options, ZOOM_DEFAULT_SCOPES, scopes),
23
- state,
24
- redirectURI,
25
- codeVerifier: options.pkce ? codeVerifier : void 0,
26
- additionalParams
27
- });
28
- },
14
+ createAuthorizationURL: async ({ state, redirectURI, codeVerifier, additionalParams }) => createAuthorizationURL({
15
+ id: "zoom",
16
+ options,
17
+ authorizationEndpoint: "https://zoom.us/oauth/authorize",
18
+ state,
19
+ redirectURI,
20
+ codeVerifier: options.pkce ? codeVerifier : void 0,
21
+ additionalParams
22
+ }),
29
23
  validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
30
24
  return validateAuthorizationCode({
31
25
  code,
@@ -10,7 +10,7 @@ import { BetterAuthOptions, BetterAuthRateLimitOptions, UserProvisioningSource }
10
10
  import { Account } from "../db/schema/account.mjs";
11
11
  import { BetterAuthCookie, BetterAuthCookies } from "./cookie.mjs";
12
12
  import { SecretConfig } from "./secret.mjs";
13
- import { UpstreamProvider } from "../oauth2/oauth-provider.mjs";
13
+ import { OAuthProvider } from "../oauth2/oauth-provider.mjs";
14
14
  import { CookieOptions, EndpointContext } from "better-call";
15
15
 
16
16
  //#region src/types/context.d.ts
@@ -54,6 +54,10 @@ type GenericEndpointContext<Options extends BetterAuthOptions = BetterAuthOption
54
54
  context: AuthContext<Options>;
55
55
  };
56
56
  interface InternalAdapter<_Options extends BetterAuthOptions = BetterAuthOptions> {
57
+ createOAuthUser(user: Omit<User, "id" | "createdAt" | "updatedAt">, account: Omit<Account, "userId" | "id" | "createdAt" | "updatedAt"> & Partial<Account>): Promise<{
58
+ user: User;
59
+ account: Account;
60
+ }>;
57
61
  createUser<T extends Record<string, any>>(user: Omit<User, "id" | "createdAt" | "updatedAt" | "emailVerified"> & Partial<User> & Record<string, any>,
58
62
  /**
59
63
  * Provisioning source. The creation seam adds `action: "create-user"` and
@@ -232,7 +236,7 @@ type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> = Plugin
232
236
  session: Session & Record<string, any>;
233
237
  user: User & Record<string, any>;
234
238
  } | null) => void;
235
- socialProviders: UpstreamProvider[];
239
+ socialProviders: OAuthProvider[];
236
240
  authCookies: BetterAuthCookies;
237
241
  logger: ReturnType<typeof createLogger>;
238
242
  rateLimit: {
@@ -26,7 +26,7 @@
26
26
  * The semantic kind of a host, derived from RFC 6890 special-purpose registries
27
27
  * plus a few domain-name categories (localhost, cloud metadata FQDNs).
28
28
  */
29
- type HostKind = /** IPv4 `127.0.0.0/8` or IPv6 `::1`. */"loopback" /** DNS name `localhost` or RFC 6761 `.localhost` TLD. */ | "localhost" /** IPv4 `0.0.0.0` or IPv6 `::` — "this host on this network", not loopback. */ | "unspecified" /** RFC 1918 `10/8`, `172.16/12`, `192.168/16`, or IPv6 ULA `fc00::/7`. */ | "private" /** IPv4 `169.254/16` or IPv6 `fe80::/10`. Includes AWS IMDS `169.254.169.254`. */ | "linkLocal" /** RFC 6598 carrier-grade NAT `100.64.0.0/10`. */ | "sharedAddressSpace" /** RFC 5737 `192.0.2/24`, `198.51.100/24`, `203.0.113/24`, or RFC 3849 `2001:db8::/32`. */ | "documentation" /** RFC 2544 `198.18.0.0/15`. */ | "benchmarking" /** IPv4 `224.0.0.0/4` or IPv6 `ff00::/8`. */ | "multicast" /** IPv4 limited broadcast `255.255.255.255`. */ | "broadcast" /** Other RFC 6890 special-purpose ranges (0/8, 192.0.0/24, 240/4, 2001::/32, etc.). */ | "reserved" /** Cloud metadata service FQDN (e.g. `metadata.google.internal`). */ | "cloudMetadata" /** Any host not matching a special-purpose range above. */ | "public";
29
+ type HostKind = /** IPv4 `127.0.0.0/8` or IPv6 `::1`. */"loopback" /** DNS name `localhost` or RFC 6761 `.localhost` TLD. */ | "localhost" /** IPv4 `0.0.0.0` or IPv6 `::` — "this host on this network", not loopback. */ | "unspecified" /** RFC 1918 `10/8`, `172.16/12`, `192.168/16`, or IPv6 ULA `fc00::/7`. */ | "private" /** IPv4 `169.254/16` or IPv6 `fe80::/10`. Includes AWS IMDS `169.254.169.254`. */ | "linkLocal" /** RFC 6598 carrier-grade NAT `100.64.0.0/10`. */ | "sharedAddressSpace" /** RFC 5737 `192.0.2/24`, `198.51.100/24`, `203.0.113/24`, or RFC 3849 `2001:db8::/32`. */ | "documentation" /** RFC 2544 `198.18.0.0/15`. */ | "benchmarking" /** IPv4 `224.0.0.0/4` or IPv6 `ff00::/8`. */ | "multicast" /** IPv4 limited broadcast `255.255.255.255`. */ | "broadcast" /** Other RFC 6890 special-purpose ranges (0.0.0.0/8, 192.0.0.0/24, 192.88.99.0/24, 240.0.0.0/4, 2001::/32, etc.). */ | "reserved" /** Cloud metadata service FQDN (e.g. `metadata.google.internal`). */ | "cloudMetadata" /** Any host not matching a special-purpose range above. */ | "public";
30
30
  /**
31
31
  * The syntactic form of the input host: an IPv4 literal, an IPv6 literal, or
32
32
  * a domain name. IPv4-mapped IPv6 (`::ffff:192.0.2.1`) is reported as `ipv4`
@@ -86,6 +86,7 @@ function classifyIPv4(ip) {
86
86
  if (inIPv4Range(n, ipv4ToUint32("224.0.0.0"), 4)) return "multicast";
87
87
  if (inIPv4Range(n, ipv4ToUint32("0.0.0.0"), 8)) return "reserved";
88
88
  if (inIPv4Range(n, ipv4ToUint32("192.0.0.0"), 24)) return "reserved";
89
+ if (inIPv4Range(n, ipv4ToUint32("192.88.99.0"), 24)) return "reserved";
89
90
  if (inIPv4Range(n, ipv4ToUint32("240.0.0.0"), 4)) return "reserved";
90
91
  return "public";
91
92
  }
@@ -124,6 +125,7 @@ function classifyIPv6(expanded) {
124
125
  const secondByte = Number.parseInt(expanded.slice(2, 4), 16);
125
126
  if (firstByte === 255) return "multicast";
126
127
  if (firstByte === 254 && (secondByte & 192) === 128) return "linkLocal";
128
+ if (firstByte === 254 && (secondByte & 192) === 192) return "reserved";
127
129
  if ((firstByte & 254) === 252) return "private";
128
130
  if (expanded.startsWith("2001:0db8:")) return "documentation";
129
131
  if (expanded.startsWith("2001:0002:0000:")) return "benchmarking";
@@ -146,6 +148,7 @@ function classifyIPv6(expanded) {
146
148
  if (expanded.startsWith("0100:0000:0000:0000:")) return "reserved";
147
149
  if (expanded.startsWith("3fff:0")) return "documentation";
148
150
  if (expanded.startsWith("5f00:")) return "reserved";
151
+ if (expanded.startsWith("0000:0000:0000:0000:0000:0000:")) return "reserved";
149
152
  return "public";
150
153
  }
151
154
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/core",
3
- "version": "1.7.0-beta.6",
3
+ "version": "1.7.0-beta.8",
4
4
  "description": "The most comprehensive authentication framework for TypeScript.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -261,15 +261,10 @@ export const getAuthTables = (
261
261
  options.account?.fields?.refreshTokenExpiresAt ||
262
262
  "refreshTokenExpiresAt",
263
263
  },
264
- // Renamed from the legacy `scope` column. The migration generator
265
- // only adds this column; it does not transform the legacy `scope`
266
- // value. Upgrading installs need a manual data migration (split
267
- // legacy `scope` on comma/space, trim, drop empties, dedupe). Order
268
- // is insignificant per RFC 6749 §3.3.
269
- grantedScopes: {
270
- type: "string[]",
264
+ scope: {
265
+ type: "string",
271
266
  required: false,
272
- fieldName: options.account?.fields?.grantedScopes || "grantedScopes",
267
+ fieldName: options.account?.fields?.scope || "scope",
273
268
  },
274
269
  password: {
275
270
  type: "string",
@@ -23,21 +23,12 @@ export const accountSchema = coreSchema.extend({
23
23
  */
24
24
  refreshTokenExpiresAt: z.date().nullish(),
25
25
  /**
26
- * The scopes the user has granted, as last observed (durable, per-account,
27
- * the unit of revocation and the refresh ceiling). A native array, not a
28
- * delimited string: scope order is insignificant per RFC 6749 §3.3, so the
29
- * value is normalized (trimmed, deduped, sorted) on write.
30
- *
31
- * Renamed from the legacy comma-joined `scope` string. Breaking, with no
32
- * automatic data migration (and no read-time shim): the migration generator
33
- * only adds the new `grantedScopes` column, so legacy accounts read as empty
34
- * here until an upgrade backfills `grantedScopes` from the old `scope` values
35
- * (split on comma/space, trim, drop empties, dedupe). See the release
36
- * changeset for the backfill.
37
- *
38
- * @see https://www.rfc-editor.org/rfc/rfc6749#section-3.3
26
+ * The set of OAuth scopes the user has granted to this account, stored
27
+ * as a comma-separated list. Represents the accumulated grant rather
28
+ * than the latest token's `scope` claim, since per RFC 6749 Section 1.5 a
29
+ * token's scope may be narrower than the user's grant.
39
30
  */
40
- grantedScopes: z.array(z.string()).nullish(),
31
+ scope: z.string().nullish(),
41
32
  /**
42
33
  * Password is only stored in the credential provider
43
34
  */
@@ -29,11 +29,6 @@ export const BASE_ERROR_CODES = defineErrorCodes({
29
29
  TOKEN_EXPIRED: "Token expired",
30
30
  ID_TOKEN_NOT_SUPPORTED: "id_token not supported",
31
31
  FAILED_TO_GET_USER_INFO: "Failed to get user info",
32
- PROVIDER_NOT_SUPPORTED: "Provider not supported",
33
- TOKEN_REFRESH_NOT_SUPPORTED: "Token refresh not supported",
34
- REFRESH_TOKEN_NOT_FOUND: "Refresh token not found",
35
- FAILED_TO_GET_ACCESS_TOKEN: "Failed to get a valid access token",
36
- FAILED_TO_REFRESH_ACCESS_TOKEN: "Failed to refresh access token",
37
32
  USER_EMAIL_NOT_FOUND: "User email not found",
38
33
  EMAIL_NOT_VERIFIED: "Email not verified",
39
34
  PASSWORD_TOO_SHORT: "Password too short",
@@ -15,6 +15,7 @@ export const RESERVED_AUTHORIZATION_PARAMS = [
15
15
  "response_type",
16
16
  "code_challenge",
17
17
  "code_challenge_method",
18
+ "nonce",
18
19
  "scope",
19
20
  ] as const;
20
21
 
@@ -37,6 +38,7 @@ export async function createAuthorizationURL({
37
38
  responseType,
38
39
  display,
39
40
  loginHint,
41
+ nonce,
40
42
  hd,
41
43
  responseMode,
42
44
  additionalParams,
@@ -56,6 +58,7 @@ export async function createAuthorizationURL({
56
58
  responseType?: string | undefined;
57
59
  display?: string | undefined;
58
60
  loginHint?: string | undefined;
61
+ nonce?: string | undefined;
59
62
  hd?: string | undefined;
60
63
  responseMode?: string | undefined;
61
64
  additionalParams?: Record<string, string> | undefined;
@@ -77,6 +80,7 @@ export async function createAuthorizationURL({
77
80
  duration && url.searchParams.set("duration", duration);
78
81
  display && url.searchParams.set("display", display);
79
82
  loginHint && url.searchParams.set("login_hint", loginHint);
83
+ nonce && url.searchParams.set("nonce", nonce);
80
84
  prompt && url.searchParams.set("prompt", prompt);
81
85
  hd && url.searchParams.set("hd", hd);
82
86
  accessType && url.searchParams.set("access_type", accessType);
@@ -107,5 +111,5 @@ export async function createAuthorizationURL({
107
111
  url.searchParams.set(key, value);
108
112
  }
109
113
  }
110
- return { url, requestedScopes: scopes ?? [] };
114
+ return url;
111
115
  }
@@ -63,27 +63,17 @@ export {
63
63
  verifyDpopProof,
64
64
  } from "./dpop";
65
65
  export type {
66
- AuthorizationURLResult,
67
- GrantAuthority,
68
66
  OAuth2Tokens,
69
67
  OAuth2UserInfo,
70
68
  OAuthIdTokenConfig,
71
- ProviderGrantAuthority,
69
+ OAuthProvider,
70
+ OAuthRefreshContext,
72
71
  ProviderOptions,
73
- UpstreamProvider,
74
72
  } from "./oauth-provider";
75
73
  export {
76
74
  refreshAccessToken,
77
75
  refreshAccessTokenRequest,
78
76
  } from "./refresh-access-token";
79
- export {
80
- includesGrantedScope,
81
- normalizeScopes,
82
- parseScopeField,
83
- readGrantedScopes,
84
- resolveRequestedScopes,
85
- unionGrantedScopes,
86
- } from "./scopes";
87
77
  export type {
88
78
  TokenEndpointAuth,
89
79
  TokenEndpointAuthMethod,
@@ -94,6 +84,7 @@ export {
94
84
  generateCodeChallenge,
95
85
  getOAuth2Tokens,
96
86
  getPrimaryClientId,
87
+ mergeScopes,
97
88
  } from "./utils";
98
89
  export {
99
90
  authorizationCodeRequest,
@@ -78,63 +78,33 @@ export type OAuth2UserInfo = {
78
78
  };
79
79
 
80
80
  /**
81
- * The result of building a provider authorization URL.
81
+ * Request metadata available to provider refresh hooks.
82
82
  *
83
- * `requestedScopes` is the effective set of scopes encoded in the URL (the
84
- * provider's built-in defaults + configured `options.scope` + per-request
85
- * `scopes`, composed by `resolveRequestedScopes`). Callers persist it so the
86
- * callback can fall back to the request when the provider omits `scope` from
87
- * its token response (RFC 6749 §5.1).
83
+ * The refresh flow may be triggered by endpoints such as `getAccessToken` or
84
+ * `refreshToken`; this context gives provider hooks access to the triggering
85
+ * request without exposing the full endpoint implementation surface.
88
86
  */
89
- export interface AuthorizationURLResult {
90
- url: URL;
91
- requestedScopes: string[];
87
+ export interface OAuthRefreshContext {
88
+ headers?: Headers | undefined;
89
+ request?: Request | undefined;
92
90
  }
93
91
 
94
- /**
95
- * How much an RP trusts a provider's echoed token-response `scope` when
96
- * persisting `account.grantedScopes`.
97
- *
98
- * - `"full-grant"`: the echo is the user's complete current grant, so the seam
99
- * replaces the stored grant with it. This is the only path that may narrow
100
- * the grant. Declare it only for providers whose token response reports the
101
- * full combined grant, e.g. Google with `include_granted_scopes`.
102
- * - `"projection"`: the echo is this request's subset, so the seam unions it
103
- * onto the stored grant. The safe default for every provider.
104
- * - `"absent-echo"`: the provider omitted `scope`, so the grant equals what was
105
- * requested (RFC 6749 §5.1) and the seam unions the requested set. Resolved
106
- * at runtime by the persistence seam, never declared by a provider.
107
- *
108
- * @see https://www.rfc-editor.org/rfc/rfc6749#section-5.1
109
- */
110
- export type GrantAuthority = "full-grant" | "projection" | "absent-echo";
111
-
112
- /**
113
- * The authority a provider may declare for its own echoed scope. `"absent-echo"`
114
- * is excluded because it is a runtime condition (an omitted echo), not a
115
- * provider trait.
116
- */
117
- export type ProviderGrantAuthority = Exclude<GrantAuthority, "absent-echo">;
118
-
119
- export interface UpstreamProvider<
92
+ export interface OAuthProvider<
120
93
  T extends Record<string, any> = Record<string, any>,
121
94
  O extends Record<string, any> = Partial<ProviderOptions>,
122
95
  > {
123
96
  id: LiteralString;
124
97
  /**
125
- * The path the provider redirects back to, relative to the app base URL,
126
- * e.g. `/callback/google`.
127
- */
128
- callbackPath: string;
129
- /**
130
- * How the persistence seam treats this provider's echoed token-response
131
- * `scope`. Declare `"full-grant"` only when the echo is the user's complete
132
- * current grant (e.g. Google with `include_granted_scopes`); otherwise the
133
- * echo is unioned onto the stored grant.
98
+ * Optional path under the resolved per-request `baseURL` where this
99
+ * provider's OAuth callback handler is mounted. Providers that use the
100
+ * shared `/callback/<id>` route can omit this.
134
101
  *
135
- * @default "projection"
102
+ * Custom paths must start with `/`.
103
+ *
104
+ * Endpoints compose `redirectURI = ctx.context.baseURL + callbackPath` per
105
+ * request, so the provider must not hardcode an origin or `baseURL` here.
136
106
  */
137
- grantAuthority?: ProviderGrantAuthority | undefined;
107
+ callbackPath?: string | undefined;
138
108
  createAuthorizationURL: (data: {
139
109
  state: string;
140
110
  codeVerifier: string;
@@ -142,6 +112,12 @@ export interface UpstreamProvider<
142
112
  redirectURI: string;
143
113
  display?: string | undefined;
144
114
  loginHint?: string | undefined;
115
+ /**
116
+ * OIDC nonce generated by the redirect initiator and persisted in OAuth
117
+ * state. Providers that set `requiresIdTokenNonce` must forward this to
118
+ * the authorization URL as the `nonce` parameter.
119
+ */
120
+ idTokenNonce?: string | undefined;
145
121
  /**
146
122
  * Extra query parameters to append to the authorization URL.
147
123
  * Providers forward these to the shared `createAuthorizationURL` helper,
@@ -149,7 +125,7 @@ export interface UpstreamProvider<
149
125
  * before applying them.
150
126
  */
151
127
  additionalParams?: Record<string, string> | undefined;
152
- }) => Awaitable<AuthorizationURLResult>;
128
+ }) => Awaitable<URL>;
153
129
  name: string;
154
130
  validateAuthorizationCode: (data: {
155
131
  code: string;
@@ -159,6 +135,12 @@ export interface UpstreamProvider<
159
135
  }) => Promise<OAuth2Tokens | null>;
160
136
  getUserInfo: (
161
137
  token: OAuth2Tokens & {
138
+ /**
139
+ * OIDC nonce recovered from OAuth state. Providers that required an
140
+ * ID-token nonce must pass this to `verifyProviderIdToken` before
141
+ * trusting ID-token claims.
142
+ */
143
+ expectedIdTokenNonce?: string | undefined;
162
144
  /**
163
145
  * The user object from the provider
164
146
  * This is only available for some providers like Apple
@@ -178,11 +160,19 @@ export interface UpstreamProvider<
178
160
  data: T;
179
161
  } | null>;
180
162
  /**
181
- * Custom function to refresh a token
163
+ * Custom function to refresh a token.
164
+ *
165
+ * Receives request metadata from the endpoint that triggered the refresh.
166
+ * Providers that don't need request-scoped data can ignore the second
167
+ * argument.
182
168
  */
183
169
  refreshAccessToken?:
184
- | ((refreshToken: string) => Promise<OAuth2Tokens>)
170
+ | ((
171
+ refreshToken: string,
172
+ ctx?: OAuthRefreshContext,
173
+ ) => Promise<OAuth2Tokens>)
185
174
  | undefined;
175
+ revokeToken?: ((token: string) => Promise<void>) | undefined;
186
176
  /**
187
177
  * Declarative id_token verification config consumed by the shared
188
178
  * `verifyProviderIdToken` verifier. Providers set this instead of implementing a boolean
@@ -195,6 +185,13 @@ export interface UpstreamProvider<
195
185
  * against this value to prevent authorization server mix-up attacks.
196
186
  */
197
187
  issuer?: string | undefined;
188
+ /**
189
+ * Require shared OAuth redirect routes to bind ID-token verification to an
190
+ * authorization request nonce. When true, routes generate `idTokenNonce`,
191
+ * pass it to `createAuthorizationURL`, persist it in state, and provide it
192
+ * back to `getUserInfo` as `expectedIdTokenNonce`.
193
+ */
194
+ requiresIdTokenNonce?: boolean | undefined;
198
195
  /**
199
196
  * Disable implicit sign up for new users. When set to true for the provider,
200
197
  * sign-in need to be called with with requestSignUp as true to create new users.
@@ -283,10 +280,6 @@ export type ProviderOptions<Profile extends Record<string, any> = any> = {
283
280
  emailVerified: boolean;
284
281
  [key: string]: any;
285
282
  };
286
- // TODO: type as `Profile` once provider getUserInfo paths that return a
287
- // narrower data shape than their declared profile are reconciled; today
288
- // `any` is load-bearing for those (e.g. facebook) and tightening it ripples
289
- // across ~10 providers, out of scope for the grant refactor.
290
283
  data: any;
291
284
  } | null>)
292
285
  | undefined;