@better-auth/core 1.7.0-beta.3 → 1.7.0-beta.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.
Files changed (170) hide show
  1. package/dist/context/global.mjs +1 -1
  2. package/dist/db/adapter/factory.mjs +63 -1
  3. package/dist/db/adapter/index.d.mts +35 -1
  4. package/dist/db/adapter/types.d.mts +1 -1
  5. package/dist/db/type.d.mts +12 -0
  6. package/dist/error/codes.d.mts +1 -0
  7. package/dist/error/codes.mjs +1 -0
  8. package/dist/instrumentation/tracer.mjs +1 -1
  9. package/dist/oauth2/authorization-params.d.mts +12 -0
  10. package/dist/oauth2/authorization-params.mjs +12 -0
  11. package/dist/oauth2/basic-credentials.d.mts +30 -0
  12. package/dist/oauth2/basic-credentials.mjs +64 -0
  13. package/dist/oauth2/client-assertion.d.mts +38 -22
  14. package/dist/oauth2/client-assertion.mjs +63 -28
  15. package/dist/oauth2/client-credentials-token.d.mts +19 -40
  16. package/dist/oauth2/client-credentials-token.mjs +18 -29
  17. package/dist/oauth2/create-authorization-url.d.mts +9 -1
  18. package/dist/oauth2/create-authorization-url.mjs +23 -5
  19. package/dist/oauth2/index.d.mts +10 -7
  20. package/dist/oauth2/index.mjs +9 -7
  21. package/dist/oauth2/oauth-provider.d.mts +21 -2
  22. package/dist/oauth2/refresh-access-token.d.mts +20 -40
  23. package/dist/oauth2/refresh-access-token.mjs +19 -32
  24. package/dist/oauth2/token-endpoint-auth.d.mts +17 -0
  25. package/dist/oauth2/token-endpoint-auth.mjs +89 -0
  26. package/dist/oauth2/utils.d.mts +9 -1
  27. package/dist/oauth2/utils.mjs +12 -1
  28. package/dist/oauth2/validate-authorization-code.d.mts +17 -52
  29. package/dist/oauth2/validate-authorization-code.mjs +17 -30
  30. package/dist/oauth2/verify.mjs +15 -5
  31. package/dist/social-providers/apple.d.mts +4 -18
  32. package/dist/social-providers/apple.mjs +14 -3
  33. package/dist/social-providers/atlassian.d.mts +3 -1
  34. package/dist/social-providers/atlassian.mjs +5 -2
  35. package/dist/social-providers/cognito.d.mts +16 -1
  36. package/dist/social-providers/cognito.mjs +6 -2
  37. package/dist/social-providers/discord.d.mts +4 -2
  38. package/dist/social-providers/discord.mjs +16 -3
  39. package/dist/social-providers/dropbox.d.mts +3 -1
  40. package/dist/social-providers/dropbox.mjs +5 -4
  41. package/dist/social-providers/facebook.d.mts +3 -1
  42. package/dist/social-providers/facebook.mjs +5 -2
  43. package/dist/social-providers/figma.d.mts +3 -1
  44. package/dist/social-providers/figma.mjs +3 -2
  45. package/dist/social-providers/github.d.mts +3 -1
  46. package/dist/social-providers/github.mjs +5 -4
  47. package/dist/social-providers/gitlab.d.mts +3 -1
  48. package/dist/social-providers/gitlab.mjs +3 -2
  49. package/dist/social-providers/google.d.mts +3 -1
  50. package/dist/social-providers/google.mjs +5 -2
  51. package/dist/social-providers/huggingface.d.mts +3 -1
  52. package/dist/social-providers/huggingface.mjs +3 -2
  53. package/dist/social-providers/index.d.mts +104 -36
  54. package/dist/social-providers/kakao.d.mts +3 -1
  55. package/dist/social-providers/kakao.mjs +3 -2
  56. package/dist/social-providers/kick.d.mts +3 -1
  57. package/dist/social-providers/kick.mjs +3 -2
  58. package/dist/social-providers/line.d.mts +3 -1
  59. package/dist/social-providers/line.mjs +3 -2
  60. package/dist/social-providers/linear.d.mts +3 -1
  61. package/dist/social-providers/linear.mjs +3 -2
  62. package/dist/social-providers/linkedin.d.mts +3 -1
  63. package/dist/social-providers/linkedin.mjs +3 -2
  64. package/dist/social-providers/microsoft-entra-id.d.mts +2 -1
  65. package/dist/social-providers/microsoft-entra-id.mjs +3 -2
  66. package/dist/social-providers/naver.d.mts +3 -1
  67. package/dist/social-providers/naver.mjs +3 -2
  68. package/dist/social-providers/notion.d.mts +3 -1
  69. package/dist/social-providers/notion.mjs +5 -2
  70. package/dist/social-providers/paybin.d.mts +3 -1
  71. package/dist/social-providers/paybin.mjs +3 -2
  72. package/dist/social-providers/paypal.d.mts +3 -1
  73. package/dist/social-providers/paypal.mjs +4 -3
  74. package/dist/social-providers/polar.d.mts +3 -1
  75. package/dist/social-providers/polar.mjs +3 -2
  76. package/dist/social-providers/railway.d.mts +3 -1
  77. package/dist/social-providers/railway.mjs +3 -2
  78. package/dist/social-providers/reddit.d.mts +3 -1
  79. package/dist/social-providers/reddit.mjs +3 -2
  80. package/dist/social-providers/roblox.d.mts +4 -2
  81. package/dist/social-providers/roblox.mjs +12 -2
  82. package/dist/social-providers/salesforce.d.mts +3 -1
  83. package/dist/social-providers/salesforce.mjs +3 -2
  84. package/dist/social-providers/slack.d.mts +4 -2
  85. package/dist/social-providers/slack.mjs +11 -8
  86. package/dist/social-providers/spotify.d.mts +3 -1
  87. package/dist/social-providers/spotify.mjs +3 -2
  88. package/dist/social-providers/tiktok.d.mts +3 -1
  89. package/dist/social-providers/tiktok.mjs +14 -2
  90. package/dist/social-providers/twitch.d.mts +3 -1
  91. package/dist/social-providers/twitch.mjs +3 -2
  92. package/dist/social-providers/twitter.d.mts +5 -2
  93. package/dist/social-providers/twitter.mjs +2 -1
  94. package/dist/social-providers/vercel.d.mts +3 -1
  95. package/dist/social-providers/vercel.mjs +3 -2
  96. package/dist/social-providers/vk.d.mts +3 -1
  97. package/dist/social-providers/vk.mjs +3 -2
  98. package/dist/social-providers/wechat.d.mts +3 -1
  99. package/dist/social-providers/wechat.mjs +7 -1
  100. package/dist/social-providers/zoom.d.mts +4 -2
  101. package/dist/social-providers/zoom.mjs +10 -17
  102. package/dist/types/context.d.mts +23 -3
  103. package/dist/types/init-options.d.mts +29 -5
  104. package/dist/utils/ip.d.mts +5 -4
  105. package/dist/utils/ip.mjs +3 -3
  106. package/dist/utils/redirect-uri.d.mts +20 -0
  107. package/dist/utils/redirect-uri.mjs +48 -0
  108. package/dist/utils/string.d.mts +5 -1
  109. package/dist/utils/string.mjs +20 -1
  110. package/dist/utils/url.d.mts +18 -1
  111. package/dist/utils/url.mjs +30 -1
  112. package/package.json +9 -8
  113. package/src/db/adapter/factory.ts +118 -0
  114. package/src/db/adapter/index.ts +32 -0
  115. package/src/db/adapter/types.ts +1 -0
  116. package/src/db/type.ts +12 -0
  117. package/src/error/codes.ts +1 -0
  118. package/src/oauth2/authorization-params.ts +28 -0
  119. package/src/oauth2/basic-credentials.ts +87 -0
  120. package/src/oauth2/client-assertion.ts +131 -58
  121. package/src/oauth2/client-credentials-token.ts +48 -72
  122. package/src/oauth2/create-authorization-url.ts +28 -6
  123. package/src/oauth2/index.ts +25 -9
  124. package/src/oauth2/oauth-provider.ts +21 -2
  125. package/src/oauth2/refresh-access-token.ts +50 -76
  126. package/src/oauth2/token-endpoint-auth.ts +221 -0
  127. package/src/oauth2/utils.ts +19 -0
  128. package/src/oauth2/validate-authorization-code.ts +55 -85
  129. package/src/oauth2/verify.ts +20 -4
  130. package/src/social-providers/apple.ts +26 -2
  131. package/src/social-providers/atlassian.ts +8 -1
  132. package/src/social-providers/cognito.ts +26 -1
  133. package/src/social-providers/discord.ts +21 -17
  134. package/src/social-providers/dropbox.ts +7 -5
  135. package/src/social-providers/facebook.ts +11 -6
  136. package/src/social-providers/figma.ts +8 -1
  137. package/src/social-providers/github.ts +4 -2
  138. package/src/social-providers/gitlab.ts +2 -0
  139. package/src/social-providers/google.ts +2 -0
  140. package/src/social-providers/huggingface.ts +8 -1
  141. package/src/social-providers/kakao.ts +2 -1
  142. package/src/social-providers/kick.ts +8 -1
  143. package/src/social-providers/line.ts +2 -0
  144. package/src/social-providers/linear.ts +8 -1
  145. package/src/social-providers/linkedin.ts +2 -0
  146. package/src/social-providers/microsoft-entra-id.ts +1 -0
  147. package/src/social-providers/naver.ts +2 -1
  148. package/src/social-providers/notion.ts +8 -1
  149. package/src/social-providers/paybin.ts +2 -0
  150. package/src/social-providers/paypal.ts +7 -1
  151. package/src/social-providers/polar.ts +8 -1
  152. package/src/social-providers/railway.ts +8 -1
  153. package/src/social-providers/reddit.ts +2 -1
  154. package/src/social-providers/roblox.ts +16 -11
  155. package/src/social-providers/salesforce.ts +8 -1
  156. package/src/social-providers/slack.ts +15 -9
  157. package/src/social-providers/spotify.ts +8 -1
  158. package/src/social-providers/tiktok.ts +22 -9
  159. package/src/social-providers/twitch.ts +2 -1
  160. package/src/social-providers/twitter.ts +1 -0
  161. package/src/social-providers/vercel.ts +8 -1
  162. package/src/social-providers/vk.ts +8 -1
  163. package/src/social-providers/wechat.ts +9 -1
  164. package/src/social-providers/zoom.ts +15 -19
  165. package/src/types/context.ts +25 -4
  166. package/src/types/init-options.ts +29 -5
  167. package/src/utils/ip.ts +12 -13
  168. package/src/utils/redirect-uri.ts +54 -0
  169. package/src/utils/string.ts +37 -0
  170. package/src/utils/url.ts +28 -0
@@ -77,12 +77,35 @@ export interface AppleOptions extends ProviderOptions<AppleProfile> {
77
77
  audience?: (string | string[]) | undefined;
78
78
  }
79
79
 
80
+ async function sha256Hex(value: string) {
81
+ const data = new TextEncoder().encode(value);
82
+ const digest = await crypto.subtle.digest("SHA-256", data);
83
+ return Array.from(new Uint8Array(digest))
84
+ .map((byte) => byte.toString(16).padStart(2, "0"))
85
+ .join("");
86
+ }
87
+
88
+ async function nonceMatches(jwtNonce: unknown, nonce: string) {
89
+ if (typeof jwtNonce !== "string") {
90
+ return false;
91
+ }
92
+ if (jwtNonce === nonce) {
93
+ return true;
94
+ }
95
+ return jwtNonce === (await sha256Hex(nonce));
96
+ }
97
+
80
98
  export const apple = (options: AppleOptions) => {
81
99
  const tokenEndpoint = "https://appleid.apple.com/auth/token";
82
100
  return {
83
101
  id: "apple",
84
102
  name: "Apple",
85
- async createAuthorizationURL({ state, scopes, redirectURI }) {
103
+ async createAuthorizationURL({
104
+ state,
105
+ scopes,
106
+ redirectURI,
107
+ additionalParams,
108
+ }) {
86
109
  if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
87
110
  logger.error(
88
111
  "Client ID and client secret are required for Apple. Make sure to provide them in the options.",
@@ -101,6 +124,7 @@ export const apple = (options: AppleOptions) => {
101
124
  redirectURI,
102
125
  responseMode: "form_post",
103
126
  responseType: "code id_token",
127
+ additionalParams,
104
128
  });
105
129
  return url;
106
130
  },
@@ -141,7 +165,7 @@ export const apple = (options: AppleOptions) => {
141
165
  jwtClaims[field] = Boolean(jwtClaims[field]);
142
166
  }
143
167
  });
144
- if (nonce && jwtClaims.nonce !== nonce) {
168
+ if (nonce && !(await nonceMatches(jwtClaims.nonce, nonce))) {
145
169
  return false;
146
170
  }
147
171
  return !!jwtClaims;
@@ -35,7 +35,13 @@ export const atlassian = (options: AtlassianOptions) => {
35
35
  id: "atlassian",
36
36
  name: "Atlassian",
37
37
 
38
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
38
+ async createAuthorizationURL({
39
+ state,
40
+ scopes,
41
+ codeVerifier,
42
+ redirectURI,
43
+ additionalParams,
44
+ }) {
39
45
  if (!options.clientId || !options.clientSecret) {
40
46
  logger.error("Client Id and Secret are required for Atlassian");
41
47
  throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
@@ -59,6 +65,7 @@ export const atlassian = (options: AtlassianOptions) => {
59
65
  codeVerifier,
60
66
  redirectURI,
61
67
  additionalParams: {
68
+ ...(additionalParams ?? {}),
62
69
  audience: "api.atlassian.com",
63
70
  },
64
71
  prompt: options.prompt,
@@ -42,6 +42,19 @@ export interface CognitoOptions extends ProviderOptions<CognitoProfile> {
42
42
  region: string;
43
43
  userPoolId: string;
44
44
  requireClientSecret?: boolean | undefined;
45
+ /**
46
+ * Skip the Cognito hosted-UI identity-provider picker by preselecting an
47
+ * IdP (maps to the `identity_provider` query parameter on the authorize
48
+ * request). Accepts `"COGNITO"`, a SAML/OIDC provider name configured on
49
+ * the User Pool, or one of the social providers (`"Google"`, `"Facebook"`,
50
+ * `"LoginWithAmazon"`, `"SignInWithApple"`).
51
+ *
52
+ * Per-request overrides via `signIn.social({ additionalParams: { identity_provider } })`
53
+ * take precedence over this value.
54
+ *
55
+ * @see https://docs.aws.amazon.com/cognito/latest/developerguide/authorization-endpoint.html
56
+ */
57
+ identityProvider?: string | undefined;
45
58
  }
46
59
 
47
60
  export const cognito = (options: CognitoOptions) => {
@@ -60,7 +73,13 @@ export const cognito = (options: CognitoOptions) => {
60
73
  return {
61
74
  id: "cognito",
62
75
  name: "Cognito",
63
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
76
+ async createAuthorizationURL({
77
+ state,
78
+ scopes,
79
+ codeVerifier,
80
+ redirectURI,
81
+ additionalParams,
82
+ }) {
64
83
  if (!getPrimaryClientId(options.clientId)) {
65
84
  logger.error(
66
85
  "ClientId is required for Amazon Cognito. Make sure to provide them in the options.",
@@ -91,6 +110,12 @@ export const cognito = (options: CognitoOptions) => {
91
110
  codeVerifier,
92
111
  redirectURI,
93
112
  prompt: options.prompt,
113
+ additionalParams: {
114
+ ...(options.identityProvider
115
+ ? { identity_provider: options.identityProvider }
116
+ : {}),
117
+ ...(additionalParams ?? {}),
118
+ },
94
119
  });
95
120
  // AWS Cognito requires scopes to be encoded with %20 instead of +
96
121
  // URLSearchParams encodes spaces as + by default, so we need to fix this
@@ -1,6 +1,10 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
2
  import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
- import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
3
+ import {
4
+ createAuthorizationURL,
5
+ refreshAccessToken,
6
+ validateAuthorizationCode,
7
+ } from "../oauth2";
4
8
  export interface DiscordProfile extends Record<string, any> {
5
9
  /** the user's id (i.e. the numerical snowflake) */
6
10
  id: string;
@@ -84,26 +88,26 @@ export const discord = (options: DiscordOptions) => {
84
88
  return {
85
89
  id: "discord",
86
90
  name: "Discord",
87
- createAuthorizationURL({ state, scopes, redirectURI }) {
91
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
88
92
  const _scopes = options.disableDefaultScope ? [] : ["identify", "email"];
89
93
  if (scopes) _scopes.push(...scopes);
90
94
  if (options.scope) _scopes.push(...options.scope);
91
95
  const hasBotScope = _scopes.includes("bot");
92
- const permissionsParam =
93
- hasBotScope && options.permissions !== undefined
94
- ? `&permissions=${options.permissions}`
95
- : "";
96
- return new URL(
97
- `https://discord.com/api/oauth2/authorize?scope=${_scopes.join(
98
- "+",
99
- )}&response_type=code&client_id=${
100
- options.clientId
101
- }&redirect_uri=${encodeURIComponent(
102
- options.redirectURI || redirectURI,
103
- )}&state=${state}&prompt=${
104
- options.prompt || "none"
105
- }${permissionsParam}`,
106
- );
96
+ return createAuthorizationURL({
97
+ id: "discord",
98
+ options,
99
+ authorizationEndpoint: "https://discord.com/api/oauth2/authorize",
100
+ scopes: _scopes,
101
+ state,
102
+ redirectURI,
103
+ prompt: options.prompt || "none",
104
+ additionalParams: {
105
+ ...(hasBotScope && options.permissions !== undefined
106
+ ? { permissions: String(options.permissions) }
107
+ : {}),
108
+ ...(additionalParams ?? {}),
109
+ },
110
+ });
107
111
  },
108
112
  validateAuthorizationCode: async ({ code, redirectURI }) => {
109
113
  return validateAuthorizationCode({
@@ -36,14 +36,11 @@ export const dropbox = (options: DropboxOptions) => {
36
36
  scopes,
37
37
  codeVerifier,
38
38
  redirectURI,
39
+ additionalParams,
39
40
  }) => {
40
41
  const _scopes = options.disableDefaultScope ? [] : ["account_info.read"];
41
42
  if (options.scope) _scopes.push(...options.scope);
42
43
  if (scopes) _scopes.push(...scopes);
43
- const additionalParams: Record<string, string> = {};
44
- if (options.accessType) {
45
- additionalParams.token_access_type = options.accessType;
46
- }
47
44
  return await createAuthorizationURL({
48
45
  id: "dropbox",
49
46
  options,
@@ -52,7 +49,12 @@ export const dropbox = (options: DropboxOptions) => {
52
49
  state,
53
50
  redirectURI,
54
51
  codeVerifier,
55
- additionalParams,
52
+ additionalParams: {
53
+ ...(options.accessType
54
+ ? { token_access_type: options.accessType }
55
+ : {}),
56
+ ...(additionalParams ?? {}),
57
+ },
56
58
  });
57
59
  },
58
60
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -43,7 +43,13 @@ export const facebook = (options: FacebookOptions) => {
43
43
  return {
44
44
  id: "facebook",
45
45
  name: "Facebook",
46
- async createAuthorizationURL({ state, scopes, redirectURI, loginHint }) {
46
+ async createAuthorizationURL({
47
+ state,
48
+ scopes,
49
+ redirectURI,
50
+ loginHint,
51
+ additionalParams,
52
+ }) {
47
53
  if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
48
54
  logger.error(
49
55
  "Client ID and client secret are required for Facebook. Make sure to provide them in the options.",
@@ -63,11 +69,10 @@ export const facebook = (options: FacebookOptions) => {
63
69
  state,
64
70
  redirectURI,
65
71
  loginHint,
66
- additionalParams: options.configId
67
- ? {
68
- config_id: options.configId,
69
- }
70
- : {},
72
+ additionalParams: {
73
+ ...(options.configId ? { config_id: options.configId } : {}),
74
+ ...(additionalParams ?? {}),
75
+ },
71
76
  });
72
77
  },
73
78
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -24,7 +24,13 @@ export const figma = (options: FigmaOptions) => {
24
24
  return {
25
25
  id: "figma",
26
26
  name: "Figma",
27
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
27
+ async createAuthorizationURL({
28
+ state,
29
+ scopes,
30
+ codeVerifier,
31
+ redirectURI,
32
+ additionalParams,
33
+ }) {
28
34
  if (!options.clientId || !options.clientSecret) {
29
35
  logger.error(
30
36
  "Client Id and Client Secret are required for Figma. Make sure to provide them in the options.",
@@ -47,6 +53,7 @@ export const figma = (options: FigmaOptions) => {
47
53
  state,
48
54
  codeVerifier,
49
55
  redirectURI,
56
+ additionalParams,
50
57
  });
51
58
 
52
59
  return url;
@@ -6,7 +6,7 @@ import {
6
6
  getOAuth2Tokens,
7
7
  refreshAccessToken,
8
8
  } from "../oauth2";
9
- import { createAuthorizationCodeRequest } from "../oauth2/validate-authorization-code";
9
+ import { authorizationCodeRequest } from "../oauth2/validate-authorization-code";
10
10
 
11
11
  export interface GithubProfile {
12
12
  login: string;
@@ -69,6 +69,7 @@ export const github = (options: GithubOptions) => {
69
69
  loginHint,
70
70
  codeVerifier,
71
71
  redirectURI,
72
+ additionalParams,
72
73
  }) {
73
74
  const _scopes = options.disableDefaultScope
74
75
  ? []
@@ -85,10 +86,11 @@ export const github = (options: GithubOptions) => {
85
86
  redirectURI,
86
87
  loginHint,
87
88
  prompt: options.prompt,
89
+ additionalParams,
88
90
  });
89
91
  },
90
92
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
91
- const { body, headers: requestHeaders } = createAuthorizationCodeRequest({
93
+ const { body, headers: requestHeaders } = await authorizationCodeRequest({
92
94
  code,
93
95
  codeVerifier,
94
96
  redirectURI,
@@ -87,6 +87,7 @@ export const gitlab = (options: GitlabOptions) => {
87
87
  codeVerifier,
88
88
  loginHint,
89
89
  redirectURI,
90
+ additionalParams,
90
91
  }) => {
91
92
  const _scopes = options.disableDefaultScope ? [] : ["read_user"];
92
93
  if (options.scope) _scopes.push(...options.scope);
@@ -100,6 +101,7 @@ export const gitlab = (options: GitlabOptions) => {
100
101
  redirectURI,
101
102
  codeVerifier,
102
103
  loginHint,
104
+ additionalParams,
103
105
  });
104
106
  },
105
107
  validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
@@ -64,6 +64,7 @@ export const google = (options: GoogleOptions) => {
64
64
  redirectURI,
65
65
  loginHint,
66
66
  display,
67
+ additionalParams,
67
68
  }) {
68
69
  if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
69
70
  logger.error(
@@ -94,6 +95,7 @@ export const google = (options: GoogleOptions) => {
94
95
  hd: options.hd,
95
96
  additionalParams: {
96
97
  include_granted_scopes: "true",
98
+ ...(additionalParams ?? {}),
97
99
  },
98
100
  });
99
101
  return url;
@@ -47,7 +47,13 @@ export const huggingface = (options: HuggingFaceOptions) => {
47
47
  return {
48
48
  id: "huggingface",
49
49
  name: "Hugging Face",
50
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
50
+ createAuthorizationURL({
51
+ state,
52
+ scopes,
53
+ codeVerifier,
54
+ redirectURI,
55
+ additionalParams,
56
+ }) {
51
57
  const _scopes = options.disableDefaultScope
52
58
  ? []
53
59
  : ["openid", "profile", "email"];
@@ -61,6 +67,7 @@ export const huggingface = (options: HuggingFaceOptions) => {
61
67
  state,
62
68
  codeVerifier,
63
69
  redirectURI,
70
+ additionalParams,
64
71
  });
65
72
  },
66
73
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -106,7 +106,7 @@ export const kakao = (options: KakaoOptions) => {
106
106
  return {
107
107
  id: "kakao",
108
108
  name: "Kakao",
109
- createAuthorizationURL({ state, scopes, redirectURI }) {
109
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
110
110
  const _scopes = options.disableDefaultScope
111
111
  ? []
112
112
  : ["account_email", "profile_image", "profile_nickname"];
@@ -119,6 +119,7 @@ export const kakao = (options: KakaoOptions) => {
119
119
  scopes: _scopes,
120
120
  state,
121
121
  redirectURI,
122
+ additionalParams,
122
123
  });
123
124
  },
124
125
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -33,7 +33,13 @@ export const kick = (options: KickOptions) => {
33
33
  return {
34
34
  id: "kick",
35
35
  name: "Kick",
36
- createAuthorizationURL({ state, scopes, redirectURI, codeVerifier }) {
36
+ createAuthorizationURL({
37
+ state,
38
+ scopes,
39
+ redirectURI,
40
+ codeVerifier,
41
+ additionalParams,
42
+ }) {
37
43
  const _scopes = options.disableDefaultScope ? [] : ["user:read"];
38
44
  if (options.scope) _scopes.push(...options.scope);
39
45
  if (scopes) _scopes.push(...scopes);
@@ -46,6 +52,7 @@ export const kick = (options: KickOptions) => {
46
52
  scopes: _scopes,
47
53
  codeVerifier,
48
54
  state,
55
+ additionalParams,
49
56
  });
50
57
  },
51
58
  async validateAuthorizationCode({ code, redirectURI, codeVerifier }) {
@@ -56,6 +56,7 @@ export const line = (options: LineOptions) => {
56
56
  codeVerifier,
57
57
  redirectURI,
58
58
  loginHint,
59
+ additionalParams,
59
60
  }) {
60
61
  const _scopes = options.disableDefaultScope
61
62
  ? []
@@ -71,6 +72,7 @@ export const line = (options: LineOptions) => {
71
72
  codeVerifier,
72
73
  redirectURI,
73
74
  loginHint,
75
+ additionalParams,
74
76
  });
75
77
  },
76
78
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -31,7 +31,13 @@ export const linear = (options: LinearOptions) => {
31
31
  return {
32
32
  id: "linear",
33
33
  name: "Linear",
34
- createAuthorizationURL({ state, scopes, loginHint, redirectURI }) {
34
+ createAuthorizationURL({
35
+ state,
36
+ scopes,
37
+ loginHint,
38
+ redirectURI,
39
+ additionalParams,
40
+ }) {
35
41
  const _scopes = options.disableDefaultScope ? [] : ["read"];
36
42
  if (options.scope) _scopes.push(...options.scope);
37
43
  if (scopes) _scopes.push(...scopes);
@@ -43,6 +49,7 @@ export const linear = (options: LinearOptions) => {
43
49
  state,
44
50
  redirectURI,
45
51
  loginHint,
52
+ additionalParams,
46
53
  });
47
54
  },
48
55
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -37,6 +37,7 @@ export const linkedin = (options: LinkedInOptions) => {
37
37
  scopes,
38
38
  redirectURI,
39
39
  loginHint,
40
+ additionalParams,
40
41
  }) => {
41
42
  const _scopes = options.disableDefaultScope
42
43
  ? []
@@ -51,6 +52,7 @@ export const linkedin = (options: LinkedInOptions) => {
51
52
  state,
52
53
  loginHint,
53
54
  redirectURI,
55
+ additionalParams,
54
56
  });
55
57
  },
56
58
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -174,6 +174,7 @@ export const microsoft = (options: MicrosoftOptions) => {
174
174
  redirectURI: data.redirectURI,
175
175
  prompt: options.prompt,
176
176
  loginHint: data.loginHint,
177
+ additionalParams: data.additionalParams,
177
178
  });
178
179
  },
179
180
  validateAuthorizationCode({ code, codeVerifier, redirectURI }) {
@@ -44,7 +44,7 @@ export const naver = (options: NaverOptions) => {
44
44
  return {
45
45
  id: "naver",
46
46
  name: "Naver",
47
- createAuthorizationURL({ state, scopes, redirectURI }) {
47
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
48
48
  const _scopes = options.disableDefaultScope ? [] : ["profile", "email"];
49
49
  if (options.scope) _scopes.push(...options.scope);
50
50
  if (scopes) _scopes.push(...scopes);
@@ -55,6 +55,7 @@ export const naver = (options: NaverOptions) => {
55
55
  scopes: _scopes,
56
56
  state,
57
57
  redirectURI,
58
+ additionalParams,
58
59
  });
59
60
  },
60
61
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -28,7 +28,13 @@ export const notion = (options: NotionOptions) => {
28
28
  return {
29
29
  id: "notion",
30
30
  name: "Notion",
31
- createAuthorizationURL({ state, scopes, loginHint, redirectURI }) {
31
+ createAuthorizationURL({
32
+ state,
33
+ scopes,
34
+ loginHint,
35
+ redirectURI,
36
+ additionalParams,
37
+ }) {
32
38
  const _scopes: string[] = options.disableDefaultScope ? [] : [];
33
39
  if (options.scope) _scopes.push(...options.scope);
34
40
  if (scopes) _scopes.push(...scopes);
@@ -41,6 +47,7 @@ export const notion = (options: NotionOptions) => {
41
47
  redirectURI,
42
48
  loginHint,
43
49
  additionalParams: {
50
+ ...(additionalParams ?? {}),
44
51
  owner: "user",
45
52
  },
46
53
  });
@@ -42,6 +42,7 @@ export const paybin = (options: PaybinOptions) => {
42
42
  codeVerifier,
43
43
  redirectURI,
44
44
  loginHint,
45
+ additionalParams,
45
46
  }) {
46
47
  if (!options.clientId || !options.clientSecret) {
47
48
  logger.error(
@@ -67,6 +68,7 @@ export const paybin = (options: PaybinOptions) => {
67
68
  redirectURI,
68
69
  prompt: options.prompt,
69
70
  loginHint,
71
+ additionalParams,
70
72
  });
71
73
  return url;
72
74
  },
@@ -78,7 +78,12 @@ export const paypal = (options: PayPalOptions) => {
78
78
  return {
79
79
  id: "paypal",
80
80
  name: "PayPal",
81
- async createAuthorizationURL({ state, codeVerifier, redirectURI }) {
81
+ async createAuthorizationURL({
82
+ state,
83
+ codeVerifier,
84
+ redirectURI,
85
+ additionalParams,
86
+ }) {
82
87
  if (!options.clientId || !options.clientSecret) {
83
88
  logger.error(
84
89
  "Client Id and Client Secret is required for PayPal. Make sure to provide them in the options.",
@@ -103,6 +108,7 @@ export const paypal = (options: PayPalOptions) => {
103
108
  codeVerifier,
104
109
  redirectURI,
105
110
  prompt: options.prompt,
111
+ additionalParams,
106
112
  });
107
113
  return url;
108
114
  },
@@ -37,7 +37,13 @@ export const polar = (options: PolarOptions) => {
37
37
  return {
38
38
  id: "polar",
39
39
  name: "Polar",
40
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
40
+ createAuthorizationURL({
41
+ state,
42
+ scopes,
43
+ codeVerifier,
44
+ redirectURI,
45
+ additionalParams,
46
+ }) {
41
47
  const _scopes = options.disableDefaultScope
42
48
  ? []
43
49
  : ["openid", "profile", "email"];
@@ -52,6 +58,7 @@ export const polar = (options: PolarOptions) => {
52
58
  codeVerifier,
53
59
  redirectURI,
54
60
  prompt: options.prompt,
61
+ additionalParams,
55
62
  });
56
63
  },
57
64
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -29,7 +29,13 @@ export const railway = (options: RailwayOptions) => {
29
29
  return {
30
30
  id: "railway",
31
31
  name: "Railway",
32
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
32
+ createAuthorizationURL({
33
+ state,
34
+ scopes,
35
+ codeVerifier,
36
+ redirectURI,
37
+ additionalParams,
38
+ }) {
33
39
  const _scopes = options.disableDefaultScope
34
40
  ? []
35
41
  : ["openid", "email", "profile"];
@@ -43,6 +49,7 @@ export const railway = (options: RailwayOptions) => {
43
49
  state,
44
50
  codeVerifier,
45
51
  redirectURI,
52
+ additionalParams,
46
53
  });
47
54
  },
48
55
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -25,7 +25,7 @@ export const reddit = (options: RedditOptions) => {
25
25
  return {
26
26
  id: "reddit",
27
27
  name: "Reddit",
28
- createAuthorizationURL({ state, scopes, redirectURI }) {
28
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
29
29
  const _scopes = options.disableDefaultScope ? [] : ["identity"];
30
30
  if (options.scope) _scopes.push(...options.scope);
31
31
  if (scopes) _scopes.push(...scopes);
@@ -37,6 +37,7 @@ export const reddit = (options: RedditOptions) => {
37
37
  state,
38
38
  redirectURI,
39
39
  duration: options.duration,
40
+ additionalParams,
40
41
  });
41
42
  },
42
43
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -1,6 +1,10 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
2
  import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
- import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
3
+ import {
4
+ createAuthorizationURL,
5
+ refreshAccessToken,
6
+ validateAuthorizationCode,
7
+ } from "../oauth2";
4
8
 
5
9
  export interface RobloxProfile extends Record<string, any> {
6
10
  /** the user's id */
@@ -37,19 +41,20 @@ export const roblox = (options: RobloxOptions) => {
37
41
  return {
38
42
  id: "roblox",
39
43
  name: "Roblox",
40
- createAuthorizationURL({ state, scopes, redirectURI }) {
44
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
41
45
  const _scopes = options.disableDefaultScope ? [] : ["openid", "profile"];
42
46
  if (options.scope) _scopes.push(...options.scope);
43
47
  if (scopes) _scopes.push(...scopes);
44
- return new URL(
45
- `https://apis.roblox.com/oauth/v1/authorize?scope=${_scopes.join(
46
- "+",
47
- )}&response_type=code&client_id=${
48
- options.clientId
49
- }&redirect_uri=${encodeURIComponent(
50
- options.redirectURI || redirectURI,
51
- )}&state=${state}&prompt=${options.prompt || "select_account consent"}`,
52
- );
48
+ return createAuthorizationURL({
49
+ id: "roblox",
50
+ options,
51
+ authorizationEndpoint: "https://apis.roblox.com/oauth/v1/authorize",
52
+ scopes: _scopes,
53
+ state,
54
+ redirectURI,
55
+ prompt: options.prompt || "select_account consent",
56
+ additionalParams,
57
+ });
53
58
  },
54
59
  validateAuthorizationCode: async ({ code, redirectURI }) => {
55
60
  return validateAuthorizationCode({
@@ -64,7 +64,13 @@ export const salesforce = (options: SalesforceOptions) => {
64
64
  id: "salesforce",
65
65
  name: "Salesforce",
66
66
 
67
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
67
+ async createAuthorizationURL({
68
+ state,
69
+ scopes,
70
+ codeVerifier,
71
+ redirectURI,
72
+ additionalParams,
73
+ }) {
68
74
  if (!options.clientId || !options.clientSecret) {
69
75
  logger.error(
70
76
  "Client Id and Client Secret are required for Salesforce. Make sure to provide them in the options.",
@@ -89,6 +95,7 @@ export const salesforce = (options: SalesforceOptions) => {
89
95
  state,
90
96
  codeVerifier,
91
97
  redirectURI: options.redirectURI || redirectURI,
98
+ additionalParams,
92
99
  });
93
100
  },
94
101