@better-auth/core 1.7.0-beta.2 → 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 (172) hide show
  1. package/dist/context/global.mjs +1 -1
  2. package/dist/db/adapter/factory.mjs +64 -3
  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 +5 -12
  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 +5 -3
  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 +5 -3
  42. package/dist/social-providers/facebook.mjs +6 -3
  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 +4 -2
  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 +5 -3
  63. package/dist/social-providers/linkedin.mjs +4 -3
  64. package/dist/social-providers/microsoft-entra-id.d.mts +3 -2
  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 +30 -4
  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 +121 -3
  114. package/src/db/adapter/index.ts +32 -0
  115. package/src/db/adapter/types.ts +1 -0
  116. package/src/db/get-tables.ts +2 -0
  117. package/src/db/schema/user.ts +3 -0
  118. package/src/db/type.ts +12 -0
  119. package/src/error/codes.ts +1 -0
  120. package/src/oauth2/authorization-params.ts +28 -0
  121. package/src/oauth2/basic-credentials.ts +87 -0
  122. package/src/oauth2/client-assertion.ts +131 -58
  123. package/src/oauth2/client-credentials-token.ts +48 -72
  124. package/src/oauth2/create-authorization-url.ts +28 -6
  125. package/src/oauth2/index.ts +25 -9
  126. package/src/oauth2/oauth-provider.ts +21 -2
  127. package/src/oauth2/refresh-access-token.ts +50 -76
  128. package/src/oauth2/token-endpoint-auth.ts +221 -0
  129. package/src/oauth2/utils.ts +19 -0
  130. package/src/oauth2/validate-authorization-code.ts +55 -85
  131. package/src/oauth2/verify.ts +20 -4
  132. package/src/social-providers/apple.ts +27 -3
  133. package/src/social-providers/atlassian.ts +8 -1
  134. package/src/social-providers/cognito.ts +26 -1
  135. package/src/social-providers/discord.ts +22 -18
  136. package/src/social-providers/dropbox.ts +7 -5
  137. package/src/social-providers/facebook.ts +14 -9
  138. package/src/social-providers/figma.ts +8 -1
  139. package/src/social-providers/github.ts +5 -3
  140. package/src/social-providers/gitlab.ts +2 -0
  141. package/src/social-providers/google.ts +2 -0
  142. package/src/social-providers/huggingface.ts +8 -1
  143. package/src/social-providers/kakao.ts +2 -1
  144. package/src/social-providers/kick.ts +8 -1
  145. package/src/social-providers/line.ts +2 -0
  146. package/src/social-providers/linear.ts +8 -1
  147. package/src/social-providers/linkedin.ts +5 -3
  148. package/src/social-providers/microsoft-entra-id.ts +2 -1
  149. package/src/social-providers/naver.ts +2 -1
  150. package/src/social-providers/notion.ts +8 -1
  151. package/src/social-providers/paybin.ts +2 -0
  152. package/src/social-providers/paypal.ts +7 -1
  153. package/src/social-providers/polar.ts +8 -1
  154. package/src/social-providers/railway.ts +8 -1
  155. package/src/social-providers/reddit.ts +2 -1
  156. package/src/social-providers/roblox.ts +16 -11
  157. package/src/social-providers/salesforce.ts +8 -1
  158. package/src/social-providers/slack.ts +15 -9
  159. package/src/social-providers/spotify.ts +8 -1
  160. package/src/social-providers/tiktok.ts +22 -9
  161. package/src/social-providers/twitch.ts +2 -1
  162. package/src/social-providers/twitter.ts +1 -0
  163. package/src/social-providers/vercel.ts +8 -1
  164. package/src/social-providers/vk.ts +8 -1
  165. package/src/social-providers/wechat.ts +9 -1
  166. package/src/social-providers/zoom.ts +15 -19
  167. package/src/types/context.ts +33 -5
  168. package/src/types/init-options.ts +29 -5
  169. package/src/utils/ip.ts +12 -13
  170. package/src/utils/redirect-uri.ts +54 -0
  171. package/src/utils/string.ts +37 -0
  172. package/src/utils/url.ts +28 -0
@@ -1,30 +1,25 @@
1
- import { resolveAssertionParams } from "./client-assertion.mjs";
2
- import { base64 } from "@better-auth/utils/base64";
1
+ import { applyTokenEndpointAuth } from "./token-endpoint-auth.mjs";
3
2
  import { betterFetch } from "@better-fetch/fetch";
4
3
  //#region src/oauth2/client-credentials-token.ts
5
- async function clientCredentialsTokenRequest({ options, scope, authentication, clientAssertion, tokenEndpoint, resource }) {
4
+ async function clientCredentialsTokenRequest({ options, scope, authentication, tokenEndpointAuth, tokenEndpoint, resource }) {
6
5
  options = typeof options === "function" ? await options() : options;
7
- let extraParams;
8
- if (authentication === "private_key_jwt") {
9
- if (!clientAssertion) throw new Error("private_key_jwt authentication requires a clientAssertion configuration");
10
- extraParams = await resolveAssertionParams({
11
- clientAssertion,
12
- clientId: Array.isArray(options.clientId) ? options.clientId[0] : options.clientId,
13
- tokenEndpoint
14
- });
15
- }
16
- return createClientCredentialsTokenRequest({
6
+ const request = buildClientCredentialsTokenRequest({
17
7
  options,
18
8
  scope,
19
- authentication,
20
- resource,
21
- extraParams
9
+ resource
22
10
  });
11
+ await applyTokenEndpointAuth({
12
+ body: request.body,
13
+ headers: request.headers,
14
+ options,
15
+ tokenEndpoint: tokenEndpoint ?? "",
16
+ grantType: "client_credentials",
17
+ tokenEndpointAuth,
18
+ authentication
19
+ });
20
+ return request;
23
21
  }
24
- /**
25
- * @deprecated use async'd clientCredentialsTokenRequest instead
26
- */
27
- function createClientCredentialsTokenRequest({ options, scope, authentication, resource, extraParams }) {
22
+ function buildClientCredentialsTokenRequest({ options, scope, resource, extraParams }) {
28
23
  const body = new URLSearchParams();
29
24
  const headers = {
30
25
  "content-type": "application/x-www-form-urlencoded",
@@ -34,12 +29,6 @@ function createClientCredentialsTokenRequest({ options, scope, authentication, r
34
29
  scope && body.set("scope", scope);
35
30
  if (resource) if (typeof resource === "string") body.append("resource", resource);
36
31
  else for (const _resource of resource) body.append("resource", _resource);
37
- const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
38
- if (authentication === "basic") headers["authorization"] = `Basic ${base64.encode(`${primaryClientId}:${options.clientSecret ?? ""}`)}`;
39
- else {
40
- body.set("client_id", primaryClientId);
41
- if (authentication !== "private_key_jwt" && options.clientSecret) body.set("client_secret", options.clientSecret);
42
- }
43
32
  if (extraParams) {
44
33
  for (const [key, value] of Object.entries(extraParams)) if (!body.has(key)) body.append(key, value);
45
34
  }
@@ -48,12 +37,12 @@ function createClientCredentialsTokenRequest({ options, scope, authentication, r
48
37
  headers
49
38
  };
50
39
  }
51
- async function clientCredentialsToken({ options, tokenEndpoint, scope, authentication, clientAssertion, resource }) {
40
+ async function clientCredentialsToken({ options, tokenEndpoint, scope, authentication, tokenEndpointAuth, resource }) {
52
41
  const { body, headers } = await clientCredentialsTokenRequest({
53
42
  options,
54
43
  scope,
55
44
  authentication,
56
- clientAssertion,
45
+ tokenEndpointAuth,
57
46
  tokenEndpoint,
58
47
  resource
59
48
  });
@@ -75,4 +64,4 @@ async function clientCredentialsToken({ options, tokenEndpoint, scope, authentic
75
64
  return tokens;
76
65
  }
77
66
  //#endregion
78
- export { clientCredentialsToken, clientCredentialsTokenRequest, createClientCredentialsTokenRequest };
67
+ export { clientCredentialsToken, clientCredentialsTokenRequest };
@@ -1,6 +1,14 @@
1
1
  import { AwaitableFunction } from "../types/helper.mjs";
2
2
  import { ProviderOptions } from "./oauth-provider.mjs";
3
3
  //#region src/oauth2/create-authorization-url.d.ts
4
+ /**
5
+ * Query-parameter names that are populated by the framework as part of the
6
+ * authorization request and must not be overridden by caller-supplied
7
+ * `additionalParams`. Overriding `state`, PKCE, or `redirect_uri` would
8
+ * break the callback correlation and session pinning guarantees.
9
+ */
10
+ declare const RESERVED_AUTHORIZATION_PARAMS: readonly ["state", "client_id", "redirect_uri", "response_type", "code_challenge", "code_challenge_method", "scope"];
11
+ declare const RESERVED_AUTHORIZATION_PARAMS_SET: ReadonlySet<string>;
4
12
  declare function createAuthorizationURL({
5
13
  id,
6
14
  options,
@@ -41,4 +49,4 @@ declare function createAuthorizationURL({
41
49
  scopeJoiner?: string | undefined;
42
50
  }): Promise<URL>;
43
51
  //#endregion
44
- export { createAuthorizationURL };
52
+ export { RESERVED_AUTHORIZATION_PARAMS, RESERVED_AUTHORIZATION_PARAMS_SET, createAuthorizationURL };
@@ -1,10 +1,27 @@
1
- import { generateCodeChallenge } from "./utils.mjs";
1
+ import { generateCodeChallenge, getPrimaryClientId } from "./utils.mjs";
2
2
  //#region src/oauth2/create-authorization-url.ts
3
+ /**
4
+ * Query-parameter names that are populated by the framework as part of the
5
+ * authorization request and must not be overridden by caller-supplied
6
+ * `additionalParams`. Overriding `state`, PKCE, or `redirect_uri` would
7
+ * break the callback correlation and session pinning guarantees.
8
+ */
9
+ const RESERVED_AUTHORIZATION_PARAMS = [
10
+ "state",
11
+ "client_id",
12
+ "redirect_uri",
13
+ "response_type",
14
+ "code_challenge",
15
+ "code_challenge_method",
16
+ "scope"
17
+ ];
18
+ const RESERVED_AUTHORIZATION_PARAMS_SET = new Set(RESERVED_AUTHORIZATION_PARAMS);
3
19
  async function createAuthorizationURL({ id, options, authorizationEndpoint, state, codeVerifier, scopes, claims, redirectURI, duration, prompt, accessType, responseType, display, loginHint, hd, responseMode, additionalParams, scopeJoiner }) {
4
20
  options = typeof options === "function" ? await options() : options;
5
21
  const url = new URL(options.authorizationEndpoint || authorizationEndpoint);
6
22
  url.searchParams.set("response_type", responseType || "code");
7
- const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
23
+ const primaryClientId = getPrimaryClientId(options.clientId);
24
+ if (!primaryClientId) throw new Error("OAuth provider requires clientId");
8
25
  url.searchParams.set("client_id", primaryClientId);
9
26
  url.searchParams.set("state", state);
10
27
  if (scopes) url.searchParams.set("scope", scopes.join(scopeJoiner || " "));
@@ -32,10 +49,11 @@ async function createAuthorizationURL({ id, options, authorizationEndpoint, stat
32
49
  ...claimsObj
33
50
  } }));
34
51
  }
35
- if (additionalParams) Object.entries(additionalParams).forEach(([key, value]) => {
52
+ if (additionalParams) for (const [key, value] of Object.entries(additionalParams)) {
53
+ if (RESERVED_AUTHORIZATION_PARAMS_SET.has(key)) continue;
36
54
  url.searchParams.set(key, value);
37
- });
55
+ }
38
56
  return url;
39
57
  }
40
58
  //#endregion
41
- export { createAuthorizationURL };
59
+ export { RESERVED_AUTHORIZATION_PARAMS, RESERVED_AUTHORIZATION_PARAMS_SET, createAuthorizationURL };
@@ -1,9 +1,12 @@
1
- import { ASSERTION_SIGNING_ALGORITHMS, AssertionSigningAlgorithm, CLIENT_ASSERTION_TYPE, ClientAssertionConfig, resolveAssertionParams, signClientAssertion } from "./client-assertion.mjs";
1
+ import { additionalAuthorizationParamsSchema } from "./authorization-params.mjs";
2
+ import { decodeBasicCredentials, encodeBasicCredentials } from "./basic-credentials.mjs";
3
+ import { CLIENT_ASSERTION_TYPE, ClientAssertionContext, ClientAssertionGetter, ClientAssertionGrantType, PRIVATE_KEY_JWT_SIGNING_ALGORITHMS, PrivateKeyJwtClientAssertionGetterOptions, PrivateKeyJwtSigningAlgorithm, createPrivateKeyJwtClientAssertionGetter, resolveClientAssertionParams, signPrivateKeyJwtClientAssertion } from "./client-assertion.mjs";
2
4
  import { OAuth2Tokens, OAuth2UserInfo, OAuthProvider, ProviderOptions } from "./oauth-provider.mjs";
3
- import { clientCredentialsToken, clientCredentialsTokenRequest, createClientCredentialsTokenRequest } from "./client-credentials-token.mjs";
4
- import { createAuthorizationURL } from "./create-authorization-url.mjs";
5
- import { createRefreshAccessTokenRequest, refreshAccessToken, refreshAccessTokenRequest } from "./refresh-access-token.mjs";
6
- import { generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId } from "./utils.mjs";
7
- import { authorizationCodeRequest, createAuthorizationCodeRequest, validateAuthorizationCode, validateToken } from "./validate-authorization-code.mjs";
5
+ import { TokenEndpointAuth, TokenEndpointAuthMethod, TokenEndpointSecretAuthentication } from "./token-endpoint-auth.mjs";
6
+ import { clientCredentialsToken, clientCredentialsTokenRequest } from "./client-credentials-token.mjs";
7
+ import { RESERVED_AUTHORIZATION_PARAMS, RESERVED_AUTHORIZATION_PARAMS_SET, createAuthorizationURL } from "./create-authorization-url.mjs";
8
+ import { refreshAccessToken, refreshAccessTokenRequest } from "./refresh-access-token.mjs";
9
+ import { applyDefaultAccessTokenExpiry, generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId } from "./utils.mjs";
10
+ import { authorizationCodeRequest, validateAuthorizationCode, validateToken } from "./validate-authorization-code.mjs";
8
11
  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, getPrimaryClientId, refreshAccessToken, refreshAccessTokenRequest, resolveAssertionParams, signClientAssertion, validateAuthorizationCode, validateToken, verifyAccessToken, verifyJwsAccessToken };
12
+ export { CLIENT_ASSERTION_TYPE, type ClientAssertionContext, type ClientAssertionGetter, type ClientAssertionGrantType, type OAuth2Tokens, type OAuth2UserInfo, type OAuthProvider, PRIVATE_KEY_JWT_SIGNING_ALGORITHMS, type PrivateKeyJwtClientAssertionGetterOptions, type PrivateKeyJwtSigningAlgorithm, type ProviderOptions, RESERVED_AUTHORIZATION_PARAMS, RESERVED_AUTHORIZATION_PARAMS_SET, type TokenEndpointAuth, type TokenEndpointAuthMethod, type TokenEndpointSecretAuthentication, additionalAuthorizationParamsSchema, applyDefaultAccessTokenExpiry, authorizationCodeRequest, clientCredentialsToken, clientCredentialsTokenRequest, createAuthorizationURL, createPrivateKeyJwtClientAssertionGetter, decodeBasicCredentials, encodeBasicCredentials, generateCodeChallenge, getJwks, getOAuth2Tokens, getPrimaryClientId, refreshAccessToken, refreshAccessTokenRequest, resolveClientAssertionParams, signPrivateKeyJwtClientAssertion, validateAuthorizationCode, validateToken, verifyAccessToken, verifyJwsAccessToken };
@@ -1,8 +1,10 @@
1
- import { ASSERTION_SIGNING_ALGORITHMS, CLIENT_ASSERTION_TYPE, resolveAssertionParams, signClientAssertion } from "./client-assertion.mjs";
2
- import { clientCredentialsToken, clientCredentialsTokenRequest, createClientCredentialsTokenRequest } from "./client-credentials-token.mjs";
3
- import { generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId } from "./utils.mjs";
4
- import { createAuthorizationURL } from "./create-authorization-url.mjs";
5
- import { createRefreshAccessTokenRequest, refreshAccessToken, refreshAccessTokenRequest } from "./refresh-access-token.mjs";
6
- import { authorizationCodeRequest, createAuthorizationCodeRequest, validateAuthorizationCode, validateToken } from "./validate-authorization-code.mjs";
1
+ import { applyDefaultAccessTokenExpiry, generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId } from "./utils.mjs";
2
+ import { RESERVED_AUTHORIZATION_PARAMS, RESERVED_AUTHORIZATION_PARAMS_SET, createAuthorizationURL } from "./create-authorization-url.mjs";
3
+ import { additionalAuthorizationParamsSchema } from "./authorization-params.mjs";
4
+ import { decodeBasicCredentials, encodeBasicCredentials } from "./basic-credentials.mjs";
5
+ import { CLIENT_ASSERTION_TYPE, PRIVATE_KEY_JWT_SIGNING_ALGORITHMS, createPrivateKeyJwtClientAssertionGetter, resolveClientAssertionParams, signPrivateKeyJwtClientAssertion } from "./client-assertion.mjs";
6
+ import { clientCredentialsToken, clientCredentialsTokenRequest } from "./client-credentials-token.mjs";
7
+ import { refreshAccessToken, refreshAccessTokenRequest } from "./refresh-access-token.mjs";
8
+ import { authorizationCodeRequest, validateAuthorizationCode, validateToken } from "./validate-authorization-code.mjs";
7
9
  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, getPrimaryClientId, refreshAccessToken, refreshAccessTokenRequest, resolveAssertionParams, signClientAssertion, validateAuthorizationCode, validateToken, verifyAccessToken, verifyJwsAccessToken };
10
+ export { CLIENT_ASSERTION_TYPE, PRIVATE_KEY_JWT_SIGNING_ALGORITHMS, RESERVED_AUTHORIZATION_PARAMS, RESERVED_AUTHORIZATION_PARAMS_SET, additionalAuthorizationParamsSchema, applyDefaultAccessTokenExpiry, authorizationCodeRequest, clientCredentialsToken, clientCredentialsTokenRequest, createAuthorizationURL, createPrivateKeyJwtClientAssertionGetter, decodeBasicCredentials, encodeBasicCredentials, generateCodeChallenge, getJwks, getOAuth2Tokens, getPrimaryClientId, refreshAccessToken, refreshAccessTokenRequest, resolveClientAssertionParams, signPrivateKeyJwtClientAssertion, validateAuthorizationCode, validateToken, verifyAccessToken, verifyJwsAccessToken };
@@ -30,6 +30,13 @@ interface OAuthProvider<T extends Record<string, any> = Record<string, any>, O e
30
30
  redirectURI: string;
31
31
  display?: string | undefined;
32
32
  loginHint?: string | undefined;
33
+ /**
34
+ * Extra query parameters to append to the authorization URL.
35
+ * Providers forward these to the shared `createAuthorizationURL` helper,
36
+ * which drops any keys present in `RESERVED_AUTHORIZATION_PARAMS`
37
+ * before applying them.
38
+ */
39
+ additionalParams?: Record<string, string> | undefined;
33
40
  }) => Awaitable<URL>;
34
41
  name: string;
35
42
  validateAuthorizationCode: (data: {
@@ -81,6 +88,17 @@ interface OAuthProvider<T extends Record<string, any> = Record<string, any>, O e
81
88
  * Disable sign up for new users.
82
89
  */
83
90
  disableSignUp?: boolean | undefined;
91
+ /**
92
+ * Accept callbacks that arrive without a `state` parameter. When true,
93
+ * the shared OAuth callback handler restarts the flow server-side with
94
+ * fresh `state` and PKCE instead of rejecting the request. Intended for
95
+ * providers that initiate OAuth without RP-side flow kickoff (e.g.
96
+ * Clever). Leave unset for any provider that always initiates from the
97
+ * RP.
98
+ *
99
+ * @default false
100
+ */
101
+ allowIdpInitiated?: boolean | undefined;
84
102
  /**
85
103
  * Options for the provider
86
104
  */
@@ -90,9 +108,10 @@ type ProviderOptions<Profile extends Record<string, any> = any> = {
90
108
  /**
91
109
  * The client ID of your application.
92
110
  *
93
- * This is usually a string but can be any type depending on the provider.
111
+ * Some providers accept multiple platform client IDs. The first entry is the
112
+ * primary client ID used for token endpoint client authentication.
94
113
  */
95
- clientId?: unknown | undefined;
114
+ clientId?: LiteralString | string[] | undefined;
96
115
  /**
97
116
  * The client secret of your application
98
117
  */
@@ -1,61 +1,41 @@
1
- import { ClientAssertionConfig } from "./client-assertion.mjs";
2
1
  import { AwaitableFunction } from "../types/helper.mjs";
3
2
  import { OAuth2Tokens, ProviderOptions } from "./oauth-provider.mjs";
3
+ import { TokenEndpointAuth, TokenEndpointSecretAuthentication } from "./token-endpoint-auth.mjs";
4
4
 
5
5
  //#region src/oauth2/refresh-access-token.d.ts
6
- declare function refreshAccessTokenRequest({
7
- refreshToken,
8
- options,
9
- authentication,
10
- clientAssertion,
11
- tokenEndpoint,
12
- extraParams,
13
- resource
14
- }: {
6
+ interface RefreshAccessTokenRequestInput {
15
7
  refreshToken: string;
16
8
  options: AwaitableFunction<Partial<ProviderOptions>>;
17
- authentication?: ("basic" | "post" | "private_key_jwt") | undefined;
18
- clientAssertion?: ClientAssertionConfig | undefined; /** Token endpoint URL. Used as the JWT `aud` claim when signing assertions. */
9
+ authentication?: TokenEndpointSecretAuthentication | undefined;
10
+ tokenEndpointAuth?: TokenEndpointAuth | undefined;
19
11
  tokenEndpoint?: string | undefined;
20
12
  extraParams?: Record<string, string> | undefined;
21
13
  resource?: (string | string[]) | undefined;
22
- }): Promise<{
23
- body: URLSearchParams;
24
- headers: Record<string, any>;
25
- }>;
26
- /**
27
- * @deprecated use async'd refreshAccessTokenRequest instead
28
- */
29
- declare function createRefreshAccessTokenRequest({
14
+ }
15
+ interface RefreshAccessTokenInput extends RefreshAccessTokenRequestInput {
16
+ options: Partial<ProviderOptions>;
17
+ tokenEndpoint: string;
18
+ }
19
+ declare function refreshAccessTokenRequest({
30
20
  refreshToken,
31
21
  options,
32
22
  authentication,
23
+ tokenEndpointAuth,
24
+ tokenEndpoint,
33
25
  extraParams,
34
26
  resource
35
- }: {
36
- refreshToken: string;
37
- options: ProviderOptions;
38
- authentication?: ("basic" | "post" | "private_key_jwt") | undefined;
39
- extraParams?: Record<string, string> | undefined;
40
- resource?: (string | string[]) | undefined;
41
- }): {
27
+ }: RefreshAccessTokenRequestInput): Promise<{
42
28
  body: URLSearchParams;
43
- headers: Record<string, any>;
44
- };
29
+ headers: Record<string, string>;
30
+ }>;
45
31
  declare function refreshAccessToken({
46
32
  refreshToken,
47
33
  options,
48
34
  tokenEndpoint,
49
35
  authentication,
50
- clientAssertion,
51
- extraParams
52
- }: {
53
- refreshToken: string;
54
- options: Partial<ProviderOptions>;
55
- tokenEndpoint: string;
56
- authentication?: ("basic" | "post" | "private_key_jwt") | undefined;
57
- clientAssertion?: ClientAssertionConfig | undefined;
58
- extraParams?: Record<string, string> | undefined;
59
- }): Promise<OAuth2Tokens>;
36
+ tokenEndpointAuth,
37
+ extraParams,
38
+ resource
39
+ }: RefreshAccessTokenInput): Promise<OAuth2Tokens>;
60
40
  //#endregion
61
- export { createRefreshAccessTokenRequest, refreshAccessToken, refreshAccessTokenRequest };
41
+ export { refreshAccessToken, refreshAccessTokenRequest };
@@ -1,33 +1,26 @@
1
- import { resolveAssertionParams } from "./client-assertion.mjs";
2
- import { base64 } from "@better-auth/utils/base64";
1
+ import { applyTokenEndpointAuth } from "./token-endpoint-auth.mjs";
3
2
  import { betterFetch } from "@better-fetch/fetch";
4
3
  //#region src/oauth2/refresh-access-token.ts
5
- async function refreshAccessTokenRequest({ refreshToken, options, authentication, clientAssertion, tokenEndpoint, extraParams, resource }) {
4
+ async function refreshAccessTokenRequest({ refreshToken, options, authentication, tokenEndpointAuth, tokenEndpoint, extraParams, resource }) {
6
5
  options = typeof options === "function" ? await options() : options;
7
- if (authentication === "private_key_jwt") {
8
- if (!clientAssertion) throw new Error("private_key_jwt authentication requires a clientAssertion configuration");
9
- const assertionParams = await resolveAssertionParams({
10
- clientAssertion,
11
- clientId: Array.isArray(options.clientId) ? options.clientId[0] : options.clientId,
12
- tokenEndpoint
13
- });
14
- extraParams = {
15
- ...extraParams,
16
- ...assertionParams
17
- };
18
- }
19
- return createRefreshAccessTokenRequest({
6
+ const request = buildRefreshAccessTokenRequest({
20
7
  refreshToken,
21
8
  options,
22
- authentication,
23
9
  extraParams,
24
10
  resource
25
11
  });
12
+ await applyTokenEndpointAuth({
13
+ body: request.body,
14
+ headers: request.headers,
15
+ options,
16
+ tokenEndpoint: tokenEndpoint ?? "",
17
+ grantType: "refresh_token",
18
+ tokenEndpointAuth,
19
+ authentication
20
+ });
21
+ return request;
26
22
  }
27
- /**
28
- * @deprecated use async'd refreshAccessTokenRequest instead
29
- */
30
- function createRefreshAccessTokenRequest({ refreshToken, options, authentication, extraParams, resource }) {
23
+ function buildRefreshAccessTokenRequest({ refreshToken, options, extraParams, resource }) {
31
24
  const body = new URLSearchParams();
32
25
  const headers = {
33
26
  "content-type": "application/x-www-form-urlencoded",
@@ -35,13 +28,6 @@ function createRefreshAccessTokenRequest({ refreshToken, options, authentication
35
28
  };
36
29
  body.set("grant_type", "refresh_token");
37
30
  body.set("refresh_token", refreshToken);
38
- const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
39
- if (authentication === "basic") if (primaryClientId) headers["authorization"] = "Basic " + base64.encode(`${primaryClientId}:${options.clientSecret ?? ""}`);
40
- else headers["authorization"] = "Basic " + base64.encode(`:${options.clientSecret ?? ""}`);
41
- else {
42
- body.set("client_id", primaryClientId);
43
- if (authentication !== "private_key_jwt" && options.clientSecret) body.set("client_secret", options.clientSecret);
44
- }
45
31
  if (resource) if (typeof resource === "string") body.append("resource", resource);
46
32
  else for (const _resource of resource) body.append("resource", _resource);
47
33
  if (extraParams) for (const [key, value] of Object.entries(extraParams)) body.set(key, value);
@@ -50,14 +36,15 @@ function createRefreshAccessTokenRequest({ refreshToken, options, authentication
50
36
  headers
51
37
  };
52
38
  }
53
- async function refreshAccessToken({ refreshToken, options, tokenEndpoint, authentication, clientAssertion, extraParams }) {
39
+ async function refreshAccessToken({ refreshToken, options, tokenEndpoint, authentication, tokenEndpointAuth, extraParams, resource }) {
54
40
  const { body, headers } = await refreshAccessTokenRequest({
55
41
  refreshToken,
56
42
  options,
57
43
  authentication,
58
- clientAssertion,
44
+ tokenEndpointAuth,
59
45
  tokenEndpoint,
60
- extraParams
46
+ extraParams,
47
+ resource
61
48
  });
62
49
  const { data, error } = await betterFetch(tokenEndpoint, {
63
50
  method: "POST",
@@ -83,4 +70,4 @@ async function refreshAccessToken({ refreshToken, options, tokenEndpoint, authen
83
70
  return tokens;
84
71
  }
85
72
  //#endregion
86
- export { createRefreshAccessTokenRequest, refreshAccessToken, refreshAccessTokenRequest };
73
+ export { refreshAccessToken, refreshAccessTokenRequest };
@@ -0,0 +1,17 @@
1
+ import { ClientAssertionGetter } from "./client-assertion.mjs";
2
+
3
+ //#region src/oauth2/token-endpoint-auth.d.ts
4
+ type TokenEndpointAuth = {
5
+ method: "none";
6
+ } | {
7
+ method: "client_secret_basic";
8
+ } | {
9
+ method: "client_secret_post";
10
+ } | {
11
+ method: "private_key_jwt";
12
+ getClientAssertion: ClientAssertionGetter;
13
+ };
14
+ type TokenEndpointAuthMethod = TokenEndpointAuth["method"];
15
+ type TokenEndpointSecretAuthentication = "basic" | "post";
16
+ //#endregion
17
+ export { TokenEndpointAuth, TokenEndpointAuthMethod, TokenEndpointSecretAuthentication };
@@ -0,0 +1,89 @@
1
+ import { getPrimaryClientId } from "./utils.mjs";
2
+ import { encodeBasicCredentials } from "./basic-credentials.mjs";
3
+ import { resolveClientAssertionParams } from "./client-assertion.mjs";
4
+ //#region src/oauth2/token-endpoint-auth.ts
5
+ function getDefaultTokenEndpointAuth(options, authentication) {
6
+ if (authentication === "basic") return { method: "client_secret_basic" };
7
+ if (options.clientSecret) return { method: "client_secret_post" };
8
+ return { method: "none" };
9
+ }
10
+ function assertNoClientSecret(method, options, body) {
11
+ if (options.clientSecret || body.has("client_secret")) throw new Error(`${method} token endpoint authentication cannot be combined with clientSecret`);
12
+ }
13
+ function setClientId(body, clientId) {
14
+ if (clientId) body.set("client_id", clientId);
15
+ }
16
+ function assertClientSecretConfigured(method, options) {
17
+ if (!options.clientSecret) throw new Error(`${method} token endpoint authentication requires clientSecret`);
18
+ }
19
+ function assertClientIdConfigured(method, clientId) {
20
+ if (!clientId) throw new Error(`${method} token endpoint authentication requires clientId`);
21
+ }
22
+ function setClientSecretPostAuth({ body, options, clientId, requireClientSecret }) {
23
+ if (requireClientSecret) assertClientSecretConfigured("client_secret_post", options);
24
+ if (options.clientSecret) {
25
+ assertClientIdConfigured("client_secret_post", clientId);
26
+ setClientId(body, clientId);
27
+ body.set("client_secret", options.clientSecret);
28
+ }
29
+ }
30
+ function setClientSecretBasicAuth({ headers, options, clientId, body }) {
31
+ if (body.has("client_secret")) throw new Error("client_secret_basic token endpoint authentication cannot be combined with client_secret body parameters");
32
+ assertClientSecretConfigured("client_secret_basic", options);
33
+ assertClientIdConfigured("client_secret_basic", clientId);
34
+ headers.authorization = encodeBasicCredentials(clientId, options.clientSecret);
35
+ }
36
+ function assertCompleteManualClientAssertion(body) {
37
+ if (body.has("client_assertion") !== body.has("client_assertion_type")) throw new Error("client_assertion and client_assertion_type must both be provided");
38
+ }
39
+ async function applyTokenEndpointAuth({ body, headers, options, tokenEndpoint, grantType, tokenEndpointAuth, authentication }) {
40
+ assertCompleteManualClientAssertion(body);
41
+ const clientId = getPrimaryClientId(options.clientId);
42
+ if (body.has("client_assertion")) {
43
+ if (tokenEndpointAuth) throw new Error("client_assertion body parameters cannot be combined with tokenEndpointAuth");
44
+ assertNoClientSecret("private_key_jwt", options, body);
45
+ setClientId(body, clientId);
46
+ return;
47
+ }
48
+ const auth = tokenEndpointAuth ?? getDefaultTokenEndpointAuth(options, authentication);
49
+ if (auth.method === "private_key_jwt") {
50
+ assertNoClientSecret(auth.method, options, body);
51
+ assertClientIdConfigured(auth.method, clientId);
52
+ if (!tokenEndpoint) throw new Error("private_key_jwt token endpoint authentication requires tokenEndpoint");
53
+ const assertionParams = await resolveClientAssertionParams({
54
+ getClientAssertion: auth.getClientAssertion,
55
+ context: {
56
+ clientId,
57
+ tokenEndpoint,
58
+ grantType
59
+ }
60
+ });
61
+ setClientId(body, clientId);
62
+ for (const [key, value] of Object.entries(assertionParams)) body.set(key, value);
63
+ return;
64
+ }
65
+ if (auth.method === "none") {
66
+ assertNoClientSecret(auth.method, options, body);
67
+ if (grantType === "client_credentials") throw new Error("none token endpoint authentication cannot be used with client_credentials grant");
68
+ assertClientIdConfigured(auth.method, clientId);
69
+ setClientId(body, clientId);
70
+ return;
71
+ }
72
+ if (auth.method === "client_secret_basic") {
73
+ setClientSecretBasicAuth({
74
+ headers,
75
+ options,
76
+ clientId,
77
+ body
78
+ });
79
+ return;
80
+ }
81
+ setClientSecretPostAuth({
82
+ body,
83
+ options,
84
+ clientId,
85
+ requireClientSecret: tokenEndpointAuth?.method === "client_secret_post"
86
+ });
87
+ }
88
+ //#endregion
89
+ export { applyTokenEndpointAuth };
@@ -2,6 +2,14 @@ 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
+ * Fill in `accessTokenExpiresAt` from the provider's configured
7
+ * `accessTokenExpiresIn` when the token response omitted `expires_in`. Without a
8
+ * known expiry, `getAccessToken` cannot tell the token is expired and never
9
+ * refreshes it. No-op when the provider already supplied an expiry or no
10
+ * fallback is configured.
11
+ */
12
+ declare function applyDefaultAccessTokenExpiry(tokens: OAuth2Tokens, accessTokenExpiresIn: number | undefined): OAuth2Tokens;
5
13
  /**
6
14
  * Return the provider's primary Client ID: the single string, or the entry at
7
15
  * array index 0 for the cross-platform form used by ID token audience
@@ -13,4 +21,4 @@ declare function getOAuth2Tokens(data: Record<string, any>): OAuth2Tokens;
13
21
  declare function getPrimaryClientId(clientId: unknown): string | undefined;
14
22
  declare function generateCodeChallenge(codeVerifier: string): Promise<string>;
15
23
  //#endregion
16
- export { generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId };
24
+ export { applyDefaultAccessTokenExpiry, generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId };
@@ -17,6 +17,17 @@ function getOAuth2Tokens(data) {
17
17
  };
18
18
  }
19
19
  /**
20
+ * Fill in `accessTokenExpiresAt` from the provider's configured
21
+ * `accessTokenExpiresIn` when the token response omitted `expires_in`. Without a
22
+ * known expiry, `getAccessToken` cannot tell the token is expired and never
23
+ * refreshes it. No-op when the provider already supplied an expiry or no
24
+ * fallback is configured.
25
+ */
26
+ function applyDefaultAccessTokenExpiry(tokens, accessTokenExpiresIn) {
27
+ if (!tokens.accessTokenExpiresAt && accessTokenExpiresIn) tokens.accessTokenExpiresAt = new Date(Date.now() + accessTokenExpiresIn * 1e3);
28
+ return tokens;
29
+ }
30
+ /**
20
31
  * Return the provider's primary Client ID: the single string, or the entry at
21
32
  * array index 0 for the cross-platform form used by ID token audience
22
33
  * verification. Index 0 is the designated primary and pairs with
@@ -34,4 +45,4 @@ async function generateCodeChallenge(codeVerifier) {
34
45
  return base64Url.encode(new Uint8Array(hash), { padding: false });
35
46
  }
36
47
  //#endregion
37
- export { generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId };
48
+ export { applyDefaultAccessTokenExpiry, generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId };