@better-auth/core 1.5.0-beta.4 → 1.5.0-beta.6

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 (178) hide show
  1. package/.turbo/turbo-build.log +170 -37
  2. package/dist/api/index.d.mts +188 -1
  3. package/dist/api/index.mjs +2 -1
  4. package/dist/context/endpoint-context.d.mts +19 -0
  5. package/dist/context/endpoint-context.mjs +31 -0
  6. package/dist/context/index.d.mts +3 -52
  7. package/dist/context/index.mjs +3 -1
  8. package/dist/context/request-state.d.mts +27 -0
  9. package/dist/context/request-state.mjs +49 -0
  10. package/dist/context/transaction.d.mts +16 -0
  11. package/dist/context/transaction.mjs +52 -0
  12. package/dist/db/adapter/factory.d.mts +27 -0
  13. package/dist/db/adapter/factory.mjs +738 -0
  14. package/dist/db/adapter/get-default-field-name.d.mts +18 -0
  15. package/dist/db/adapter/get-default-field-name.mjs +38 -0
  16. package/dist/db/adapter/get-default-model-name.d.mts +12 -0
  17. package/dist/db/adapter/get-default-model-name.mjs +32 -0
  18. package/dist/db/adapter/get-field-attributes.d.mts +29 -0
  19. package/dist/db/adapter/get-field-attributes.mjs +39 -0
  20. package/dist/db/adapter/get-field-name.d.mts +18 -0
  21. package/dist/db/adapter/get-field-name.mjs +33 -0
  22. package/dist/db/adapter/get-id-field.d.mts +39 -0
  23. package/dist/db/adapter/get-id-field.mjs +67 -0
  24. package/dist/db/adapter/get-model-name.d.mts +12 -0
  25. package/dist/db/adapter/get-model-name.mjs +23 -0
  26. package/dist/db/adapter/index.d.mts +513 -1
  27. package/dist/db/adapter/index.mjs +8 -970
  28. package/dist/db/adapter/types.d.mts +139 -0
  29. package/dist/db/adapter/utils.d.mts +7 -0
  30. package/dist/db/adapter/utils.mjs +38 -0
  31. package/dist/db/get-tables.d.mts +8 -0
  32. package/dist/{get-tables-CMc_Emww.mjs → db/get-tables.mjs} +1 -1
  33. package/dist/db/index.d.mts +10 -2
  34. package/dist/db/index.mjs +7 -60
  35. package/dist/db/plugin.d.mts +12 -0
  36. package/dist/db/schema/account.d.mts +26 -0
  37. package/dist/db/schema/account.mjs +19 -0
  38. package/dist/db/schema/rate-limit.d.mts +14 -0
  39. package/dist/db/schema/rate-limit.mjs +11 -0
  40. package/dist/db/schema/session.d.mts +21 -0
  41. package/dist/db/schema/session.mjs +14 -0
  42. package/dist/db/schema/shared.d.mts +10 -0
  43. package/dist/db/schema/shared.mjs +11 -0
  44. package/dist/db/schema/user.d.mts +20 -0
  45. package/dist/db/schema/user.mjs +13 -0
  46. package/dist/db/schema/verification.d.mts +19 -0
  47. package/dist/db/schema/verification.mjs +12 -0
  48. package/dist/db/type.d.mts +143 -0
  49. package/dist/env/color-depth.d.mts +4 -0
  50. package/dist/env/color-depth.mjs +88 -0
  51. package/dist/env/env-impl.d.mts +32 -0
  52. package/dist/env/env-impl.mjs +82 -0
  53. package/dist/env/index.d.mts +4 -2
  54. package/dist/env/index.mjs +3 -1
  55. package/dist/{index-BRBu0-5h.d.mts → env/logger.d.mts} +1 -35
  56. package/dist/env/logger.mjs +81 -0
  57. package/dist/error/codes.d.mts +186 -0
  58. package/dist/{error-GNtLPYaS.mjs → error/codes.mjs} +2 -29
  59. package/dist/error/index.d.mts +1 -185
  60. package/dist/error/index.mjs +28 -3
  61. package/dist/index.d.mts +7 -1
  62. package/dist/oauth2/client-credentials-token.d.mts +36 -0
  63. package/dist/oauth2/client-credentials-token.mjs +54 -0
  64. package/dist/oauth2/create-authorization-url.d.mts +45 -0
  65. package/dist/oauth2/create-authorization-url.mjs +42 -0
  66. package/dist/oauth2/index.d.mts +8 -2
  67. package/dist/oauth2/index.mjs +6 -2
  68. package/dist/oauth2/oauth-provider.d.mts +194 -0
  69. package/dist/oauth2/refresh-access-token.d.mts +36 -0
  70. package/dist/oauth2/refresh-access-token.mjs +58 -0
  71. package/dist/oauth2/utils.d.mts +7 -0
  72. package/dist/oauth2/utils.mjs +27 -0
  73. package/dist/oauth2/validate-authorization-code.d.mts +55 -0
  74. package/dist/oauth2/validate-authorization-code.mjs +71 -0
  75. package/dist/oauth2/verify.d.mts +49 -0
  76. package/dist/oauth2/verify.mjs +95 -0
  77. package/dist/social-providers/apple.d.mts +119 -0
  78. package/dist/social-providers/apple.mjs +102 -0
  79. package/dist/social-providers/atlassian.d.mts +72 -0
  80. package/dist/social-providers/atlassian.mjs +83 -0
  81. package/dist/social-providers/cognito.d.mts +87 -0
  82. package/dist/social-providers/cognito.mjs +165 -0
  83. package/dist/social-providers/discord.d.mts +126 -0
  84. package/dist/social-providers/discord.mjs +64 -0
  85. package/dist/social-providers/dropbox.d.mts +71 -0
  86. package/dist/social-providers/dropbox.mjs +75 -0
  87. package/dist/social-providers/facebook.d.mts +81 -0
  88. package/dist/social-providers/facebook.mjs +120 -0
  89. package/dist/social-providers/figma.d.mts +63 -0
  90. package/dist/social-providers/figma.mjs +84 -0
  91. package/dist/social-providers/github.d.mts +104 -0
  92. package/dist/social-providers/github.mjs +80 -0
  93. package/dist/social-providers/gitlab.d.mts +125 -0
  94. package/dist/social-providers/gitlab.mjs +82 -0
  95. package/dist/social-providers/google.d.mts +99 -0
  96. package/dist/social-providers/google.mjs +108 -0
  97. package/dist/social-providers/huggingface.d.mts +85 -0
  98. package/dist/social-providers/huggingface.mjs +75 -0
  99. package/dist/social-providers/index.d.mts +1723 -1
  100. package/dist/social-providers/index.mjs +33 -2569
  101. package/dist/social-providers/kakao.d.mts +163 -0
  102. package/dist/social-providers/kakao.mjs +72 -0
  103. package/dist/social-providers/kick.d.mts +75 -0
  104. package/dist/social-providers/kick.mjs +71 -0
  105. package/dist/social-providers/line.d.mts +107 -0
  106. package/dist/social-providers/line.mjs +113 -0
  107. package/dist/social-providers/linear.d.mts +70 -0
  108. package/dist/social-providers/linear.mjs +88 -0
  109. package/dist/social-providers/linkedin.d.mts +69 -0
  110. package/dist/social-providers/linkedin.mjs +76 -0
  111. package/dist/social-providers/microsoft-entra-id.d.mts +174 -0
  112. package/dist/social-providers/microsoft-entra-id.mjs +106 -0
  113. package/dist/social-providers/naver.d.mts +104 -0
  114. package/dist/social-providers/naver.mjs +67 -0
  115. package/dist/social-providers/notion.d.mts +66 -0
  116. package/dist/social-providers/notion.mjs +75 -0
  117. package/dist/social-providers/paybin.d.mts +73 -0
  118. package/dist/social-providers/paybin.mjs +85 -0
  119. package/dist/social-providers/paypal.d.mts +131 -0
  120. package/dist/social-providers/paypal.mjs +144 -0
  121. package/dist/social-providers/polar.d.mts +76 -0
  122. package/dist/social-providers/polar.mjs +73 -0
  123. package/dist/social-providers/reddit.d.mts +64 -0
  124. package/dist/social-providers/reddit.mjs +83 -0
  125. package/dist/social-providers/roblox.d.mts +72 -0
  126. package/dist/social-providers/roblox.mjs +59 -0
  127. package/dist/social-providers/salesforce.d.mts +81 -0
  128. package/dist/social-providers/salesforce.mjs +91 -0
  129. package/dist/social-providers/slack.d.mts +85 -0
  130. package/dist/social-providers/slack.mjs +68 -0
  131. package/dist/social-providers/spotify.d.mts +65 -0
  132. package/dist/social-providers/spotify.mjs +71 -0
  133. package/dist/social-providers/tiktok.d.mts +171 -0
  134. package/dist/social-providers/tiktok.mjs +62 -0
  135. package/dist/social-providers/twitch.d.mts +81 -0
  136. package/dist/social-providers/twitch.mjs +78 -0
  137. package/dist/social-providers/twitter.d.mts +140 -0
  138. package/dist/social-providers/twitter.mjs +87 -0
  139. package/dist/social-providers/vercel.d.mts +64 -0
  140. package/dist/social-providers/vercel.mjs +61 -0
  141. package/dist/social-providers/vk.d.mts +72 -0
  142. package/dist/social-providers/vk.mjs +83 -0
  143. package/dist/social-providers/zoom.d.mts +173 -0
  144. package/dist/social-providers/zoom.mjs +72 -0
  145. package/dist/types/context.d.mts +246 -0
  146. package/dist/types/cookie.d.mts +23 -0
  147. package/dist/types/helper.d.mts +8 -0
  148. package/dist/types/index.d.mts +8 -0
  149. package/dist/types/init-options.d.mts +1266 -0
  150. package/dist/types/plugin-client.d.mts +110 -0
  151. package/dist/types/plugin.d.mts +124 -0
  152. package/dist/utils/deprecate.d.mts +10 -0
  153. package/dist/utils/deprecate.mjs +17 -0
  154. package/dist/utils/{index.d.mts → error-codes.d.mts} +1 -19
  155. package/dist/utils/error-codes.mjs +11 -0
  156. package/dist/utils/id.d.mts +4 -0
  157. package/dist/utils/id.mjs +9 -0
  158. package/dist/utils/json.d.mts +4 -0
  159. package/dist/utils/json.mjs +25 -0
  160. package/dist/utils/string.d.mts +4 -0
  161. package/dist/utils/string.mjs +7 -0
  162. package/package.json +9 -6
  163. package/src/context/endpoint-context.ts +11 -2
  164. package/src/context/index.ts +0 -29
  165. package/src/context/request-state.ts +8 -2
  166. package/src/context/transaction.ts +11 -2
  167. package/src/db/adapter/get-id-field.ts +1 -1
  168. package/src/error/codes.ts +1 -1
  169. package/src/oauth2/create-authorization-url.ts +1 -1
  170. package/src/oauth2/oauth-provider.ts +6 -0
  171. package/tsdown.config.ts +3 -1
  172. package/dist/context-BBNwughv.mjs +0 -133
  173. package/dist/env-DbssmzoK.mjs +0 -245
  174. package/dist/index-B5x_W0dM.d.mts +0 -8054
  175. package/dist/oauth2-BjWM15hm.mjs +0 -326
  176. package/dist/utils/index.mjs +0 -4
  177. package/dist/utils-puAL36Bz.mjs +0 -63
  178. package/src/utils/index.ts +0 -5
@@ -0,0 +1,58 @@
1
+ import { base64 } from "@better-auth/utils/base64";
2
+ import { betterFetch } from "@better-fetch/fetch";
3
+
4
+ //#region src/oauth2/refresh-access-token.ts
5
+ function createRefreshAccessTokenRequest({ refreshToken, options, authentication, extraParams, resource }) {
6
+ const body = new URLSearchParams();
7
+ const headers = {
8
+ "content-type": "application/x-www-form-urlencoded",
9
+ accept: "application/json"
10
+ };
11
+ body.set("grant_type", "refresh_token");
12
+ body.set("refresh_token", refreshToken);
13
+ if (authentication === "basic") {
14
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
15
+ if (primaryClientId) headers["authorization"] = "Basic " + base64.encode(`${primaryClientId}:${options.clientSecret ?? ""}`);
16
+ else headers["authorization"] = "Basic " + base64.encode(`:${options.clientSecret ?? ""}`);
17
+ } else {
18
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
19
+ body.set("client_id", primaryClientId);
20
+ if (options.clientSecret) body.set("client_secret", options.clientSecret);
21
+ }
22
+ if (resource) if (typeof resource === "string") body.append("resource", resource);
23
+ else for (const _resource of resource) body.append("resource", _resource);
24
+ if (extraParams) for (const [key, value] of Object.entries(extraParams)) body.set(key, value);
25
+ return {
26
+ body,
27
+ headers
28
+ };
29
+ }
30
+ async function refreshAccessToken({ refreshToken, options, tokenEndpoint, authentication, extraParams }) {
31
+ const { body, headers } = createRefreshAccessTokenRequest({
32
+ refreshToken,
33
+ options,
34
+ authentication,
35
+ extraParams
36
+ });
37
+ const { data, error } = await betterFetch(tokenEndpoint, {
38
+ method: "POST",
39
+ body,
40
+ headers
41
+ });
42
+ if (error) throw error;
43
+ const tokens = {
44
+ accessToken: data.access_token,
45
+ refreshToken: data.refresh_token,
46
+ tokenType: data.token_type,
47
+ scopes: data.scope?.split(" "),
48
+ idToken: data.id_token
49
+ };
50
+ if (data.expires_in) {
51
+ const now = /* @__PURE__ */ new Date();
52
+ tokens.accessTokenExpiresAt = new Date(now.getTime() + data.expires_in * 1e3);
53
+ }
54
+ return tokens;
55
+ }
56
+
57
+ //#endregion
58
+ export { createRefreshAccessTokenRequest, refreshAccessToken };
@@ -0,0 +1,7 @@
1
+ import { OAuth2Tokens } from "./oauth-provider.mjs";
2
+
3
+ //#region src/oauth2/utils.d.ts
4
+ declare function getOAuth2Tokens(data: Record<string, any>): OAuth2Tokens;
5
+ declare function generateCodeChallenge(codeVerifier: string): Promise<string>;
6
+ //#endregion
7
+ export { generateCodeChallenge, getOAuth2Tokens };
@@ -0,0 +1,27 @@
1
+ import { base64Url } from "@better-auth/utils/base64";
2
+
3
+ //#region src/oauth2/utils.ts
4
+ function getOAuth2Tokens(data) {
5
+ const getDate = (seconds) => {
6
+ const now = /* @__PURE__ */ new Date();
7
+ return new Date(now.getTime() + seconds * 1e3);
8
+ };
9
+ return {
10
+ tokenType: data.token_type,
11
+ accessToken: data.access_token,
12
+ refreshToken: data.refresh_token,
13
+ accessTokenExpiresAt: data.expires_in ? getDate(data.expires_in) : void 0,
14
+ refreshTokenExpiresAt: data.refresh_token_expires_in ? getDate(data.refresh_token_expires_in) : void 0,
15
+ scopes: data?.scope ? typeof data.scope === "string" ? data.scope.split(" ") : data.scope : [],
16
+ idToken: data.id_token,
17
+ raw: data
18
+ };
19
+ }
20
+ async function generateCodeChallenge(codeVerifier) {
21
+ const data = new TextEncoder().encode(codeVerifier);
22
+ const hash = await crypto.subtle.digest("SHA-256", data);
23
+ return base64Url.encode(new Uint8Array(hash), { padding: false });
24
+ }
25
+
26
+ //#endregion
27
+ export { generateCodeChallenge, getOAuth2Tokens };
@@ -0,0 +1,55 @@
1
+ import { OAuth2Tokens, ProviderOptions } from "./oauth-provider.mjs";
2
+ import "./index.mjs";
3
+ import * as jose0 from "jose";
4
+
5
+ //#region src/oauth2/validate-authorization-code.d.ts
6
+ declare function createAuthorizationCodeRequest({
7
+ code,
8
+ codeVerifier,
9
+ redirectURI,
10
+ options,
11
+ authentication,
12
+ deviceId,
13
+ headers,
14
+ additionalParams,
15
+ resource
16
+ }: {
17
+ code: string;
18
+ redirectURI: string;
19
+ options: Partial<ProviderOptions>;
20
+ codeVerifier?: string | undefined;
21
+ deviceId?: string | undefined;
22
+ authentication?: ("basic" | "post") | undefined;
23
+ headers?: Record<string, string> | undefined;
24
+ additionalParams?: Record<string, string> | undefined;
25
+ resource?: (string | string[]) | undefined;
26
+ }): {
27
+ body: URLSearchParams;
28
+ headers: Record<string, any>;
29
+ };
30
+ declare function validateAuthorizationCode({
31
+ code,
32
+ codeVerifier,
33
+ redirectURI,
34
+ options,
35
+ tokenEndpoint,
36
+ authentication,
37
+ deviceId,
38
+ headers,
39
+ additionalParams,
40
+ resource
41
+ }: {
42
+ code: string;
43
+ redirectURI: string;
44
+ options: Partial<ProviderOptions>;
45
+ codeVerifier?: string | undefined;
46
+ deviceId?: string | undefined;
47
+ tokenEndpoint: string;
48
+ authentication?: ("basic" | "post") | undefined;
49
+ headers?: Record<string, string> | undefined;
50
+ additionalParams?: Record<string, string> | undefined;
51
+ resource?: (string | string[]) | undefined;
52
+ }): Promise<OAuth2Tokens>;
53
+ declare function validateToken(token: string, jwksEndpoint: string): Promise<jose0.JWTVerifyResult<jose0.JWTPayload>>;
54
+ //#endregion
55
+ export { createAuthorizationCodeRequest, validateAuthorizationCode, validateToken };
@@ -0,0 +1,71 @@
1
+ import { getOAuth2Tokens } from "./utils.mjs";
2
+ import "./index.mjs";
3
+ import { base64 } from "@better-auth/utils/base64";
4
+ import { betterFetch } from "@better-fetch/fetch";
5
+ import { jwtVerify } from "jose";
6
+
7
+ //#region src/oauth2/validate-authorization-code.ts
8
+ function createAuthorizationCodeRequest({ code, codeVerifier, redirectURI, options, authentication, deviceId, headers, additionalParams = {}, resource }) {
9
+ const body = new URLSearchParams();
10
+ const requestHeaders = {
11
+ "content-type": "application/x-www-form-urlencoded",
12
+ accept: "application/json",
13
+ ...headers
14
+ };
15
+ body.set("grant_type", "authorization_code");
16
+ body.set("code", code);
17
+ codeVerifier && body.set("code_verifier", codeVerifier);
18
+ options.clientKey && body.set("client_key", options.clientKey);
19
+ deviceId && body.set("device_id", deviceId);
20
+ body.set("redirect_uri", options.redirectURI || redirectURI);
21
+ if (resource) if (typeof resource === "string") body.append("resource", resource);
22
+ else for (const _resource of resource) body.append("resource", _resource);
23
+ if (authentication === "basic") {
24
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
25
+ requestHeaders["authorization"] = `Basic ${base64.encode(`${primaryClientId}:${options.clientSecret ?? ""}`)}`;
26
+ } else {
27
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
28
+ body.set("client_id", primaryClientId);
29
+ if (options.clientSecret) body.set("client_secret", options.clientSecret);
30
+ }
31
+ for (const [key, value] of Object.entries(additionalParams)) if (!body.has(key)) body.append(key, value);
32
+ return {
33
+ body,
34
+ headers: requestHeaders
35
+ };
36
+ }
37
+ async function validateAuthorizationCode({ code, codeVerifier, redirectURI, options, tokenEndpoint, authentication, deviceId, headers, additionalParams = {}, resource }) {
38
+ const { body, headers: requestHeaders } = createAuthorizationCodeRequest({
39
+ code,
40
+ codeVerifier,
41
+ redirectURI,
42
+ options,
43
+ authentication,
44
+ deviceId,
45
+ headers,
46
+ additionalParams,
47
+ resource
48
+ });
49
+ const { data, error } = await betterFetch(tokenEndpoint, {
50
+ method: "POST",
51
+ body,
52
+ headers: requestHeaders
53
+ });
54
+ if (error) throw error;
55
+ return getOAuth2Tokens(data);
56
+ }
57
+ async function validateToken(token, jwksEndpoint) {
58
+ const { data, error } = await betterFetch(jwksEndpoint, {
59
+ method: "GET",
60
+ headers: { accept: "application/json" }
61
+ });
62
+ if (error) throw error;
63
+ const keys = data["keys"];
64
+ const header = JSON.parse(atob(token.split(".")[0]));
65
+ const key = keys.find((key$1) => key$1.kid === header.kid);
66
+ if (!key) throw new Error("Key not found");
67
+ return await jwtVerify(token, key);
68
+ }
69
+
70
+ //#endregion
71
+ export { createAuthorizationCodeRequest, validateAuthorizationCode, validateToken };
@@ -0,0 +1,49 @@
1
+ import { JSONWebKeySet, JWTPayload, JWTVerifyOptions } from "jose";
2
+
3
+ //#region src/oauth2/verify.d.ts
4
+ interface VerifyAccessTokenRemote {
5
+ /** Full url of the introspect endpoint. Should end with `/oauth2/introspect` */
6
+ introspectUrl: string;
7
+ /** Client Secret */
8
+ clientId: string;
9
+ /** Client Secret */
10
+ clientSecret: string;
11
+ /**
12
+ * Forces remote verification of a token.
13
+ * This ensures attached session (if applicable)
14
+ * is also still active.
15
+ */
16
+ force?: boolean;
17
+ }
18
+ /**
19
+ * Performs local verification of an access token for your APIs.
20
+ *
21
+ * Can also be configured for remote verification.
22
+ */
23
+ declare function verifyJwsAccessToken(token: string, opts: {
24
+ /** Jwks url or promise of a Jwks */
25
+ jwksFetch: string | (() => Promise<JSONWebKeySet | undefined>);
26
+ /** Verify options */
27
+ verifyOptions: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience" | "issuer">>;
28
+ }): Promise<JWTPayload>;
29
+ declare function getJwks(token: string, opts: {
30
+ /** Jwks url or promise of a Jwks */
31
+ jwksFetch: string | (() => Promise<JSONWebKeySet | undefined>);
32
+ }): Promise<JSONWebKeySet>;
33
+ /**
34
+ * Performs local verification of an access token for your API.
35
+ *
36
+ * Can also be configured for remote verification.
37
+ */
38
+ declare function verifyAccessToken(token: string, opts: {
39
+ /** Verify options */
40
+ verifyOptions: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience" | "issuer">>;
41
+ /** Scopes to additionally verify. Token must include all but not exact. */
42
+ scopes?: string[];
43
+ /** Required to verify access token locally */
44
+ jwksUrl?: string;
45
+ /** If provided, can verify a token remotely */
46
+ remoteVerify?: VerifyAccessTokenRemote;
47
+ }): Promise<JWTPayload>;
48
+ //#endregion
49
+ export { getJwks, verifyAccessToken, verifyJwsAccessToken };
@@ -0,0 +1,95 @@
1
+ import { logger } from "../env/logger.mjs";
2
+ import "../env/index.mjs";
3
+ import { APIError } from "better-call";
4
+ import { betterFetch } from "@better-fetch/fetch";
5
+ import { UnsecuredJWT, createLocalJWKSet, decodeProtectedHeader, jwtVerify } from "jose";
6
+
7
+ //#region src/oauth2/verify.ts
8
+ /** Last fetched jwks used locally in getJwks @internal */
9
+ let jwks;
10
+ /**
11
+ * Performs local verification of an access token for your APIs.
12
+ *
13
+ * Can also be configured for remote verification.
14
+ */
15
+ async function verifyJwsAccessToken(token, opts) {
16
+ try {
17
+ const jwt = await jwtVerify(token, createLocalJWKSet(await getJwks(token, opts)), opts.verifyOptions);
18
+ if (jwt.payload.azp) jwt.payload.client_id = jwt.payload.azp;
19
+ return jwt.payload;
20
+ } catch (error) {
21
+ if (error instanceof Error) throw error;
22
+ throw new Error(error);
23
+ }
24
+ }
25
+ async function getJwks(token, opts) {
26
+ let jwtHeaders;
27
+ try {
28
+ jwtHeaders = decodeProtectedHeader(token);
29
+ } catch (error) {
30
+ if (error instanceof Error) throw error;
31
+ throw new Error(error);
32
+ }
33
+ if (!jwtHeaders.kid) throw new Error("Missing jwt kid");
34
+ if (!jwks || !jwks.keys.find((jwk) => jwk.kid === jwtHeaders.kid)) {
35
+ jwks = typeof opts.jwksFetch === "string" ? await betterFetch(opts.jwksFetch, { headers: { Accept: "application/json" } }).then(async (res) => {
36
+ if (res.error) throw new Error(`Jwks failed: ${res.error.message ?? res.error.statusText}`);
37
+ return res.data;
38
+ }) : await opts.jwksFetch();
39
+ if (!jwks) throw new Error("No jwks found");
40
+ }
41
+ return jwks;
42
+ }
43
+ /**
44
+ * Performs local verification of an access token for your API.
45
+ *
46
+ * Can also be configured for remote verification.
47
+ */
48
+ async function verifyAccessToken(token, opts) {
49
+ let payload;
50
+ if (opts.jwksUrl && !opts?.remoteVerify?.force) try {
51
+ payload = await verifyJwsAccessToken(token, {
52
+ jwksFetch: opts.jwksUrl,
53
+ verifyOptions: opts.verifyOptions
54
+ });
55
+ } catch (error) {
56
+ if (error instanceof Error) if (error.name === "TypeError" || error.name === "JWSInvalid") {} else if (error.name === "JWTExpired") throw new APIError("UNAUTHORIZED", { message: "token expired" });
57
+ else if (error.name === "JWTInvalid") throw new APIError("UNAUTHORIZED", { message: "token invalid" });
58
+ else throw error;
59
+ else throw new Error(error);
60
+ }
61
+ if (opts?.remoteVerify) {
62
+ const { data: introspect, error: introspectError } = await betterFetch(opts.remoteVerify.introspectUrl, {
63
+ method: "POST",
64
+ headers: {
65
+ Accept: "application/json",
66
+ "Content-Type": "application/x-www-form-urlencoded"
67
+ },
68
+ body: new URLSearchParams({
69
+ client_id: opts.remoteVerify.clientId,
70
+ client_secret: opts.remoteVerify.clientSecret,
71
+ token,
72
+ token_type_hint: "access_token"
73
+ }).toString()
74
+ });
75
+ if (introspectError) logger.error(`Introspection failed: ${introspectError.message ?? introspectError.statusText}`);
76
+ if (!introspect) throw new APIError("INTERNAL_SERVER_ERROR", { message: "introspection failed" });
77
+ if (!introspect.active) throw new APIError("UNAUTHORIZED", { message: "token inactive" });
78
+ try {
79
+ const unsecuredJwt = new UnsecuredJWT(introspect).encode();
80
+ const { audience: _audience, ...verifyOptions } = opts.verifyOptions;
81
+ payload = (introspect.aud ? UnsecuredJWT.decode(unsecuredJwt, opts.verifyOptions) : UnsecuredJWT.decode(unsecuredJwt, verifyOptions)).payload;
82
+ } catch (error) {
83
+ throw new Error(error);
84
+ }
85
+ }
86
+ if (!payload) throw new APIError("UNAUTHORIZED", { message: `no token payload` });
87
+ if (opts.scopes) {
88
+ const validScopes = new Set(payload.scope?.split(" "));
89
+ for (const sc of opts.scopes) if (!validScopes.has(sc)) throw new APIError("FORBIDDEN", { message: `invalid scope ${sc}` });
90
+ }
91
+ return payload;
92
+ }
93
+
94
+ //#endregion
95
+ export { getJwks, verifyAccessToken, verifyJwsAccessToken };
@@ -0,0 +1,119 @@
1
+ import { OAuth2Tokens, ProviderOptions } from "../oauth2/oauth-provider.mjs";
2
+ import "../oauth2/index.mjs";
3
+
4
+ //#region src/social-providers/apple.d.ts
5
+ interface AppleProfile {
6
+ /**
7
+ * The subject registered claim identifies the principal that’s the subject
8
+ * of the identity token. Because this token is for your app, the value is
9
+ * the unique identifier for the user.
10
+ */
11
+ sub: string;
12
+ /**
13
+ * A String value representing the user's email address.
14
+ * The email address is either the user's real email address or the proxy
15
+ * address, depending on their status private email relay service.
16
+ */
17
+ email: string;
18
+ /**
19
+ * A string or Boolean value that indicates whether the service verifies
20
+ * the email. The value can either be a string ("true" or "false") or a
21
+ * Boolean (true or false). The system may not verify email addresses for
22
+ * Sign in with Apple at Work & School users, and this claim is "false" or
23
+ * false for those users.
24
+ */
25
+ email_verified: true | "true";
26
+ /**
27
+ * A string or Boolean value that indicates whether the email that the user
28
+ * shares is the proxy address. The value can either be a string ("true" or
29
+ * "false") or a Boolean (true or false).
30
+ */
31
+ is_private_email: boolean;
32
+ /**
33
+ * An Integer value that indicates whether the user appears to be a real
34
+ * person. Use the value of this claim to mitigate fraud. The possible
35
+ * values are: 0 (or Unsupported), 1 (or Unknown), 2 (or LikelyReal). For
36
+ * more information, see ASUserDetectionStatus. This claim is present only
37
+ * in iOS 14 and later, macOS 11 and later, watchOS 7 and later, tvOS 14
38
+ * and later. The claim isn’t present or supported for web-based apps.
39
+ */
40
+ real_user_status: number;
41
+ /**
42
+ * The user’s full name in the format provided during the authorization
43
+ * process.
44
+ */
45
+ name: string;
46
+ /**
47
+ * The URL to the user's profile picture.
48
+ */
49
+ picture: string;
50
+ user?: AppleNonConformUser | undefined;
51
+ }
52
+ /**
53
+ * This is the shape of the `user` query parameter that Apple sends the first
54
+ * time the user consents to the app.
55
+ * @see https://developer.apple.com/documentation/signinwithapplerestapi/request-an-authorization-to-the-sign-in-with-apple-server./
56
+ */
57
+ interface AppleNonConformUser {
58
+ name: {
59
+ firstName: string;
60
+ lastName: string;
61
+ };
62
+ email: string;
63
+ }
64
+ interface AppleOptions extends ProviderOptions<AppleProfile> {
65
+ clientId: string;
66
+ appBundleIdentifier?: string | undefined;
67
+ audience?: (string | string[]) | undefined;
68
+ }
69
+ declare const apple: (options: AppleOptions) => {
70
+ id: "apple";
71
+ name: string;
72
+ createAuthorizationURL({
73
+ state,
74
+ scopes,
75
+ redirectURI
76
+ }: {
77
+ state: string;
78
+ codeVerifier: string;
79
+ scopes?: string[] | undefined;
80
+ redirectURI: string;
81
+ display?: string | undefined;
82
+ loginHint?: string | undefined;
83
+ }): Promise<URL>;
84
+ validateAuthorizationCode: ({
85
+ code,
86
+ codeVerifier,
87
+ redirectURI
88
+ }: {
89
+ code: string;
90
+ redirectURI: string;
91
+ codeVerifier?: string | undefined;
92
+ deviceId?: string | undefined;
93
+ }) => Promise<OAuth2Tokens>;
94
+ verifyIdToken(token: string, nonce: string | undefined): Promise<boolean>;
95
+ refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
96
+ getUserInfo(token: OAuth2Tokens & {
97
+ user?: {
98
+ name?: {
99
+ firstName?: string;
100
+ lastName?: string;
101
+ };
102
+ email?: string;
103
+ } | undefined;
104
+ }): Promise<{
105
+ user: {
106
+ id: string;
107
+ name?: string;
108
+ email?: string | null;
109
+ image?: string;
110
+ emailVerified: boolean;
111
+ [key: string]: any;
112
+ };
113
+ data: any;
114
+ } | null>;
115
+ options: AppleOptions;
116
+ };
117
+ declare const getApplePublicKey: (kid: string) => Promise<Uint8Array<ArrayBufferLike> | CryptoKey>;
118
+ //#endregion
119
+ export { AppleNonConformUser, AppleOptions, AppleProfile, apple, getApplePublicKey };
@@ -0,0 +1,102 @@
1
+ import { APIError } from "../error/index.mjs";
2
+ import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
3
+ import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
4
+ import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
5
+ import "../oauth2/index.mjs";
6
+ import { betterFetch } from "@better-fetch/fetch";
7
+ import { decodeJwt, decodeProtectedHeader, importJWK, jwtVerify } from "jose";
8
+
9
+ //#region src/social-providers/apple.ts
10
+ const apple = (options) => {
11
+ const tokenEndpoint = "https://appleid.apple.com/auth/token";
12
+ return {
13
+ id: "apple",
14
+ name: "Apple",
15
+ async createAuthorizationURL({ state, scopes, redirectURI }) {
16
+ const _scope = options.disableDefaultScope ? [] : ["email", "name"];
17
+ if (options.scope) _scope.push(...options.scope);
18
+ if (scopes) _scope.push(...scopes);
19
+ return await createAuthorizationURL({
20
+ id: "apple",
21
+ options,
22
+ authorizationEndpoint: "https://appleid.apple.com/auth/authorize",
23
+ scopes: _scope,
24
+ state,
25
+ redirectURI,
26
+ responseMode: "form_post",
27
+ responseType: "code id_token"
28
+ });
29
+ },
30
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
31
+ return validateAuthorizationCode({
32
+ code,
33
+ codeVerifier,
34
+ redirectURI,
35
+ options,
36
+ tokenEndpoint
37
+ });
38
+ },
39
+ async verifyIdToken(token, nonce) {
40
+ if (options.disableIdTokenSignIn) return false;
41
+ if (options.verifyIdToken) return options.verifyIdToken(token, nonce);
42
+ const { kid, alg: jwtAlg } = decodeProtectedHeader(token);
43
+ if (!kid || !jwtAlg) return false;
44
+ const { payload: jwtClaims } = await jwtVerify(token, await getApplePublicKey(kid), {
45
+ algorithms: [jwtAlg],
46
+ issuer: "https://appleid.apple.com",
47
+ audience: options.audience && options.audience.length ? options.audience : options.appBundleIdentifier ? options.appBundleIdentifier : options.clientId,
48
+ maxTokenAge: "1h"
49
+ });
50
+ ["email_verified", "is_private_email"].forEach((field) => {
51
+ if (jwtClaims[field] !== void 0) jwtClaims[field] = Boolean(jwtClaims[field]);
52
+ });
53
+ if (nonce && jwtClaims.nonce !== nonce) return false;
54
+ return !!jwtClaims;
55
+ },
56
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
57
+ return refreshAccessToken({
58
+ refreshToken,
59
+ options: {
60
+ clientId: options.clientId,
61
+ clientKey: options.clientKey,
62
+ clientSecret: options.clientSecret
63
+ },
64
+ tokenEndpoint: "https://appleid.apple.com/auth/token"
65
+ });
66
+ },
67
+ async getUserInfo(token) {
68
+ if (options.getUserInfo) return options.getUserInfo(token);
69
+ if (!token.idToken) return null;
70
+ const profile = decodeJwt(token.idToken);
71
+ if (!profile) return null;
72
+ const name = token.user ? `${token.user.name?.firstName} ${token.user.name?.lastName}` : profile.name || profile.email;
73
+ const emailVerified = typeof profile.email_verified === "boolean" ? profile.email_verified : profile.email_verified === "true";
74
+ const enrichedProfile = {
75
+ ...profile,
76
+ name
77
+ };
78
+ const userMap = await options.mapProfileToUser?.(enrichedProfile);
79
+ return {
80
+ user: {
81
+ id: profile.sub,
82
+ name: enrichedProfile.name,
83
+ emailVerified,
84
+ email: profile.email,
85
+ ...userMap
86
+ },
87
+ data: enrichedProfile
88
+ };
89
+ },
90
+ options
91
+ };
92
+ };
93
+ const getApplePublicKey = async (kid) => {
94
+ const { data } = await betterFetch(`https://appleid.apple.com/auth/keys`);
95
+ if (!data?.keys) throw new APIError("BAD_REQUEST", { message: "Keys not found" });
96
+ const jwk = data.keys.find((key) => key.kid === kid);
97
+ if (!jwk) throw new Error(`JWK with kid ${kid} not found`);
98
+ return await importJWK(jwk, jwk.alg);
99
+ };
100
+
101
+ //#endregion
102
+ export { apple, getApplePublicKey };
@@ -0,0 +1,72 @@
1
+ import { OAuth2Tokens, ProviderOptions } from "../oauth2/oauth-provider.mjs";
2
+ import "../oauth2/index.mjs";
3
+
4
+ //#region src/social-providers/atlassian.d.ts
5
+ interface AtlassianProfile {
6
+ account_type?: string | undefined;
7
+ account_id: string;
8
+ email?: string | undefined;
9
+ name: string;
10
+ picture?: string | undefined;
11
+ nickname?: string | undefined;
12
+ locale?: string | undefined;
13
+ extended_profile?: {
14
+ job_title?: string;
15
+ organization?: string;
16
+ department?: string;
17
+ location?: string;
18
+ } | undefined;
19
+ }
20
+ interface AtlassianOptions extends ProviderOptions<AtlassianProfile> {
21
+ clientId: string;
22
+ }
23
+ declare const atlassian: (options: AtlassianOptions) => {
24
+ id: "atlassian";
25
+ name: string;
26
+ createAuthorizationURL({
27
+ state,
28
+ scopes,
29
+ codeVerifier,
30
+ redirectURI
31
+ }: {
32
+ state: string;
33
+ codeVerifier: string;
34
+ scopes?: string[] | undefined;
35
+ redirectURI: string;
36
+ display?: string | undefined;
37
+ loginHint?: string | undefined;
38
+ }): Promise<URL>;
39
+ validateAuthorizationCode: ({
40
+ code,
41
+ codeVerifier,
42
+ redirectURI
43
+ }: {
44
+ code: string;
45
+ redirectURI: string;
46
+ codeVerifier?: string | undefined;
47
+ deviceId?: string | undefined;
48
+ }) => Promise<OAuth2Tokens>;
49
+ refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
50
+ getUserInfo(token: OAuth2Tokens & {
51
+ user?: {
52
+ name?: {
53
+ firstName?: string;
54
+ lastName?: string;
55
+ };
56
+ email?: string;
57
+ } | undefined;
58
+ }): Promise<{
59
+ user: {
60
+ id: string;
61
+ name?: string;
62
+ email?: string | null;
63
+ image?: string;
64
+ emailVerified: boolean;
65
+ [key: string]: any;
66
+ };
67
+ data: any;
68
+ } | null>;
69
+ options: AtlassianOptions;
70
+ };
71
+ //#endregion
72
+ export { AtlassianOptions, AtlassianProfile, atlassian };