@depup/better-auth__core 1.6.23-depup.2
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.
- package/LICENSE.md +20 -0
- package/README.md +32 -0
- package/changes.json +14 -0
- package/dist/api/index.d.mts +281 -0
- package/dist/api/index.mjs +92 -0
- package/dist/async_hooks/index.d.mts +6 -0
- package/dist/async_hooks/index.mjs +20 -0
- package/dist/async_hooks/pure.index.d.mts +6 -0
- package/dist/async_hooks/pure.index.mjs +33 -0
- package/dist/context/endpoint-context.d.mts +18 -0
- package/dist/context/endpoint-context.mjs +29 -0
- package/dist/context/global.d.mts +6 -0
- package/dist/context/global.mjs +36 -0
- package/dist/context/index.d.mts +5 -0
- package/dist/context/index.mjs +5 -0
- package/dist/context/request-state.d.mts +27 -0
- package/dist/context/request-state.mjs +47 -0
- package/dist/context/transaction.d.mts +27 -0
- package/dist/context/transaction.mjs +96 -0
- package/dist/db/adapter/factory.d.mts +17 -0
- package/dist/db/adapter/factory.mjs +905 -0
- package/dist/db/adapter/get-default-field-name.d.mts +18 -0
- package/dist/db/adapter/get-default-field-name.mjs +36 -0
- package/dist/db/adapter/get-default-model-name.d.mts +12 -0
- package/dist/db/adapter/get-default-model-name.mjs +30 -0
- package/dist/db/adapter/get-field-attributes.d.mts +26 -0
- package/dist/db/adapter/get-field-attributes.mjs +37 -0
- package/dist/db/adapter/get-field-name.d.mts +18 -0
- package/dist/db/adapter/get-field-name.mjs +31 -0
- package/dist/db/adapter/get-id-field.d.mts +36 -0
- package/dist/db/adapter/get-id-field.mjs +64 -0
- package/dist/db/adapter/get-model-name.d.mts +12 -0
- package/dist/db/adapter/get-model-name.mjs +21 -0
- package/dist/db/adapter/index.d.mts +615 -0
- package/dist/db/adapter/index.mjs +24 -0
- package/dist/db/adapter/types.d.mts +105 -0
- package/dist/db/adapter/utils.d.mts +7 -0
- package/dist/db/adapter/utils.mjs +37 -0
- package/dist/db/get-tables.d.mts +6 -0
- package/dist/db/get-tables.mjs +266 -0
- package/dist/db/index.d.mts +10 -0
- package/dist/db/index.mjs +8 -0
- package/dist/db/plugin.d.mts +12 -0
- package/dist/db/schema/account.d.mts +28 -0
- package/dist/db/schema/account.mjs +17 -0
- package/dist/db/schema/rate-limit.d.mts +18 -0
- package/dist/db/schema/rate-limit.mjs +9 -0
- package/dist/db/schema/session.d.mts +23 -0
- package/dist/db/schema/session.mjs +12 -0
- package/dist/db/schema/shared.d.mts +10 -0
- package/dist/db/schema/shared.mjs +9 -0
- package/dist/db/schema/user.d.mts +22 -0
- package/dist/db/schema/user.mjs +11 -0
- package/dist/db/schema/verification.d.mts +21 -0
- package/dist/db/schema/verification.mjs +10 -0
- package/dist/db/type.d.mts +194 -0
- package/dist/env/color-depth.d.mts +4 -0
- package/dist/env/color-depth.mjs +86 -0
- package/dist/env/env-impl.d.mts +32 -0
- package/dist/env/env-impl.mjs +81 -0
- package/dist/env/index.d.mts +4 -0
- package/dist/env/index.mjs +4 -0
- package/dist/env/logger.d.mts +48 -0
- package/dist/env/logger.mjs +79 -0
- package/dist/error/codes.d.mts +69 -0
- package/dist/error/codes.mjs +55 -0
- package/dist/error/index.d.mts +26 -0
- package/dist/error/index.mjs +27 -0
- package/dist/index.d.mts +9 -0
- package/dist/index.mjs +1 -0
- package/dist/instrumentation/api.mjs +12 -0
- package/dist/instrumentation/attributes.d.mts +11 -0
- package/dist/instrumentation/attributes.mjs +10 -0
- package/dist/instrumentation/index.d.mts +3 -0
- package/dist/instrumentation/index.mjs +3 -0
- package/dist/instrumentation/noop.mjs +42 -0
- package/dist/instrumentation/pure.index.d.mts +7 -0
- package/dist/instrumentation/pure.index.mjs +7 -0
- package/dist/instrumentation/tracer.d.mts +13 -0
- package/dist/instrumentation/tracer.mjs +53 -0
- package/dist/oauth2/client-credentials-token.d.mts +56 -0
- package/dist/oauth2/client-credentials-token.mjs +64 -0
- package/dist/oauth2/create-authorization-url.d.mts +44 -0
- package/dist/oauth2/create-authorization-url.mjs +41 -0
- package/dist/oauth2/index.d.mts +8 -0
- package/dist/oauth2/index.mjs +7 -0
- package/dist/oauth2/oauth-provider.d.mts +192 -0
- package/dist/oauth2/refresh-access-token.d.mts +54 -0
- package/dist/oauth2/refresh-access-token.mjs +73 -0
- package/dist/oauth2/reject-redirects.mjs +60 -0
- package/dist/oauth2/utils.d.mts +24 -0
- package/dist/oauth2/utils.mjs +48 -0
- package/dist/oauth2/validate-authorization-code.d.mts +85 -0
- package/dist/oauth2/validate-authorization-code.mjs +86 -0
- package/dist/oauth2/verify.d.mts +65 -0
- package/dist/oauth2/verify.mjs +202 -0
- package/dist/social-providers/apple.d.mts +133 -0
- package/dist/social-providers/apple.mjs +117 -0
- package/dist/social-providers/atlassian.d.mts +70 -0
- package/dist/social-providers/atlassian.mjs +80 -0
- package/dist/social-providers/cognito.d.mts +85 -0
- package/dist/social-providers/cognito.mjs +162 -0
- package/dist/social-providers/discord.d.mts +124 -0
- package/dist/social-providers/discord.mjs +62 -0
- package/dist/social-providers/dropbox.d.mts +69 -0
- package/dist/social-providers/dropbox.mjs +72 -0
- package/dist/social-providers/facebook.d.mts +79 -0
- package/dist/social-providers/facebook.mjs +157 -0
- package/dist/social-providers/figma.d.mts +61 -0
- package/dist/social-providers/figma.mjs +83 -0
- package/dist/social-providers/github.d.mts +102 -0
- package/dist/social-providers/github.mjs +92 -0
- package/dist/social-providers/gitlab.d.mts +123 -0
- package/dist/social-providers/gitlab.mjs +79 -0
- package/dist/social-providers/google.d.mts +123 -0
- package/dist/social-providers/google.mjs +137 -0
- package/dist/social-providers/huggingface.d.mts +83 -0
- package/dist/social-providers/huggingface.mjs +73 -0
- package/dist/social-providers/index.d.mts +1834 -0
- package/dist/social-providers/index.mjs +78 -0
- package/dist/social-providers/kakao.d.mts +161 -0
- package/dist/social-providers/kakao.mjs +70 -0
- package/dist/social-providers/kick.d.mts +73 -0
- package/dist/social-providers/kick.mjs +68 -0
- package/dist/social-providers/line.d.mts +105 -0
- package/dist/social-providers/line.mjs +110 -0
- package/dist/social-providers/linear.d.mts +68 -0
- package/dist/social-providers/linear.mjs +85 -0
- package/dist/social-providers/linkedin.d.mts +67 -0
- package/dist/social-providers/linkedin.mjs +73 -0
- package/dist/social-providers/microsoft-entra-id.d.mts +174 -0
- package/dist/social-providers/microsoft-entra-id.mjs +152 -0
- package/dist/social-providers/naver.d.mts +92 -0
- package/dist/social-providers/naver.mjs +65 -0
- package/dist/social-providers/notion.d.mts +64 -0
- package/dist/social-providers/notion.mjs +72 -0
- package/dist/social-providers/paybin.d.mts +71 -0
- package/dist/social-providers/paybin.mjs +81 -0
- package/dist/social-providers/paypal.d.mts +131 -0
- package/dist/social-providers/paypal.mjs +188 -0
- package/dist/social-providers/polar.d.mts +74 -0
- package/dist/social-providers/polar.mjs +71 -0
- package/dist/social-providers/railway.d.mts +65 -0
- package/dist/social-providers/railway.mjs +74 -0
- package/dist/social-providers/reddit.d.mts +62 -0
- package/dist/social-providers/reddit.mjs +81 -0
- package/dist/social-providers/roblox.d.mts +70 -0
- package/dist/social-providers/roblox.mjs +57 -0
- package/dist/social-providers/salesforce.d.mts +79 -0
- package/dist/social-providers/salesforce.mjs +87 -0
- package/dist/social-providers/slack.d.mts +83 -0
- package/dist/social-providers/slack.mjs +66 -0
- package/dist/social-providers/spotify.d.mts +63 -0
- package/dist/social-providers/spotify.mjs +69 -0
- package/dist/social-providers/tiktok.d.mts +168 -0
- package/dist/social-providers/tiktok.mjs +60 -0
- package/dist/social-providers/twitch.d.mts +79 -0
- package/dist/social-providers/twitch.mjs +75 -0
- package/dist/social-providers/twitter.d.mts +126 -0
- package/dist/social-providers/twitter.mjs +85 -0
- package/dist/social-providers/vercel.d.mts +62 -0
- package/dist/social-providers/vercel.mjs +58 -0
- package/dist/social-providers/vk.d.mts +70 -0
- package/dist/social-providers/vk.mjs +81 -0
- package/dist/social-providers/wechat.d.mts +113 -0
- package/dist/social-providers/wechat.mjs +81 -0
- package/dist/social-providers/zoom.d.mts +163 -0
- package/dist/social-providers/zoom.mjs +69 -0
- package/dist/types/context.d.mts +319 -0
- package/dist/types/cookie.d.mts +15 -0
- package/dist/types/helper.d.mts +10 -0
- package/dist/types/index.d.mts +9 -0
- package/dist/types/init-options.d.mts +1430 -0
- package/dist/types/plugin-client.d.mts +123 -0
- package/dist/types/plugin.d.mts +124 -0
- package/dist/types/secret.d.mts +11 -0
- package/dist/utils/async.d.mts +22 -0
- package/dist/utils/async.mjs +32 -0
- package/dist/utils/db.d.mts +9 -0
- package/dist/utils/db.mjs +15 -0
- package/dist/utils/deprecate.d.mts +9 -0
- package/dist/utils/deprecate.mjs +16 -0
- package/dist/utils/error-codes.d.mts +13 -0
- package/dist/utils/error-codes.mjs +10 -0
- package/dist/utils/fetch-metadata.d.mts +4 -0
- package/dist/utils/fetch-metadata.mjs +6 -0
- package/dist/utils/host.d.mts +147 -0
- package/dist/utils/host.mjs +295 -0
- package/dist/utils/id.d.mts +4 -0
- package/dist/utils/id.mjs +7 -0
- package/dist/utils/ip.d.mts +77 -0
- package/dist/utils/ip.mjs +230 -0
- package/dist/utils/is-api-error.d.mts +6 -0
- package/dist/utils/is-api-error.mjs +8 -0
- package/dist/utils/json.d.mts +4 -0
- package/dist/utils/json.mjs +41 -0
- package/dist/utils/redirect-uri.d.mts +20 -0
- package/dist/utils/redirect-uri.mjs +48 -0
- package/dist/utils/string.d.mts +8 -0
- package/dist/utils/string.mjs +25 -0
- package/dist/utils/url.d.mts +37 -0
- package/dist/utils/url.mjs +61 -0
- package/package.json +216 -0
- package/src/api/index.ts +185 -0
- package/src/async_hooks/index.ts +40 -0
- package/src/async_hooks/pure.index.ts +46 -0
- package/src/context/endpoint-context.ts +50 -0
- package/src/context/global.ts +57 -0
- package/src/context/index.ts +23 -0
- package/src/context/request-state.ts +91 -0
- package/src/context/transaction.ts +169 -0
- package/src/db/adapter/factory.ts +1740 -0
- package/src/db/adapter/get-default-field-name.ts +59 -0
- package/src/db/adapter/get-default-model-name.ts +51 -0
- package/src/db/adapter/get-field-attributes.ts +62 -0
- package/src/db/adapter/get-field-name.ts +43 -0
- package/src/db/adapter/get-id-field.ts +150 -0
- package/src/db/adapter/get-model-name.ts +36 -0
- package/src/db/adapter/index.ts +656 -0
- package/src/db/adapter/types.ts +134 -0
- package/src/db/adapter/utils.ts +61 -0
- package/src/db/get-tables.ts +304 -0
- package/src/db/index.ts +43 -0
- package/src/db/plugin.ts +11 -0
- package/src/db/schema/account.ts +47 -0
- package/src/db/schema/rate-limit.ts +36 -0
- package/src/db/schema/session.ts +29 -0
- package/src/db/schema/shared.ts +7 -0
- package/src/db/schema/user.ts +31 -0
- package/src/db/schema/verification.ts +28 -0
- package/src/db/type.ts +360 -0
- package/src/env/color-depth.ts +172 -0
- package/src/env/env-impl.ts +123 -0
- package/src/env/index.ts +23 -0
- package/src/env/logger.ts +145 -0
- package/src/error/codes.ts +72 -0
- package/src/error/index.ts +44 -0
- package/src/index.ts +1 -0
- package/src/instrumentation/api.ts +17 -0
- package/src/instrumentation/attributes.ts +22 -0
- package/src/instrumentation/index.ts +2 -0
- package/src/instrumentation/noop.ts +74 -0
- package/src/instrumentation/pure.index.ts +31 -0
- package/src/instrumentation/tracer.ts +95 -0
- package/src/oauth2/client-credentials-token.ts +126 -0
- package/src/oauth2/create-authorization-url.ts +89 -0
- package/src/oauth2/index.ts +34 -0
- package/src/oauth2/oauth-provider.ts +222 -0
- package/src/oauth2/refresh-access-token.ts +157 -0
- package/src/oauth2/reject-redirects.ts +70 -0
- package/src/oauth2/utils.ts +70 -0
- package/src/oauth2/validate-authorization-code.ts +190 -0
- package/src/oauth2/verify.ts +408 -0
- package/src/social-providers/apple.ts +249 -0
- package/src/social-providers/atlassian.ts +133 -0
- package/src/social-providers/cognito.ts +281 -0
- package/src/social-providers/discord.ts +170 -0
- package/src/social-providers/dropbox.ts +112 -0
- package/src/social-providers/facebook.ts +288 -0
- package/src/social-providers/figma.ts +118 -0
- package/src/social-providers/github.ts +184 -0
- package/src/social-providers/gitlab.ts +155 -0
- package/src/social-providers/google.ts +263 -0
- package/src/social-providers/huggingface.ts +119 -0
- package/src/social-providers/index.ts +132 -0
- package/src/social-providers/kakao.ts +179 -0
- package/src/social-providers/kick.ts +109 -0
- package/src/social-providers/line.ts +169 -0
- package/src/social-providers/linear.ts +121 -0
- package/src/social-providers/linkedin.ts +110 -0
- package/src/social-providers/microsoft-entra-id.ts +391 -0
- package/src/social-providers/naver.ts +113 -0
- package/src/social-providers/notion.ts +108 -0
- package/src/social-providers/paybin.ts +118 -0
- package/src/social-providers/paypal.ts +371 -0
- package/src/social-providers/polar.ts +111 -0
- package/src/social-providers/railway.ts +100 -0
- package/src/social-providers/reddit.ts +126 -0
- package/src/social-providers/roblox.ts +112 -0
- package/src/social-providers/salesforce.ts +159 -0
- package/src/social-providers/slack.ts +112 -0
- package/src/social-providers/spotify.ts +94 -0
- package/src/social-providers/tiktok.ts +211 -0
- package/src/social-providers/twitch.ts +112 -0
- package/src/social-providers/twitter.ts +199 -0
- package/src/social-providers/vercel.ts +87 -0
- package/src/social-providers/vk.ts +125 -0
- package/src/social-providers/wechat.ts +220 -0
- package/src/social-providers/zoom.ts +230 -0
- package/src/types/context.ts +460 -0
- package/src/types/cookie.ts +10 -0
- package/src/types/helper.ts +27 -0
- package/src/types/index.ts +40 -0
- package/src/types/init-options.ts +1679 -0
- package/src/types/plugin-client.ts +143 -0
- package/src/types/plugin.ts +163 -0
- package/src/types/secret.ts +8 -0
- package/src/utils/async.ts +53 -0
- package/src/utils/db.ts +20 -0
- package/src/utils/deprecate.ts +21 -0
- package/src/utils/error-codes.ts +68 -0
- package/src/utils/fetch-metadata.ts +3 -0
- package/src/utils/host.ts +416 -0
- package/src/utils/id.ts +5 -0
- package/src/utils/ip.ts +395 -0
- package/src/utils/is-api-error.ts +10 -0
- package/src/utils/json.ts +56 -0
- package/src/utils/redirect-uri.ts +54 -0
- package/src/utils/string.ts +40 -0
- package/src/utils/url.ts +77 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { AwaitableFunction } from "../types/helper.mjs";
|
|
2
|
+
import { OAuth2Tokens, ProviderOptions } from "./oauth-provider.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/oauth2/refresh-access-token.d.ts
|
|
5
|
+
declare function refreshAccessTokenRequest({
|
|
6
|
+
refreshToken,
|
|
7
|
+
options,
|
|
8
|
+
authentication,
|
|
9
|
+
extraParams,
|
|
10
|
+
resource
|
|
11
|
+
}: {
|
|
12
|
+
refreshToken: string;
|
|
13
|
+
options: AwaitableFunction<Partial<ProviderOptions>>;
|
|
14
|
+
authentication?: ("basic" | "post") | undefined;
|
|
15
|
+
extraParams?: Record<string, string> | undefined;
|
|
16
|
+
resource?: (string | string[]) | undefined;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
body: URLSearchParams;
|
|
19
|
+
headers: Record<string, any>;
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated use async'd refreshAccessTokenRequest instead
|
|
23
|
+
*/
|
|
24
|
+
declare function createRefreshAccessTokenRequest({
|
|
25
|
+
refreshToken,
|
|
26
|
+
options,
|
|
27
|
+
authentication,
|
|
28
|
+
extraParams,
|
|
29
|
+
resource
|
|
30
|
+
}: {
|
|
31
|
+
refreshToken: string;
|
|
32
|
+
options: ProviderOptions;
|
|
33
|
+
authentication?: ("basic" | "post") | undefined;
|
|
34
|
+
extraParams?: Record<string, string> | undefined;
|
|
35
|
+
resource?: (string | string[]) | undefined;
|
|
36
|
+
}): {
|
|
37
|
+
body: URLSearchParams;
|
|
38
|
+
headers: Record<string, any>;
|
|
39
|
+
};
|
|
40
|
+
declare function refreshAccessToken({
|
|
41
|
+
refreshToken,
|
|
42
|
+
options,
|
|
43
|
+
tokenEndpoint,
|
|
44
|
+
authentication,
|
|
45
|
+
extraParams
|
|
46
|
+
}: {
|
|
47
|
+
refreshToken: string;
|
|
48
|
+
options: Partial<ProviderOptions>;
|
|
49
|
+
tokenEndpoint: string;
|
|
50
|
+
authentication?: ("basic" | "post") | undefined;
|
|
51
|
+
extraParams?: Record<string, string> | undefined;
|
|
52
|
+
}): Promise<OAuth2Tokens>;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { createRefreshAccessTokenRequest, refreshAccessToken, refreshAccessTokenRequest };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { fetchRefusingRedirects } from "./reject-redirects.mjs";
|
|
2
|
+
import { base64 } from "@better-auth/utils/base64";
|
|
3
|
+
//#region src/oauth2/refresh-access-token.ts
|
|
4
|
+
async function refreshAccessTokenRequest({ refreshToken, options, authentication, extraParams, resource }) {
|
|
5
|
+
options = typeof options === "function" ? await options() : options;
|
|
6
|
+
return createRefreshAccessTokenRequest({
|
|
7
|
+
refreshToken,
|
|
8
|
+
options,
|
|
9
|
+
authentication,
|
|
10
|
+
extraParams,
|
|
11
|
+
resource
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated use async'd refreshAccessTokenRequest instead
|
|
16
|
+
*/
|
|
17
|
+
function createRefreshAccessTokenRequest({ refreshToken, options, authentication, extraParams, resource }) {
|
|
18
|
+
const body = new URLSearchParams();
|
|
19
|
+
const headers = {
|
|
20
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
21
|
+
accept: "application/json"
|
|
22
|
+
};
|
|
23
|
+
body.set("grant_type", "refresh_token");
|
|
24
|
+
body.set("refresh_token", refreshToken);
|
|
25
|
+
if (authentication === "basic") {
|
|
26
|
+
const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
|
|
27
|
+
if (primaryClientId) headers["authorization"] = "Basic " + base64.encode(`${primaryClientId}:${options.clientSecret ?? ""}`);
|
|
28
|
+
else headers["authorization"] = "Basic " + base64.encode(`:${options.clientSecret ?? ""}`);
|
|
29
|
+
} else {
|
|
30
|
+
const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
|
|
31
|
+
body.set("client_id", primaryClientId);
|
|
32
|
+
if (options.clientSecret) body.set("client_secret", options.clientSecret);
|
|
33
|
+
}
|
|
34
|
+
if (resource) if (typeof resource === "string") body.append("resource", resource);
|
|
35
|
+
else for (const _resource of resource) body.append("resource", _resource);
|
|
36
|
+
if (extraParams) for (const [key, value] of Object.entries(extraParams)) body.set(key, value);
|
|
37
|
+
return {
|
|
38
|
+
body,
|
|
39
|
+
headers
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async function refreshAccessToken({ refreshToken, options, tokenEndpoint, authentication, extraParams }) {
|
|
43
|
+
const { body, headers } = await createRefreshAccessTokenRequest({
|
|
44
|
+
refreshToken,
|
|
45
|
+
options,
|
|
46
|
+
authentication,
|
|
47
|
+
extraParams
|
|
48
|
+
});
|
|
49
|
+
const { data, error } = await fetchRefusingRedirects(tokenEndpoint, {
|
|
50
|
+
method: "POST",
|
|
51
|
+
body,
|
|
52
|
+
headers
|
|
53
|
+
});
|
|
54
|
+
if (error) throw error;
|
|
55
|
+
const tokens = {
|
|
56
|
+
accessToken: data.access_token,
|
|
57
|
+
refreshToken: data.refresh_token,
|
|
58
|
+
tokenType: data.token_type,
|
|
59
|
+
scopes: data.scope?.split(" "),
|
|
60
|
+
idToken: data.id_token
|
|
61
|
+
};
|
|
62
|
+
if (data.expires_in) {
|
|
63
|
+
const now = /* @__PURE__ */ new Date();
|
|
64
|
+
tokens.accessTokenExpiresAt = new Date(now.getTime() + data.expires_in * 1e3);
|
|
65
|
+
}
|
|
66
|
+
if (data.refresh_token_expires_in) {
|
|
67
|
+
const now = /* @__PURE__ */ new Date();
|
|
68
|
+
tokens.refreshTokenExpiresAt = new Date(now.getTime() + data.refresh_token_expires_in * 1e3);
|
|
69
|
+
}
|
|
70
|
+
return tokens;
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
export { createRefreshAccessTokenRequest, refreshAccessToken, refreshAccessTokenRequest };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { BetterAuthError } from "../error/index.mjs";
|
|
2
|
+
import { betterFetch } from "@better-fetch/fetch";
|
|
3
|
+
//#region src/oauth2/reject-redirects.ts
|
|
4
|
+
const HTTP_REDIRECT_STATUSES = new Set([
|
|
5
|
+
301,
|
|
6
|
+
302,
|
|
7
|
+
303,
|
|
8
|
+
307,
|
|
9
|
+
308
|
|
10
|
+
]);
|
|
11
|
+
/**
|
|
12
|
+
* Whether a response from a `redirect: "manual"` fetch is a redirect.
|
|
13
|
+
*
|
|
14
|
+
* Node/undici exposes the real 3xx status. Spec-compliant runtimes (Cloudflare
|
|
15
|
+
* Workers, Deno, browsers) return an opaque-redirect filtered response with
|
|
16
|
+
* status 0 and type `"opaqueredirect"`, so the status alone is not enough.
|
|
17
|
+
*/
|
|
18
|
+
function isRedirectResponse(response) {
|
|
19
|
+
return response.type === "opaqueredirect" || HTTP_REDIRECT_STATUSES.has(response.status);
|
|
20
|
+
}
|
|
21
|
+
function redirectRefused(endpoint) {
|
|
22
|
+
return new BetterAuthError(`The OAuth endpoint "${endpoint}" returned an HTTP redirect. Server-side OAuth fetches refuse redirects to prevent SSRF; configure the final endpoint URL.`);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Fetch option that refuses HTTP redirects portably.
|
|
26
|
+
*
|
|
27
|
+
* Cloudflare Workers (workerd) rejects `redirect: "error"`, so manual mode is
|
|
28
|
+
* used and the resolved response is checked with {@link assertResponseNotRedirect}
|
|
29
|
+
* (or, for betterFetch, with {@link fetchRefusingRedirects}).
|
|
30
|
+
*/
|
|
31
|
+
const NO_FOLLOW_REDIRECT = { redirect: "manual" };
|
|
32
|
+
/**
|
|
33
|
+
* Throw when a native-`fetch` response (e.g. jose's JWKS loader) resolved to a
|
|
34
|
+
* redirect, so an attacker-influenced endpoint cannot bounce a server-side
|
|
35
|
+
* request to an internal address.
|
|
36
|
+
*/
|
|
37
|
+
function assertResponseNotRedirect(endpoint, response) {
|
|
38
|
+
if (isRedirectResponse(response)) throw redirectRefused(endpoint);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* betterFetch that refuses HTTP redirects on a server-side OAuth fetch.
|
|
42
|
+
*
|
|
43
|
+
* Returns the betterFetch result and throws if the endpoint redirected, on both
|
|
44
|
+
* undici (real 3xx status) and spec-compliant runtimes (opaque redirect, where
|
|
45
|
+
* the error status is 0). The redirect is never followed on any runtime.
|
|
46
|
+
*/
|
|
47
|
+
async function fetchRefusingRedirects(url, options) {
|
|
48
|
+
let redirected = false;
|
|
49
|
+
const result = await betterFetch(url, {
|
|
50
|
+
...options,
|
|
51
|
+
...NO_FOLLOW_REDIRECT,
|
|
52
|
+
onError(context) {
|
|
53
|
+
if (isRedirectResponse(context.response)) redirected = true;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
if (redirected) throw redirectRefused(url);
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { NO_FOLLOW_REDIRECT, assertResponseNotRedirect, fetchRefusingRedirects };
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
/**
|
|
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;
|
|
13
|
+
/**
|
|
14
|
+
* Return the provider's primary Client ID: the single string, or the entry at
|
|
15
|
+
* array index 0 for the cross-platform form used by ID token audience
|
|
16
|
+
* verification. Index 0 is the designated primary and pairs with
|
|
17
|
+
* `clientSecret` for the authorization code flow; later array entries are
|
|
18
|
+
* only used as additional accepted audiences. Returns `undefined` when the
|
|
19
|
+
* primary value is missing or an empty string.
|
|
20
|
+
*/
|
|
21
|
+
declare function getPrimaryClientId(clientId: unknown): string | undefined;
|
|
22
|
+
declare function generateCodeChallenge(codeVerifier: string): Promise<string>;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { applyDefaultAccessTokenExpiry, generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { base64Url } from "@better-auth/utils/base64";
|
|
2
|
+
//#region src/oauth2/utils.ts
|
|
3
|
+
function getOAuth2Tokens(data) {
|
|
4
|
+
const getDate = (seconds) => {
|
|
5
|
+
const now = /* @__PURE__ */ new Date();
|
|
6
|
+
return new Date(now.getTime() + seconds * 1e3);
|
|
7
|
+
};
|
|
8
|
+
return {
|
|
9
|
+
tokenType: data.token_type,
|
|
10
|
+
accessToken: data.access_token,
|
|
11
|
+
refreshToken: data.refresh_token,
|
|
12
|
+
accessTokenExpiresAt: data.expires_in ? getDate(data.expires_in) : void 0,
|
|
13
|
+
refreshTokenExpiresAt: data.refresh_token_expires_in ? getDate(data.refresh_token_expires_in) : void 0,
|
|
14
|
+
scopes: data?.scope ? typeof data.scope === "string" ? data.scope.split(" ") : data.scope : [],
|
|
15
|
+
idToken: data.id_token,
|
|
16
|
+
raw: data
|
|
17
|
+
};
|
|
18
|
+
}
|
|
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
|
+
/**
|
|
31
|
+
* Return the provider's primary Client ID: the single string, or the entry at
|
|
32
|
+
* array index 0 for the cross-platform form used by ID token audience
|
|
33
|
+
* verification. Index 0 is the designated primary and pairs with
|
|
34
|
+
* `clientSecret` for the authorization code flow; later array entries are
|
|
35
|
+
* only used as additional accepted audiences. Returns `undefined` when the
|
|
36
|
+
* primary value is missing or an empty string.
|
|
37
|
+
*/
|
|
38
|
+
function getPrimaryClientId(clientId) {
|
|
39
|
+
const value = Array.isArray(clientId) ? clientId[0] : clientId;
|
|
40
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
41
|
+
}
|
|
42
|
+
async function generateCodeChallenge(codeVerifier) {
|
|
43
|
+
const data = new TextEncoder().encode(codeVerifier);
|
|
44
|
+
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
45
|
+
return base64Url.encode(new Uint8Array(hash), { padding: false });
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { applyDefaultAccessTokenExpiry, generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { AwaitableFunction } from "../types/helper.mjs";
|
|
2
|
+
import { OAuth2Tokens, ProviderOptions } from "./oauth-provider.mjs";
|
|
3
|
+
import * as jose from "jose";
|
|
4
|
+
|
|
5
|
+
//#region src/oauth2/validate-authorization-code.d.ts
|
|
6
|
+
declare function authorizationCodeRequest({
|
|
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: AwaitableFunction<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
|
+
}): Promise<{
|
|
27
|
+
body: URLSearchParams;
|
|
28
|
+
headers: Record<string, any>;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated use async'd authorizationCodeRequest instead
|
|
32
|
+
*/
|
|
33
|
+
declare function createAuthorizationCodeRequest({
|
|
34
|
+
code,
|
|
35
|
+
codeVerifier,
|
|
36
|
+
redirectURI,
|
|
37
|
+
options,
|
|
38
|
+
authentication,
|
|
39
|
+
deviceId,
|
|
40
|
+
headers,
|
|
41
|
+
additionalParams,
|
|
42
|
+
resource
|
|
43
|
+
}: {
|
|
44
|
+
code: string;
|
|
45
|
+
redirectURI: string;
|
|
46
|
+
options: Partial<ProviderOptions>;
|
|
47
|
+
codeVerifier?: string | undefined;
|
|
48
|
+
deviceId?: string | undefined;
|
|
49
|
+
authentication?: ("basic" | "post") | undefined;
|
|
50
|
+
headers?: Record<string, string> | undefined;
|
|
51
|
+
additionalParams?: Record<string, string> | undefined;
|
|
52
|
+
resource?: (string | string[]) | undefined;
|
|
53
|
+
}): {
|
|
54
|
+
body: URLSearchParams;
|
|
55
|
+
headers: Record<string, any>;
|
|
56
|
+
};
|
|
57
|
+
declare function validateAuthorizationCode({
|
|
58
|
+
code,
|
|
59
|
+
codeVerifier,
|
|
60
|
+
redirectURI,
|
|
61
|
+
options,
|
|
62
|
+
tokenEndpoint,
|
|
63
|
+
authentication,
|
|
64
|
+
deviceId,
|
|
65
|
+
headers,
|
|
66
|
+
additionalParams,
|
|
67
|
+
resource
|
|
68
|
+
}: {
|
|
69
|
+
code: string;
|
|
70
|
+
redirectURI: string;
|
|
71
|
+
options: AwaitableFunction<Partial<ProviderOptions>>;
|
|
72
|
+
codeVerifier?: string | undefined;
|
|
73
|
+
deviceId?: string | undefined;
|
|
74
|
+
tokenEndpoint: string;
|
|
75
|
+
authentication?: ("basic" | "post") | undefined;
|
|
76
|
+
headers?: Record<string, string> | undefined;
|
|
77
|
+
additionalParams?: Record<string, string> | undefined;
|
|
78
|
+
resource?: (string | string[]) | undefined;
|
|
79
|
+
}): Promise<OAuth2Tokens>;
|
|
80
|
+
declare function validateToken(token: string, jwksEndpoint: string, options?: {
|
|
81
|
+
audience?: string | string[];
|
|
82
|
+
issuer?: string | string[];
|
|
83
|
+
}): Promise<jose.JWTVerifyResult<jose.JWTPayload> & jose.ResolvedKey>;
|
|
84
|
+
//#endregion
|
|
85
|
+
export { authorizationCodeRequest, createAuthorizationCodeRequest, validateAuthorizationCode, validateToken };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { NO_FOLLOW_REDIRECT, assertResponseNotRedirect, fetchRefusingRedirects } from "./reject-redirects.mjs";
|
|
2
|
+
import { getOAuth2Tokens } from "./utils.mjs";
|
|
3
|
+
import { base64 } from "@better-auth/utils/base64";
|
|
4
|
+
import { createRemoteJWKSet, customFetch, jwtVerify } from "jose";
|
|
5
|
+
//#region src/oauth2/validate-authorization-code.ts
|
|
6
|
+
async function authorizationCodeRequest({ code, codeVerifier, redirectURI, options, authentication, deviceId, headers, additionalParams = {}, resource }) {
|
|
7
|
+
options = typeof options === "function" ? await options() : options;
|
|
8
|
+
return createAuthorizationCodeRequest({
|
|
9
|
+
code,
|
|
10
|
+
codeVerifier,
|
|
11
|
+
redirectURI,
|
|
12
|
+
options,
|
|
13
|
+
authentication,
|
|
14
|
+
deviceId,
|
|
15
|
+
headers,
|
|
16
|
+
additionalParams,
|
|
17
|
+
resource
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* @deprecated use async'd authorizationCodeRequest instead
|
|
22
|
+
*/
|
|
23
|
+
function createAuthorizationCodeRequest({ code, codeVerifier, redirectURI, options, authentication, deviceId, headers, additionalParams = {}, resource }) {
|
|
24
|
+
const body = new URLSearchParams();
|
|
25
|
+
const requestHeaders = {
|
|
26
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
27
|
+
accept: "application/json",
|
|
28
|
+
...headers
|
|
29
|
+
};
|
|
30
|
+
body.set("grant_type", "authorization_code");
|
|
31
|
+
body.set("code", code);
|
|
32
|
+
codeVerifier && body.set("code_verifier", codeVerifier);
|
|
33
|
+
options.clientKey && body.set("client_key", options.clientKey);
|
|
34
|
+
deviceId && body.set("device_id", deviceId);
|
|
35
|
+
body.set("redirect_uri", options.redirectURI || redirectURI);
|
|
36
|
+
if (resource) if (typeof resource === "string") body.append("resource", resource);
|
|
37
|
+
else for (const _resource of resource) body.append("resource", _resource);
|
|
38
|
+
if (authentication === "basic") {
|
|
39
|
+
const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
|
|
40
|
+
requestHeaders["authorization"] = `Basic ${base64.encode(`${primaryClientId}:${options.clientSecret ?? ""}`)}`;
|
|
41
|
+
} else {
|
|
42
|
+
const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
|
|
43
|
+
body.set("client_id", primaryClientId);
|
|
44
|
+
if (options.clientSecret) body.set("client_secret", options.clientSecret);
|
|
45
|
+
}
|
|
46
|
+
for (const [key, value] of Object.entries(additionalParams)) if (!body.has(key)) body.append(key, value);
|
|
47
|
+
return {
|
|
48
|
+
body,
|
|
49
|
+
headers: requestHeaders
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async function validateAuthorizationCode({ code, codeVerifier, redirectURI, options, tokenEndpoint, authentication, deviceId, headers, additionalParams = {}, resource }) {
|
|
53
|
+
const { body, headers: requestHeaders } = await authorizationCodeRequest({
|
|
54
|
+
code,
|
|
55
|
+
codeVerifier,
|
|
56
|
+
redirectURI,
|
|
57
|
+
options,
|
|
58
|
+
authentication,
|
|
59
|
+
deviceId,
|
|
60
|
+
headers,
|
|
61
|
+
additionalParams,
|
|
62
|
+
resource
|
|
63
|
+
});
|
|
64
|
+
const { data, error } = await fetchRefusingRedirects(tokenEndpoint, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
body,
|
|
67
|
+
headers: requestHeaders
|
|
68
|
+
});
|
|
69
|
+
if (error) throw error;
|
|
70
|
+
return getOAuth2Tokens(data);
|
|
71
|
+
}
|
|
72
|
+
async function validateToken(token, jwksEndpoint, options) {
|
|
73
|
+
return await jwtVerify(token, createRemoteJWKSet(new URL(jwksEndpoint), { [customFetch]: async (url, init) => {
|
|
74
|
+
const response = await fetch(url, {
|
|
75
|
+
...init,
|
|
76
|
+
...NO_FOLLOW_REDIRECT
|
|
77
|
+
});
|
|
78
|
+
assertResponseNotRedirect(String(url), response);
|
|
79
|
+
return response;
|
|
80
|
+
} }), {
|
|
81
|
+
audience: options?.audience,
|
|
82
|
+
issuer: options?.issuer
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { authorizationCodeRequest, createAuthorizationCodeRequest, validateAuthorizationCode, validateToken };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { JSONWebKeySet, JWTPayload, JWTVerifyOptions } from "jose";
|
|
2
|
+
|
|
3
|
+
//#region src/oauth2/verify.d.ts
|
|
4
|
+
type JwksFetchOptions = {
|
|
5
|
+
/** Jwks url or promise of a Jwks */jwksFetch: string | (() => Promise<JSONWebKeySet | undefined>);
|
|
6
|
+
/**
|
|
7
|
+
* Stable object to cache the result of a function `jwksFetch` under,
|
|
8
|
+
* with the same TTL and kid-miss refetch rules as string sources.
|
|
9
|
+
* Without it, a function source is fetched on every verification.
|
|
10
|
+
*/
|
|
11
|
+
jwksCacheKey?: object;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
interface VerifyAccessTokenRemote {
|
|
17
|
+
/** Full url of the introspect endpoint. Should end with `/oauth2/introspect` */
|
|
18
|
+
introspectUrl: string;
|
|
19
|
+
/** Client Secret */
|
|
20
|
+
clientId: string;
|
|
21
|
+
/** Client Secret */
|
|
22
|
+
clientSecret: string;
|
|
23
|
+
/**
|
|
24
|
+
* Forces remote verification of a token.
|
|
25
|
+
* This ensures attached session (if applicable)
|
|
26
|
+
* is also still active.
|
|
27
|
+
*/
|
|
28
|
+
force?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Accept introspection responses that omit the `aud` claim even when a
|
|
31
|
+
* required `audience` is configured in `verifyOptions`.
|
|
32
|
+
*
|
|
33
|
+
* By default verification fails closed: if you configure an `audience` and
|
|
34
|
+
* the introspection response has no `aud` (or a mismatching one), the token
|
|
35
|
+
* is rejected. Some authorization servers legitimately omit `aud` from
|
|
36
|
+
* introspection responses (it is OPTIONAL per RFC 7662 §2.2); only enable
|
|
37
|
+
* this if you trust the issuer to bind the token to this resource through
|
|
38
|
+
* another mechanism, as it skips the audience check in that case.
|
|
39
|
+
*
|
|
40
|
+
* @default false
|
|
41
|
+
*/
|
|
42
|
+
allowMissingAudience?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Performs local verification of an access token for your APIs.
|
|
46
|
+
*
|
|
47
|
+
* Can also be configured for remote verification.
|
|
48
|
+
*/
|
|
49
|
+
declare function verifyJwsAccessToken(token: string, opts: JwksFetchOptions & {
|
|
50
|
+
/** Verify options */verifyOptions: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience" | "issuer">>;
|
|
51
|
+
}): Promise<JWTPayload>;
|
|
52
|
+
declare function getJwks(token: string, opts: JwksFetchOptions): Promise<JSONWebKeySet>;
|
|
53
|
+
/**
|
|
54
|
+
* Performs local verification of an access token for your API.
|
|
55
|
+
*
|
|
56
|
+
* Can also be configured for remote verification.
|
|
57
|
+
*/
|
|
58
|
+
declare function verifyAccessToken(token: string, opts: {
|
|
59
|
+
/** Verify options */verifyOptions: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience" | "issuer">>; /** Scopes to additionally verify. Token must include all but not exact. */
|
|
60
|
+
scopes?: string[]; /** Required to verify access token locally */
|
|
61
|
+
jwksUrl?: string; /** If provided, can verify a token remotely */
|
|
62
|
+
remoteVerify?: VerifyAccessTokenRemote;
|
|
63
|
+
}): Promise<JWTPayload>;
|
|
64
|
+
//#endregion
|
|
65
|
+
export { getJwks, verifyAccessToken, verifyJwsAccessToken };
|