@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,64 +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
  import * as jose from "jose";
5
5
 
6
6
  //#region src/oauth2/validate-authorization-code.d.ts
7
- declare function authorizationCodeRequest({
8
- code,
9
- codeVerifier,
10
- redirectURI,
11
- options,
12
- authentication,
13
- clientAssertion,
14
- tokenEndpoint,
15
- deviceId,
16
- headers,
17
- additionalParams,
18
- resource
19
- }: {
7
+ interface AuthorizationCodeRequestInput {
20
8
  code: string;
21
9
  redirectURI: string;
22
10
  options: AwaitableFunction<Partial<ProviderOptions>>;
23
11
  codeVerifier?: string | undefined;
24
12
  deviceId?: string | undefined;
25
- authentication?: ("basic" | "post" | "private_key_jwt") | undefined;
26
- clientAssertion?: ClientAssertionConfig | undefined; /** Token endpoint URL. Used as the JWT `aud` claim when signing assertions. */
13
+ authentication?: TokenEndpointSecretAuthentication | undefined;
14
+ tokenEndpointAuth?: TokenEndpointAuth | undefined;
27
15
  tokenEndpoint?: string | undefined;
28
16
  headers?: Record<string, string> | undefined;
29
17
  additionalParams?: Record<string, string> | undefined;
30
18
  resource?: (string | string[]) | undefined;
31
- }): Promise<{
32
- body: URLSearchParams;
33
- headers: Record<string, any>;
34
- }>;
35
- /**
36
- * @deprecated use async'd authorizationCodeRequest instead
37
- */
38
- declare function createAuthorizationCodeRequest({
19
+ }
20
+ interface ValidateAuthorizationCodeInput extends AuthorizationCodeRequestInput {
21
+ tokenEndpoint: string;
22
+ }
23
+ declare function authorizationCodeRequest({
39
24
  code,
40
25
  codeVerifier,
41
26
  redirectURI,
42
27
  options,
43
28
  authentication,
29
+ tokenEndpointAuth,
30
+ tokenEndpoint,
44
31
  deviceId,
45
32
  headers,
46
33
  additionalParams,
47
34
  resource
48
- }: {
49
- code: string;
50
- redirectURI: string;
51
- options: Partial<ProviderOptions>;
52
- codeVerifier?: string | undefined;
53
- deviceId?: string | undefined;
54
- authentication?: ("basic" | "post" | "private_key_jwt") | undefined;
55
- headers?: Record<string, string> | undefined;
56
- additionalParams?: Record<string, string> | undefined;
57
- resource?: (string | string[]) | undefined;
58
- }): {
35
+ }: AuthorizationCodeRequestInput): Promise<{
59
36
  body: URLSearchParams;
60
- headers: Record<string, any>;
61
- };
37
+ headers: Record<string, string>;
38
+ }>;
62
39
  declare function validateAuthorizationCode({
63
40
  code,
64
41
  codeVerifier,
@@ -66,27 +43,15 @@ declare function validateAuthorizationCode({
66
43
  options,
67
44
  tokenEndpoint,
68
45
  authentication,
69
- clientAssertion,
46
+ tokenEndpointAuth,
70
47
  deviceId,
71
48
  headers,
72
49
  additionalParams,
73
50
  resource
74
- }: {
75
- code: string;
76
- redirectURI: string;
77
- options: AwaitableFunction<Partial<ProviderOptions>>;
78
- codeVerifier?: string | undefined;
79
- deviceId?: string | undefined;
80
- tokenEndpoint: string;
81
- authentication?: ("basic" | "post" | "private_key_jwt") | undefined;
82
- clientAssertion?: ClientAssertionConfig | undefined;
83
- headers?: Record<string, string> | undefined;
84
- additionalParams?: Record<string, string> | undefined;
85
- resource?: (string | string[]) | undefined;
86
- }): Promise<OAuth2Tokens>;
51
+ }: ValidateAuthorizationCodeInput): Promise<OAuth2Tokens>;
87
52
  declare function validateToken(token: string, jwksEndpoint: string, options?: {
88
53
  audience?: string | string[];
89
54
  issuer?: string | string[];
90
55
  }): Promise<jose.JWTVerifyResult<jose.JWTPayload> & jose.ResolvedKey>;
91
56
  //#endregion
92
- export { authorizationCodeRequest, createAuthorizationCodeRequest, validateAuthorizationCode, validateToken };
57
+ export { authorizationCodeRequest, validateAuthorizationCode, validateToken };
@@ -1,39 +1,32 @@
1
- import { resolveAssertionParams } from "./client-assertion.mjs";
2
1
  import { getOAuth2Tokens } from "./utils.mjs";
2
+ import { applyTokenEndpointAuth } from "./token-endpoint-auth.mjs";
3
3
  import { createRemoteJWKSet, jwtVerify } from "jose";
4
- import { base64 } from "@better-auth/utils/base64";
5
4
  import { betterFetch } from "@better-fetch/fetch";
6
5
  //#region src/oauth2/validate-authorization-code.ts
7
- async function authorizationCodeRequest({ code, codeVerifier, redirectURI, options, authentication, clientAssertion, tokenEndpoint, deviceId, headers, additionalParams = {}, resource }) {
6
+ async function authorizationCodeRequest({ code, codeVerifier, redirectURI, options, authentication, tokenEndpointAuth, tokenEndpoint, deviceId, headers, additionalParams = {}, resource }) {
8
7
  options = typeof options === "function" ? await options() : options;
9
- if (authentication === "private_key_jwt") {
10
- if (!clientAssertion) throw new Error("private_key_jwt authentication requires a clientAssertion configuration");
11
- const assertionParams = await resolveAssertionParams({
12
- clientAssertion,
13
- clientId: Array.isArray(options.clientId) ? options.clientId[0] : options.clientId,
14
- tokenEndpoint
15
- });
16
- additionalParams = {
17
- ...additionalParams,
18
- ...assertionParams
19
- };
20
- }
21
- return createAuthorizationCodeRequest({
8
+ const request = buildAuthorizationCodeRequest({
22
9
  code,
23
10
  codeVerifier,
24
11
  redirectURI,
25
12
  options,
26
- authentication,
27
13
  deviceId,
28
14
  headers,
29
15
  additionalParams,
30
16
  resource
31
17
  });
18
+ await applyTokenEndpointAuth({
19
+ body: request.body,
20
+ headers: request.headers,
21
+ options,
22
+ tokenEndpoint: tokenEndpoint ?? "",
23
+ grantType: "authorization_code",
24
+ tokenEndpointAuth,
25
+ authentication
26
+ });
27
+ return request;
32
28
  }
33
- /**
34
- * @deprecated use async'd authorizationCodeRequest instead
35
- */
36
- function createAuthorizationCodeRequest({ code, codeVerifier, redirectURI, options, authentication, deviceId, headers, additionalParams = {}, resource }) {
29
+ function buildAuthorizationCodeRequest({ code, codeVerifier, redirectURI, options, deviceId, headers, additionalParams = {}, resource }) {
37
30
  const body = new URLSearchParams();
38
31
  const requestHeaders = {
39
32
  "content-type": "application/x-www-form-urlencoded",
@@ -48,26 +41,20 @@ function createAuthorizationCodeRequest({ code, codeVerifier, redirectURI, optio
48
41
  body.set("redirect_uri", options.redirectURI || redirectURI);
49
42
  if (resource) if (typeof resource === "string") body.append("resource", resource);
50
43
  else for (const _resource of resource) body.append("resource", _resource);
51
- const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
52
- if (authentication === "basic") requestHeaders["authorization"] = `Basic ${base64.encode(`${primaryClientId}:${options.clientSecret ?? ""}`)}`;
53
- else {
54
- body.set("client_id", primaryClientId);
55
- if (authentication !== "private_key_jwt" && options.clientSecret) body.set("client_secret", options.clientSecret);
56
- }
57
44
  for (const [key, value] of Object.entries(additionalParams)) if (!body.has(key)) body.append(key, value);
58
45
  return {
59
46
  body,
60
47
  headers: requestHeaders
61
48
  };
62
49
  }
63
- async function validateAuthorizationCode({ code, codeVerifier, redirectURI, options, tokenEndpoint, authentication, clientAssertion, deviceId, headers, additionalParams = {}, resource }) {
50
+ async function validateAuthorizationCode({ code, codeVerifier, redirectURI, options, tokenEndpoint, authentication, tokenEndpointAuth, deviceId, headers, additionalParams = {}, resource }) {
64
51
  const { body, headers: requestHeaders } = await authorizationCodeRequest({
65
52
  code,
66
53
  codeVerifier,
67
54
  redirectURI,
68
55
  options,
69
56
  authentication,
70
- clientAssertion,
57
+ tokenEndpointAuth,
71
58
  tokenEndpoint,
72
59
  deviceId,
73
60
  headers,
@@ -89,4 +76,4 @@ async function validateToken(token, jwksEndpoint, options) {
89
76
  });
90
77
  }
91
78
  //#endregion
92
- export { authorizationCodeRequest, createAuthorizationCodeRequest, validateAuthorizationCode, validateToken };
79
+ export { authorizationCodeRequest, validateAuthorizationCode, validateToken };
@@ -1,8 +1,16 @@
1
1
  import { logger } from "../env/logger.mjs";
2
2
  import { APIError } from "better-call";
3
- import { UnsecuredJWT, createLocalJWKSet, decodeProtectedHeader, jwtVerify } from "jose";
3
+ import { UnsecuredJWT, createLocalJWKSet, decodeProtectedHeader, errors, jwtVerify } from "jose";
4
4
  import { betterFetch } from "@better-fetch/fetch";
5
5
  //#region src/oauth2/verify.ts
6
+ const joseInfrastructureErrorCodes = new Set([
7
+ errors.JWKSTimeout.code,
8
+ errors.JWKSInvalid.code,
9
+ errors.JWKSMultipleMatchingKeys.code
10
+ ]);
11
+ function isJoseInfrastructureError(error) {
12
+ return joseInfrastructureErrorCodes.has(error.code);
13
+ }
6
14
  /** Last fetched jwks used locally in getJwks @internal */
7
15
  let jwks;
8
16
  /**
@@ -28,7 +36,7 @@ async function getJwks(token, opts) {
28
36
  if (error instanceof Error) throw error;
29
37
  throw new Error(error);
30
38
  }
31
- if (!jwtHeaders.kid) throw new Error("Missing jwt kid");
39
+ if (!jwtHeaders.kid) throw new APIError("UNAUTHORIZED", { message: "invalid access token" });
32
40
  if (!jwks || !jwks.keys.find((jwk) => jwk.kid === jwtHeaders.kid)) {
33
41
  jwks = typeof opts.jwksFetch === "string" ? await betterFetch(opts.jwksFetch, { headers: { Accept: "application/json" } }).then(async (res) => {
34
42
  if (res.error) throw new Error(`Jwks failed: ${res.error.message ?? res.error.statusText}`);
@@ -51,9 +59,11 @@ async function verifyAccessToken(token, opts) {
51
59
  verifyOptions: opts.verifyOptions
52
60
  });
53
61
  } catch (error) {
54
- if (error instanceof Error) if (error.name === "TypeError" || error.name === "JWSInvalid") {} else if (error.name === "JWTExpired") throw new APIError("UNAUTHORIZED", { message: "token expired" });
55
- else if (error.name === "JWTInvalid") throw new APIError("UNAUTHORIZED", { message: "token invalid" });
56
- else throw error;
62
+ if (error instanceof Error) if (error.name === "TypeError" || error.name === "JWSInvalid") {} else if (error instanceof errors.JWTExpired) throw new APIError("UNAUTHORIZED", { message: "token expired" });
63
+ else if (error instanceof errors.JOSEError) {
64
+ if (isJoseInfrastructureError(error)) throw error;
65
+ throw new APIError("UNAUTHORIZED", { message: "invalid access token" });
66
+ } else throw error;
57
67
  else throw new Error(error);
58
68
  }
59
69
  if (opts?.remoteVerify) {
@@ -12,7 +12,7 @@ interface AppleProfile {
12
12
  * The email address is either the user's real email address or the proxy
13
13
  * address, depending on their status private email relay service.
14
14
  */
15
- email: string;
15
+ email?: string;
16
16
  /**
17
17
  * A string or Boolean value that indicates whether the service verifies
18
18
  * the email. The value can either be a string ("true" or "false") or a
@@ -70,7 +70,8 @@ declare const apple: (options: AppleOptions) => {
70
70
  createAuthorizationURL({
71
71
  state,
72
72
  scopes,
73
- redirectURI
73
+ redirectURI,
74
+ additionalParams
74
75
  }: {
75
76
  state: string;
76
77
  codeVerifier: string;
@@ -78,6 +79,7 @@ declare const apple: (options: AppleOptions) => {
78
79
  redirectURI: string;
79
80
  display?: string | undefined;
80
81
  loginHint?: string | undefined;
82
+ additionalParams?: Record<string, string> | undefined;
81
83
  }): Promise<URL>;
82
84
  validateAuthorizationCode: ({
83
85
  code,
@@ -93,16 +95,7 @@ declare const apple: (options: AppleOptions) => {
93
95
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
94
96
  getUserInfo(token: OAuth2Tokens & {
95
97
  user?: {
96
- name?
97
- /**
98
- * An Integer value that indicates whether the user appears to be a real
99
- * person. Use the value of this claim to mitigate fraud. The possible
100
- * values are: 0 (or Unsupported), 1 (or Unknown), 2 (or LikelyReal). For
101
- * more information, see ASUserDetectionStatus. This claim is present only
102
- * in iOS 14 and later, macOS 11 and later, watchOS 7 and later, tvOS 14
103
- * and later. The claim isn’t present or supported for web-based apps.
104
- */
105
- : {
98
+ name?: {
106
99
  firstName?: string;
107
100
  lastName?: string;
108
101
  };
@@ -7,12 +7,22 @@ import { validateAuthorizationCode } from "../oauth2/validate-authorization-code
7
7
  import { decodeJwt, decodeProtectedHeader, importJWK, jwtVerify } from "jose";
8
8
  import { betterFetch } from "@better-fetch/fetch";
9
9
  //#region src/social-providers/apple.ts
10
+ async function sha256Hex(value) {
11
+ const data = new TextEncoder().encode(value);
12
+ const digest = await crypto.subtle.digest("SHA-256", data);
13
+ return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
14
+ }
15
+ async function nonceMatches(jwtNonce, nonce) {
16
+ if (typeof jwtNonce !== "string") return false;
17
+ if (jwtNonce === nonce) return true;
18
+ return jwtNonce === await sha256Hex(nonce);
19
+ }
10
20
  const apple = (options) => {
11
21
  const tokenEndpoint = "https://appleid.apple.com/auth/token";
12
22
  return {
13
23
  id: "apple",
14
24
  name: "Apple",
15
- async createAuthorizationURL({ state, scopes, redirectURI }) {
25
+ async createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
16
26
  if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
17
27
  logger.error("Client ID and client secret are required for Apple. Make sure to provide them in the options.");
18
28
  throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
@@ -28,7 +38,8 @@ const apple = (options) => {
28
38
  state,
29
39
  redirectURI,
30
40
  responseMode: "form_post",
31
- responseType: "code id_token"
41
+ responseType: "code id_token",
42
+ additionalParams
32
43
  });
33
44
  },
34
45
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -55,7 +66,7 @@ const apple = (options) => {
55
66
  ["email_verified", "is_private_email"].forEach((field) => {
56
67
  if (jwtClaims[field] !== void 0) jwtClaims[field] = Boolean(jwtClaims[field]);
57
68
  });
58
- if (nonce && jwtClaims.nonce !== nonce) return false;
69
+ if (nonce && !await nonceMatches(jwtClaims.nonce, nonce)) return false;
59
70
  return !!jwtClaims;
60
71
  } catch {
61
72
  return false;
@@ -25,7 +25,8 @@ declare const atlassian: (options: AtlassianOptions) => {
25
25
  state,
26
26
  scopes,
27
27
  codeVerifier,
28
- redirectURI
28
+ redirectURI,
29
+ additionalParams
29
30
  }: {
30
31
  state: string;
31
32
  codeVerifier: string;
@@ -33,6 +34,7 @@ declare const atlassian: (options: AtlassianOptions) => {
33
34
  redirectURI: string;
34
35
  display?: string | undefined;
35
36
  loginHint?: string | undefined;
37
+ additionalParams?: Record<string, string> | undefined;
36
38
  }): Promise<URL>;
37
39
  validateAuthorizationCode: ({
38
40
  code,
@@ -10,7 +10,7 @@ const atlassian = (options) => {
10
10
  return {
11
11
  id: "atlassian",
12
12
  name: "Atlassian",
13
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
13
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
14
14
  if (!options.clientId || !options.clientSecret) {
15
15
  logger.error("Client Id and Secret are required for Atlassian");
16
16
  throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
@@ -27,7 +27,10 @@ const atlassian = (options) => {
27
27
  state,
28
28
  codeVerifier,
29
29
  redirectURI,
30
- additionalParams: { audience: "api.atlassian.com" },
30
+ additionalParams: {
31
+ ...additionalParams ?? {},
32
+ audience: "api.atlassian.com"
33
+ },
31
34
  prompt: options.prompt
32
35
  });
33
36
  },
@@ -30,6 +30,19 @@ interface CognitoOptions extends ProviderOptions<CognitoProfile> {
30
30
  region: string;
31
31
  userPoolId: string;
32
32
  requireClientSecret?: boolean | undefined;
33
+ /**
34
+ * Skip the Cognito hosted-UI identity-provider picker by preselecting an
35
+ * IdP (maps to the `identity_provider` query parameter on the authorize
36
+ * request). Accepts `"COGNITO"`, a SAML/OIDC provider name configured on
37
+ * the User Pool, or one of the social providers (`"Google"`, `"Facebook"`,
38
+ * `"LoginWithAmazon"`, `"SignInWithApple"`).
39
+ *
40
+ * Per-request overrides via `signIn.social({ additionalParams: { identity_provider } })`
41
+ * take precedence over this value.
42
+ *
43
+ * @see https://docs.aws.amazon.com/cognito/latest/developerguide/authorization-endpoint.html
44
+ */
45
+ identityProvider?: string | undefined;
33
46
  }
34
47
  declare const cognito: (options: CognitoOptions) => {
35
48
  id: "cognito";
@@ -38,7 +51,8 @@ declare const cognito: (options: CognitoOptions) => {
38
51
  state,
39
52
  scopes,
40
53
  codeVerifier,
41
- redirectURI
54
+ redirectURI,
55
+ additionalParams
42
56
  }: {
43
57
  state: string;
44
58
  codeVerifier: string;
@@ -46,6 +60,7 @@ declare const cognito: (options: CognitoOptions) => {
46
60
  redirectURI: string;
47
61
  display?: string | undefined;
48
62
  loginHint?: string | undefined;
63
+ additionalParams?: Record<string, string> | undefined;
49
64
  }): Promise<URL>;
50
65
  validateAuthorizationCode: ({
51
66
  code,
@@ -19,7 +19,7 @@ const cognito = (options) => {
19
19
  return {
20
20
  id: "cognito",
21
21
  name: "Cognito",
22
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
22
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
23
23
  if (!getPrimaryClientId(options.clientId)) {
24
24
  logger.error("ClientId is required for Amazon Cognito. Make sure to provide them in the options.");
25
25
  throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
@@ -43,7 +43,11 @@ const cognito = (options) => {
43
43
  state,
44
44
  codeVerifier,
45
45
  redirectURI,
46
- prompt: options.prompt
46
+ prompt: options.prompt,
47
+ additionalParams: {
48
+ ...options.identityProvider ? { identity_provider: options.identityProvider } : {},
49
+ ...additionalParams ?? {}
50
+ }
47
51
  });
48
52
  const scopeValue = url.searchParams.get("scope");
49
53
  if (scopeValue) {
@@ -38,7 +38,7 @@ interface DiscordProfile extends Record<string, any> {
38
38
  /** whether the email on this account has been verified */
39
39
  verified: boolean;
40
40
  /** the user's email */
41
- email: string;
41
+ email?: string | null;
42
42
  /**
43
43
  * the flags on a user's account:
44
44
  * https://discord.com/developers/docs/resources/user#user-object-user-flags
@@ -80,7 +80,8 @@ declare const discord: (options: DiscordOptions) => {
80
80
  createAuthorizationURL({
81
81
  state,
82
82
  scopes,
83
- redirectURI
83
+ redirectURI,
84
+ additionalParams
84
85
  }: {
85
86
  state: string;
86
87
  codeVerifier: string;
@@ -88,7 +89,8 @@ declare const discord: (options: DiscordOptions) => {
88
89
  redirectURI: string;
89
90
  display?: string | undefined;
90
91
  loginHint?: string | undefined;
91
- }): URL;
92
+ additionalParams?: Record<string, string> | undefined;
93
+ }): Promise<URL>;
92
94
  validateAuthorizationCode: ({
93
95
  code,
94
96
  redirectURI
@@ -1,3 +1,4 @@
1
+ import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
1
2
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
2
3
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
3
4
  import { betterFetch } from "@better-fetch/fetch";
@@ -7,12 +8,24 @@ const discord = (options) => {
7
8
  return {
8
9
  id: "discord",
9
10
  name: "Discord",
10
- createAuthorizationURL({ state, scopes, redirectURI }) {
11
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
11
12
  const _scopes = options.disableDefaultScope ? [] : ["identify", "email"];
12
13
  if (scopes) _scopes.push(...scopes);
13
14
  if (options.scope) _scopes.push(...options.scope);
14
- const permissionsParam = _scopes.includes("bot") && options.permissions !== void 0 ? `&permissions=${options.permissions}` : "";
15
- return new URL(`https://discord.com/api/oauth2/authorize?scope=${_scopes.join("+")}&response_type=code&client_id=${options.clientId}&redirect_uri=${encodeURIComponent(options.redirectURI || redirectURI)}&state=${state}&prompt=${options.prompt || "none"}${permissionsParam}`);
15
+ const hasBotScope = _scopes.includes("bot");
16
+ return createAuthorizationURL({
17
+ id: "discord",
18
+ options,
19
+ authorizationEndpoint: "https://discord.com/api/oauth2/authorize",
20
+ scopes: _scopes,
21
+ state,
22
+ redirectURI,
23
+ prompt: options.prompt || "none",
24
+ additionalParams: {
25
+ ...hasBotScope && options.permissions !== void 0 ? { permissions: String(options.permissions) } : {},
26
+ ...additionalParams ?? {}
27
+ }
28
+ });
16
29
  },
17
30
  validateAuthorizationCode: async ({ code, redirectURI }) => {
18
31
  return validateAuthorizationCode({
@@ -24,7 +24,8 @@ declare const dropbox: (options: DropboxOptions) => {
24
24
  state,
25
25
  scopes,
26
26
  codeVerifier,
27
- redirectURI
27
+ redirectURI,
28
+ additionalParams
28
29
  }: {
29
30
  state: string;
30
31
  codeVerifier: string;
@@ -32,6 +33,7 @@ declare const dropbox: (options: DropboxOptions) => {
32
33
  redirectURI: string;
33
34
  display?: string | undefined;
34
35
  loginHint?: string | undefined;
36
+ additionalParams?: Record<string, string> | undefined;
35
37
  }) => Promise<URL>;
36
38
  validateAuthorizationCode: ({
37
39
  code,
@@ -8,12 +8,10 @@ const dropbox = (options) => {
8
8
  return {
9
9
  id: "dropbox",
10
10
  name: "Dropbox",
11
- createAuthorizationURL: async ({ state, scopes, codeVerifier, redirectURI }) => {
11
+ createAuthorizationURL: async ({ state, scopes, codeVerifier, redirectURI, additionalParams }) => {
12
12
  const _scopes = options.disableDefaultScope ? [] : ["account_info.read"];
13
13
  if (options.scope) _scopes.push(...options.scope);
14
14
  if (scopes) _scopes.push(...scopes);
15
- const additionalParams = {};
16
- if (options.accessType) additionalParams.token_access_type = options.accessType;
17
15
  return await createAuthorizationURL({
18
16
  id: "dropbox",
19
17
  options,
@@ -22,7 +20,10 @@ const dropbox = (options) => {
22
20
  state,
23
21
  redirectURI,
24
22
  codeVerifier,
25
- additionalParams
23
+ additionalParams: {
24
+ ...options.accessType ? { token_access_type: options.accessType } : {},
25
+ ...additionalParams ?? {}
26
+ }
26
27
  });
27
28
  },
28
29
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -3,8 +3,8 @@ import { OAuth2Tokens, ProviderOptions } from "../oauth2/oauth-provider.mjs";
3
3
  interface FacebookProfile {
4
4
  id: string;
5
5
  name: string;
6
- email: string;
7
- email_verified: boolean;
6
+ email?: string;
7
+ email_verified?: boolean;
8
8
  picture: {
9
9
  data: {
10
10
  height: number;
@@ -34,7 +34,8 @@ declare const facebook: (options: FacebookOptions) => {
34
34
  state,
35
35
  scopes,
36
36
  redirectURI,
37
- loginHint
37
+ loginHint,
38
+ additionalParams
38
39
  }: {
39
40
  state: string;
40
41
  codeVerifier: string;
@@ -42,6 +43,7 @@ declare const facebook: (options: FacebookOptions) => {
42
43
  redirectURI: string;
43
44
  display?: string | undefined;
44
45
  loginHint?: string | undefined;
46
+ additionalParams?: Record<string, string> | undefined;
45
47
  }): Promise<URL>;
46
48
  validateAuthorizationCode: ({
47
49
  code,
@@ -11,7 +11,7 @@ const facebook = (options) => {
11
11
  return {
12
12
  id: "facebook",
13
13
  name: "Facebook",
14
- async createAuthorizationURL({ state, scopes, redirectURI, loginHint }) {
14
+ async createAuthorizationURL({ state, scopes, redirectURI, loginHint, additionalParams }) {
15
15
  if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
16
16
  logger.error("Client ID and client secret are required for Facebook. Make sure to provide them in the options.");
17
17
  throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
@@ -27,7 +27,10 @@ const facebook = (options) => {
27
27
  state,
28
28
  redirectURI,
29
29
  loginHint,
30
- additionalParams: options.configId ? { config_id: options.configId } : {}
30
+ additionalParams: {
31
+ ...options.configId ? { config_id: options.configId } : {},
32
+ ...additionalParams ?? {}
33
+ }
31
34
  });
32
35
  },
33
36
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -111,7 +114,7 @@ const facebook = (options) => {
111
114
  name: profile.name,
112
115
  email: profile.email,
113
116
  image: profile.picture.data.url,
114
- emailVerified: profile.email_verified,
117
+ emailVerified: profile.email_verified ?? false,
115
118
  ...userMap
116
119
  },
117
120
  data: profile
@@ -16,7 +16,8 @@ declare const figma: (options: FigmaOptions) => {
16
16
  state,
17
17
  scopes,
18
18
  codeVerifier,
19
- redirectURI
19
+ redirectURI,
20
+ additionalParams
20
21
  }: {
21
22
  state: string;
22
23
  codeVerifier: string;
@@ -24,6 +25,7 @@ declare const figma: (options: FigmaOptions) => {
24
25
  redirectURI: string;
25
26
  display?: string | undefined;
26
27
  loginHint?: string | undefined;
28
+ additionalParams?: Record<string, string> | undefined;
27
29
  }): Promise<URL>;
28
30
  validateAuthorizationCode: ({
29
31
  code,
@@ -10,7 +10,7 @@ const figma = (options) => {
10
10
  return {
11
11
  id: "figma",
12
12
  name: "Figma",
13
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
13
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
14
14
  if (!options.clientId || !options.clientSecret) {
15
15
  logger.error("Client Id and Client Secret are required for Figma. Make sure to provide them in the options.");
16
16
  throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
@@ -26,7 +26,8 @@ const figma = (options) => {
26
26
  scopes: _scopes,
27
27
  state,
28
28
  codeVerifier,
29
- redirectURI
29
+ redirectURI,
30
+ additionalParams
30
31
  });
31
32
  },
32
33
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -23,7 +23,7 @@ interface GithubProfile {
23
23
  company: string;
24
24
  blog: string;
25
25
  location: string;
26
- email: string;
26
+ email: string | null;
27
27
  hireable: boolean;
28
28
  bio: string;
29
29
  twitter_username: string;
@@ -57,7 +57,8 @@ declare const github: (options: GithubOptions) => {
57
57
  scopes,
58
58
  loginHint,
59
59
  codeVerifier,
60
- redirectURI
60
+ redirectURI,
61
+ additionalParams
61
62
  }: {
62
63
  state: string;
63
64
  codeVerifier: string;
@@ -65,6 +66,7 @@ declare const github: (options: GithubOptions) => {
65
66
  redirectURI: string;
66
67
  display?: string | undefined;
67
68
  loginHint?: string | undefined;
69
+ additionalParams?: Record<string, string> | undefined;
68
70
  }): Promise<URL>;
69
71
  validateAuthorizationCode: ({
70
72
  code,