@hammadj/better-auth-core 1.5.0-beta.9
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/.turbo/turbo-build.log +266 -0
- package/.turbo/turbo-test.log +2 -0
- package/LICENSE.md +20 -0
- package/dist/api/index.d.mts +181 -0
- package/dist/api/index.mjs +34 -0
- package/dist/api/index.mjs.map +1 -0
- package/dist/async_hooks/index.d.mts +7 -0
- package/dist/async_hooks/index.mjs +22 -0
- package/dist/async_hooks/index.mjs.map +1 -0
- package/dist/async_hooks/pure.index.d.mts +7 -0
- package/dist/async_hooks/pure.index.mjs +35 -0
- package/dist/async_hooks/pure.index.mjs.map +1 -0
- package/dist/context/endpoint-context.d.mts +19 -0
- package/dist/context/endpoint-context.mjs +32 -0
- package/dist/context/endpoint-context.mjs.map +1 -0
- package/dist/context/global.d.mts +7 -0
- package/dist/context/global.mjs +38 -0
- package/dist/context/global.mjs.map +1 -0
- package/dist/context/index.d.mts +5 -0
- package/dist/context/index.mjs +6 -0
- package/dist/context/request-state.d.mts +26 -0
- package/dist/context/request-state.mjs +50 -0
- package/dist/context/request-state.mjs.map +1 -0
- package/dist/context/transaction.d.mts +25 -0
- package/dist/context/transaction.mjs +96 -0
- package/dist/context/transaction.mjs.map +1 -0
- package/dist/db/adapter/factory.d.mts +28 -0
- package/dist/db/adapter/factory.mjs +716 -0
- package/dist/db/adapter/factory.mjs.map +1 -0
- package/dist/db/adapter/get-default-field-name.d.mts +19 -0
- package/dist/db/adapter/get-default-field-name.mjs +39 -0
- package/dist/db/adapter/get-default-field-name.mjs.map +1 -0
- package/dist/db/adapter/get-default-model-name.d.mts +13 -0
- package/dist/db/adapter/get-default-model-name.mjs +33 -0
- package/dist/db/adapter/get-default-model-name.mjs.map +1 -0
- package/dist/db/adapter/get-field-attributes.d.mts +30 -0
- package/dist/db/adapter/get-field-attributes.mjs +40 -0
- package/dist/db/adapter/get-field-attributes.mjs.map +1 -0
- package/dist/db/adapter/get-field-name.d.mts +19 -0
- package/dist/db/adapter/get-field-name.mjs +34 -0
- package/dist/db/adapter/get-field-name.mjs.map +1 -0
- package/dist/db/adapter/get-id-field.d.mts +40 -0
- package/dist/db/adapter/get-id-field.mjs +68 -0
- package/dist/db/adapter/get-id-field.mjs.map +1 -0
- package/dist/db/adapter/get-model-name.d.mts +13 -0
- package/dist/db/adapter/get-model-name.mjs +24 -0
- package/dist/db/adapter/get-model-name.mjs.map +1 -0
- package/dist/db/adapter/index.d.mts +515 -0
- package/dist/db/adapter/index.mjs +10 -0
- package/dist/db/adapter/types.d.mts +140 -0
- package/dist/db/adapter/utils.d.mts +8 -0
- package/dist/db/adapter/utils.mjs +39 -0
- package/dist/db/adapter/utils.mjs.map +1 -0
- package/dist/db/get-tables.d.mts +9 -0
- package/dist/db/get-tables.mjs +267 -0
- package/dist/db/get-tables.mjs.map +1 -0
- package/dist/db/index.d.mts +10 -0
- package/dist/db/index.mjs +9 -0
- package/dist/db/plugin.d.mts +13 -0
- package/dist/db/schema/account.d.mts +27 -0
- package/dist/db/schema/account.mjs +20 -0
- package/dist/db/schema/account.mjs.map +1 -0
- package/dist/db/schema/rate-limit.d.mts +15 -0
- package/dist/db/schema/rate-limit.mjs +12 -0
- package/dist/db/schema/rate-limit.mjs.map +1 -0
- package/dist/db/schema/session.d.mts +22 -0
- package/dist/db/schema/session.mjs +15 -0
- package/dist/db/schema/session.mjs.map +1 -0
- package/dist/db/schema/shared.d.mts +11 -0
- package/dist/db/schema/shared.mjs +12 -0
- package/dist/db/schema/shared.mjs.map +1 -0
- package/dist/db/schema/user.d.mts +21 -0
- package/dist/db/schema/user.mjs +14 -0
- package/dist/db/schema/user.mjs.map +1 -0
- package/dist/db/schema/verification.d.mts +20 -0
- package/dist/db/schema/verification.mjs +13 -0
- package/dist/db/schema/verification.mjs.map +1 -0
- package/dist/db/type.d.mts +147 -0
- package/dist/env/color-depth.d.mts +5 -0
- package/dist/env/color-depth.mjs +89 -0
- package/dist/env/color-depth.mjs.map +1 -0
- package/dist/env/env-impl.d.mts +33 -0
- package/dist/env/env-impl.mjs +83 -0
- package/dist/env/env-impl.mjs.map +1 -0
- package/dist/env/index.d.mts +4 -0
- package/dist/env/index.mjs +5 -0
- package/dist/env/logger.d.mts +49 -0
- package/dist/env/logger.mjs +82 -0
- package/dist/env/logger.mjs.map +1 -0
- package/dist/error/codes.d.mts +199 -0
- package/dist/error/codes.mjs +57 -0
- package/dist/error/codes.mjs.map +1 -0
- package/dist/error/index.d.mts +20 -0
- package/dist/error/index.mjs +30 -0
- package/dist/error/index.mjs.map +1 -0
- package/dist/index.d.mts +8 -0
- package/dist/index.mjs +1 -0
- package/dist/oauth2/client-credentials-token.d.mts +37 -0
- package/dist/oauth2/client-credentials-token.mjs +55 -0
- package/dist/oauth2/client-credentials-token.mjs.map +1 -0
- package/dist/oauth2/create-authorization-url.d.mts +46 -0
- package/dist/oauth2/create-authorization-url.mjs +43 -0
- package/dist/oauth2/create-authorization-url.mjs.map +1 -0
- package/dist/oauth2/index.d.mts +8 -0
- package/dist/oauth2/index.mjs +8 -0
- package/dist/oauth2/oauth-provider.d.mts +195 -0
- package/dist/oauth2/refresh-access-token.d.mts +36 -0
- package/dist/oauth2/refresh-access-token.mjs +59 -0
- package/dist/oauth2/refresh-access-token.mjs.map +1 -0
- package/dist/oauth2/utils.d.mts +8 -0
- package/dist/oauth2/utils.mjs +28 -0
- package/dist/oauth2/utils.mjs.map +1 -0
- package/dist/oauth2/validate-authorization-code.d.mts +56 -0
- package/dist/oauth2/validate-authorization-code.mjs +72 -0
- package/dist/oauth2/validate-authorization-code.mjs.map +1 -0
- package/dist/oauth2/verify.d.mts +43 -0
- package/dist/oauth2/verify.mjs +96 -0
- package/dist/oauth2/verify.mjs.map +1 -0
- package/dist/social-providers/apple.d.mts +120 -0
- package/dist/social-providers/apple.mjs +105 -0
- package/dist/social-providers/apple.mjs.map +1 -0
- package/dist/social-providers/atlassian.d.mts +73 -0
- package/dist/social-providers/atlassian.mjs +84 -0
- package/dist/social-providers/atlassian.mjs.map +1 -0
- package/dist/social-providers/cognito.d.mts +88 -0
- package/dist/social-providers/cognito.mjs +166 -0
- package/dist/social-providers/cognito.mjs.map +1 -0
- package/dist/social-providers/discord.d.mts +127 -0
- package/dist/social-providers/discord.mjs +65 -0
- package/dist/social-providers/discord.mjs.map +1 -0
- package/dist/social-providers/dropbox.d.mts +72 -0
- package/dist/social-providers/dropbox.mjs +76 -0
- package/dist/social-providers/dropbox.mjs.map +1 -0
- package/dist/social-providers/facebook.d.mts +82 -0
- package/dist/social-providers/facebook.mjs +121 -0
- package/dist/social-providers/facebook.mjs.map +1 -0
- package/dist/social-providers/figma.d.mts +64 -0
- package/dist/social-providers/figma.mjs +87 -0
- package/dist/social-providers/figma.mjs.map +1 -0
- package/dist/social-providers/github.d.mts +105 -0
- package/dist/social-providers/github.mjs +97 -0
- package/dist/social-providers/github.mjs.map +1 -0
- package/dist/social-providers/gitlab.d.mts +126 -0
- package/dist/social-providers/gitlab.mjs +83 -0
- package/dist/social-providers/gitlab.mjs.map +1 -0
- package/dist/social-providers/google.d.mts +100 -0
- package/dist/social-providers/google.mjs +109 -0
- package/dist/social-providers/google.mjs.map +1 -0
- package/dist/social-providers/huggingface.d.mts +86 -0
- package/dist/social-providers/huggingface.mjs +76 -0
- package/dist/social-providers/huggingface.mjs.map +1 -0
- package/dist/social-providers/index.d.mts +1725 -0
- package/dist/social-providers/index.mjs +77 -0
- package/dist/social-providers/index.mjs.map +1 -0
- package/dist/social-providers/kakao.d.mts +164 -0
- package/dist/social-providers/kakao.mjs +73 -0
- package/dist/social-providers/kakao.mjs.map +1 -0
- package/dist/social-providers/kick.d.mts +76 -0
- package/dist/social-providers/kick.mjs +72 -0
- package/dist/social-providers/kick.mjs.map +1 -0
- package/dist/social-providers/line.d.mts +108 -0
- package/dist/social-providers/line.mjs +114 -0
- package/dist/social-providers/line.mjs.map +1 -0
- package/dist/social-providers/linear.d.mts +71 -0
- package/dist/social-providers/linear.mjs +89 -0
- package/dist/social-providers/linear.mjs.map +1 -0
- package/dist/social-providers/linkedin.d.mts +70 -0
- package/dist/social-providers/linkedin.mjs +77 -0
- package/dist/social-providers/linkedin.mjs.map +1 -0
- package/dist/social-providers/microsoft-entra-id.d.mts +175 -0
- package/dist/social-providers/microsoft-entra-id.mjs +107 -0
- package/dist/social-providers/microsoft-entra-id.mjs.map +1 -0
- package/dist/social-providers/naver.d.mts +95 -0
- package/dist/social-providers/naver.mjs +68 -0
- package/dist/social-providers/naver.mjs.map +1 -0
- package/dist/social-providers/notion.d.mts +67 -0
- package/dist/social-providers/notion.mjs +76 -0
- package/dist/social-providers/notion.mjs.map +1 -0
- package/dist/social-providers/paybin.d.mts +74 -0
- package/dist/social-providers/paybin.mjs +86 -0
- package/dist/social-providers/paybin.mjs.map +1 -0
- package/dist/social-providers/paypal.d.mts +132 -0
- package/dist/social-providers/paypal.mjs +145 -0
- package/dist/social-providers/paypal.mjs.map +1 -0
- package/dist/social-providers/polar.d.mts +77 -0
- package/dist/social-providers/polar.mjs +74 -0
- package/dist/social-providers/polar.mjs.map +1 -0
- package/dist/social-providers/reddit.d.mts +65 -0
- package/dist/social-providers/reddit.mjs +84 -0
- package/dist/social-providers/reddit.mjs.map +1 -0
- package/dist/social-providers/roblox.d.mts +73 -0
- package/dist/social-providers/roblox.mjs +60 -0
- package/dist/social-providers/roblox.mjs.map +1 -0
- package/dist/social-providers/salesforce.d.mts +82 -0
- package/dist/social-providers/salesforce.mjs +92 -0
- package/dist/social-providers/salesforce.mjs.map +1 -0
- package/dist/social-providers/slack.d.mts +86 -0
- package/dist/social-providers/slack.mjs +69 -0
- package/dist/social-providers/slack.mjs.map +1 -0
- package/dist/social-providers/spotify.d.mts +66 -0
- package/dist/social-providers/spotify.mjs +72 -0
- package/dist/social-providers/spotify.mjs.map +1 -0
- package/dist/social-providers/tiktok.d.mts +171 -0
- package/dist/social-providers/tiktok.mjs +63 -0
- package/dist/social-providers/tiktok.mjs.map +1 -0
- package/dist/social-providers/twitch.d.mts +82 -0
- package/dist/social-providers/twitch.mjs +79 -0
- package/dist/social-providers/twitch.mjs.map +1 -0
- package/dist/social-providers/twitter.d.mts +129 -0
- package/dist/social-providers/twitter.mjs +88 -0
- package/dist/social-providers/twitter.mjs.map +1 -0
- package/dist/social-providers/vercel.d.mts +65 -0
- package/dist/social-providers/vercel.mjs +62 -0
- package/dist/social-providers/vercel.mjs.map +1 -0
- package/dist/social-providers/vk.d.mts +73 -0
- package/dist/social-providers/vk.mjs +84 -0
- package/dist/social-providers/vk.mjs.map +1 -0
- package/dist/social-providers/zoom.d.mts +173 -0
- package/dist/social-providers/zoom.mjs +73 -0
- package/dist/social-providers/zoom.mjs.map +1 -0
- package/dist/types/context.d.mts +267 -0
- package/dist/types/cookie.d.mts +16 -0
- package/dist/types/helper.d.mts +10 -0
- package/dist/types/index.d.mts +8 -0
- package/dist/types/init-options.d.mts +1314 -0
- package/dist/types/plugin-client.d.mts +112 -0
- package/dist/types/plugin.d.mts +125 -0
- package/dist/utils/db.d.mts +12 -0
- package/dist/utils/db.mjs +17 -0
- package/dist/utils/db.mjs.map +1 -0
- package/dist/utils/deprecate.d.mts +10 -0
- package/dist/utils/deprecate.mjs +18 -0
- package/dist/utils/deprecate.mjs.map +1 -0
- package/dist/utils/error-codes.d.mts +13 -0
- package/dist/utils/error-codes.mjs +12 -0
- package/dist/utils/error-codes.mjs.map +1 -0
- package/dist/utils/id.d.mts +5 -0
- package/dist/utils/id.mjs +10 -0
- package/dist/utils/id.mjs.map +1 -0
- package/dist/utils/ip.d.mts +55 -0
- package/dist/utils/ip.mjs +119 -0
- package/dist/utils/ip.mjs.map +1 -0
- package/dist/utils/json.d.mts +5 -0
- package/dist/utils/json.mjs +26 -0
- package/dist/utils/json.mjs.map +1 -0
- package/dist/utils/string.d.mts +5 -0
- package/dist/utils/string.mjs +8 -0
- package/dist/utils/string.mjs.map +1 -0
- package/dist/utils/url.d.mts +21 -0
- package/dist/utils/url.mjs +33 -0
- package/dist/utils/url.mjs.map +1 -0
- package/package.json +147 -0
- package/src/api/index.ts +106 -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.test.ts +94 -0
- package/src/context/request-state.ts +91 -0
- package/src/context/transaction.ts +136 -0
- package/src/db/adapter/factory.ts +1362 -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 +141 -0
- package/src/db/adapter/get-model-name.ts +36 -0
- package/src/db/adapter/index.ts +554 -0
- package/src/db/adapter/types.ts +171 -0
- package/src/db/adapter/utils.ts +61 -0
- package/src/db/get-tables.ts +296 -0
- package/src/db/index.ts +18 -0
- package/src/db/plugin.ts +11 -0
- package/src/db/schema/account.ts +34 -0
- package/src/db/schema/rate-limit.ts +21 -0
- package/src/db/schema/session.ts +17 -0
- package/src/db/schema/shared.ts +7 -0
- package/src/db/schema/user.ts +16 -0
- package/src/db/schema/verification.ts +15 -0
- package/src/db/test/get-tables.test.ts +116 -0
- package/src/db/type.ts +180 -0
- package/src/env/color-depth.ts +172 -0
- package/src/env/env-impl.ts +124 -0
- package/src/env/index.ts +23 -0
- package/src/env/logger.test.ts +34 -0
- package/src/env/logger.ts +145 -0
- package/src/error/codes.ts +58 -0
- package/src/error/index.ts +35 -0
- package/src/index.ts +1 -0
- package/src/oauth2/client-credentials-token.ts +102 -0
- package/src/oauth2/create-authorization-url.ts +87 -0
- package/src/oauth2/index.ts +26 -0
- package/src/oauth2/oauth-provider.ts +222 -0
- package/src/oauth2/refresh-access-token.ts +124 -0
- package/src/oauth2/utils.ts +38 -0
- package/src/oauth2/validate-authorization-code.ts +149 -0
- package/src/oauth2/validate-token.test.ts +174 -0
- package/src/oauth2/verify.ts +221 -0
- package/src/social-providers/apple.ts +223 -0
- package/src/social-providers/atlassian.ts +132 -0
- package/src/social-providers/cognito.ts +279 -0
- package/src/social-providers/discord.ts +169 -0
- package/src/social-providers/dropbox.ts +112 -0
- package/src/social-providers/facebook.ts +206 -0
- package/src/social-providers/figma.ts +117 -0
- package/src/social-providers/github.ts +184 -0
- package/src/social-providers/gitlab.ts +155 -0
- package/src/social-providers/google.ts +199 -0
- package/src/social-providers/huggingface.ts +118 -0
- package/src/social-providers/index.ts +127 -0
- package/src/social-providers/kakao.ts +178 -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 +259 -0
- package/src/social-providers/naver.ts +112 -0
- package/src/social-providers/notion.ts +108 -0
- package/src/social-providers/paybin.ts +122 -0
- package/src/social-providers/paypal.ts +263 -0
- package/src/social-providers/polar.ts +110 -0
- package/src/social-providers/reddit.ts +122 -0
- package/src/social-providers/roblox.ts +111 -0
- package/src/social-providers/salesforce.ts +159 -0
- package/src/social-providers/slack.ts +111 -0
- package/src/social-providers/spotify.ts +93 -0
- package/src/social-providers/tiktok.ts +209 -0
- package/src/social-providers/twitch.ts +111 -0
- package/src/social-providers/twitter.ts +198 -0
- package/src/social-providers/vercel.ts +87 -0
- package/src/social-providers/vk.ts +124 -0
- package/src/social-providers/zoom.ts +238 -0
- package/src/types/context.ts +396 -0
- package/src/types/cookie.ts +10 -0
- package/src/types/helper.ts +26 -0
- package/src/types/index.ts +32 -0
- package/src/types/init-options.ts +1529 -0
- package/src/types/plugin-client.ts +127 -0
- package/src/types/plugin.ts +157 -0
- package/src/utils/db.ts +20 -0
- package/src/utils/deprecate.test.ts +72 -0
- package/src/utils/deprecate.ts +21 -0
- package/src/utils/error-codes.ts +65 -0
- package/src/utils/id.ts +5 -0
- package/src/utils/ip.test.ts +255 -0
- package/src/utils/ip.ts +211 -0
- package/src/utils/json.ts +25 -0
- package/src/utils/string.ts +3 -0
- package/src/utils/url.ts +43 -0
- package/tsconfig.json +7 -0
- package/tsdown.config.ts +35 -0
- package/vitest.config.ts +3 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
+
import { logger } from "../env";
|
|
3
|
+
import { BetterAuthError } from "../error";
|
|
4
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
5
|
+
import {
|
|
6
|
+
createAuthorizationURL,
|
|
7
|
+
refreshAccessToken,
|
|
8
|
+
validateAuthorizationCode,
|
|
9
|
+
} from "../oauth2";
|
|
10
|
+
|
|
11
|
+
export interface SalesforceProfile {
|
|
12
|
+
sub: string;
|
|
13
|
+
user_id: string;
|
|
14
|
+
organization_id: string;
|
|
15
|
+
preferred_username?: string | undefined;
|
|
16
|
+
email: string;
|
|
17
|
+
email_verified?: boolean | undefined;
|
|
18
|
+
name: string;
|
|
19
|
+
given_name?: string | undefined;
|
|
20
|
+
family_name?: string | undefined;
|
|
21
|
+
zoneinfo?: string | undefined;
|
|
22
|
+
photos?:
|
|
23
|
+
| {
|
|
24
|
+
picture?: string;
|
|
25
|
+
thumbnail?: string;
|
|
26
|
+
}
|
|
27
|
+
| undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SalesforceOptions extends ProviderOptions<SalesforceProfile> {
|
|
31
|
+
clientId: string;
|
|
32
|
+
environment?: ("sandbox" | "production") | undefined;
|
|
33
|
+
loginUrl?: string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Override the redirect URI if auto-detection fails.
|
|
36
|
+
* Should match the Callback URL configured in your Salesforce Connected App.
|
|
37
|
+
* @example "http://localhost:3000/api/auth/callback/salesforce"
|
|
38
|
+
*/
|
|
39
|
+
redirectURI?: string | undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const salesforce = (options: SalesforceOptions) => {
|
|
43
|
+
const environment = options.environment ?? "production";
|
|
44
|
+
const isSandbox = environment === "sandbox";
|
|
45
|
+
const authorizationEndpoint = options.loginUrl
|
|
46
|
+
? `https://${options.loginUrl}/services/oauth2/authorize`
|
|
47
|
+
: isSandbox
|
|
48
|
+
? "https://test.salesforce.com/services/oauth2/authorize"
|
|
49
|
+
: "https://login.salesforce.com/services/oauth2/authorize";
|
|
50
|
+
|
|
51
|
+
const tokenEndpoint = options.loginUrl
|
|
52
|
+
? `https://${options.loginUrl}/services/oauth2/token`
|
|
53
|
+
: isSandbox
|
|
54
|
+
? "https://test.salesforce.com/services/oauth2/token"
|
|
55
|
+
: "https://login.salesforce.com/services/oauth2/token";
|
|
56
|
+
|
|
57
|
+
const userInfoEndpoint = options.loginUrl
|
|
58
|
+
? `https://${options.loginUrl}/services/oauth2/userinfo`
|
|
59
|
+
: isSandbox
|
|
60
|
+
? "https://test.salesforce.com/services/oauth2/userinfo"
|
|
61
|
+
: "https://login.salesforce.com/services/oauth2/userinfo";
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
id: "salesforce",
|
|
65
|
+
name: "Salesforce",
|
|
66
|
+
|
|
67
|
+
async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
|
|
68
|
+
if (!options.clientId || !options.clientSecret) {
|
|
69
|
+
logger.error(
|
|
70
|
+
"Client Id and Client Secret are required for Salesforce. Make sure to provide them in the options.",
|
|
71
|
+
);
|
|
72
|
+
throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
|
|
73
|
+
}
|
|
74
|
+
if (!codeVerifier) {
|
|
75
|
+
throw new BetterAuthError("codeVerifier is required for Salesforce");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const _scopes = options.disableDefaultScope
|
|
79
|
+
? []
|
|
80
|
+
: ["openid", "email", "profile"];
|
|
81
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
82
|
+
if (scopes) _scopes.push(...scopes);
|
|
83
|
+
|
|
84
|
+
return createAuthorizationURL({
|
|
85
|
+
id: "salesforce",
|
|
86
|
+
options,
|
|
87
|
+
authorizationEndpoint,
|
|
88
|
+
scopes: _scopes,
|
|
89
|
+
state,
|
|
90
|
+
codeVerifier,
|
|
91
|
+
redirectURI: options.redirectURI || redirectURI,
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
96
|
+
return validateAuthorizationCode({
|
|
97
|
+
code,
|
|
98
|
+
codeVerifier,
|
|
99
|
+
redirectURI: options.redirectURI || redirectURI,
|
|
100
|
+
options,
|
|
101
|
+
tokenEndpoint,
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
refreshAccessToken: options.refreshAccessToken
|
|
106
|
+
? options.refreshAccessToken
|
|
107
|
+
: async (refreshToken) => {
|
|
108
|
+
return refreshAccessToken({
|
|
109
|
+
refreshToken,
|
|
110
|
+
options: {
|
|
111
|
+
clientId: options.clientId,
|
|
112
|
+
clientSecret: options.clientSecret,
|
|
113
|
+
},
|
|
114
|
+
tokenEndpoint,
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
async getUserInfo(token) {
|
|
119
|
+
if (options.getUserInfo) {
|
|
120
|
+
return options.getUserInfo(token);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const { data: user } = await betterFetch<SalesforceProfile>(
|
|
125
|
+
userInfoEndpoint,
|
|
126
|
+
{
|
|
127
|
+
headers: {
|
|
128
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (!user) {
|
|
134
|
+
logger.error("Failed to fetch user info from Salesforce");
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const userMap = await options.mapProfileToUser?.(user);
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
user: {
|
|
142
|
+
id: user.user_id,
|
|
143
|
+
name: user.name,
|
|
144
|
+
email: user.email,
|
|
145
|
+
image: user.photos?.picture || user.photos?.thumbnail,
|
|
146
|
+
emailVerified: user.email_verified ?? false,
|
|
147
|
+
...userMap,
|
|
148
|
+
},
|
|
149
|
+
data: user,
|
|
150
|
+
};
|
|
151
|
+
} catch (error) {
|
|
152
|
+
logger.error("Failed to fetch user info from Salesforce:", error);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
options,
|
|
158
|
+
} satisfies OAuthProvider<SalesforceProfile>;
|
|
159
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
3
|
+
import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
|
|
4
|
+
|
|
5
|
+
export interface SlackProfile extends Record<string, any> {
|
|
6
|
+
ok: boolean;
|
|
7
|
+
sub: string;
|
|
8
|
+
"https://slack.com/user_id": string;
|
|
9
|
+
"https://slack.com/team_id": string;
|
|
10
|
+
email: string;
|
|
11
|
+
email_verified: boolean;
|
|
12
|
+
date_email_verified: number;
|
|
13
|
+
name: string;
|
|
14
|
+
picture: string;
|
|
15
|
+
given_name: string;
|
|
16
|
+
family_name: string;
|
|
17
|
+
locale: string;
|
|
18
|
+
"https://slack.com/team_name": string;
|
|
19
|
+
"https://slack.com/team_domain": string;
|
|
20
|
+
"https://slack.com/user_image_24": string;
|
|
21
|
+
"https://slack.com/user_image_32": string;
|
|
22
|
+
"https://slack.com/user_image_48": string;
|
|
23
|
+
"https://slack.com/user_image_72": string;
|
|
24
|
+
"https://slack.com/user_image_192": string;
|
|
25
|
+
"https://slack.com/user_image_512": string;
|
|
26
|
+
"https://slack.com/team_image_34": string;
|
|
27
|
+
"https://slack.com/team_image_44": string;
|
|
28
|
+
"https://slack.com/team_image_68": string;
|
|
29
|
+
"https://slack.com/team_image_88": string;
|
|
30
|
+
"https://slack.com/team_image_102": string;
|
|
31
|
+
"https://slack.com/team_image_132": string;
|
|
32
|
+
"https://slack.com/team_image_230": string;
|
|
33
|
+
"https://slack.com/team_image_default": boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface SlackOptions extends ProviderOptions<SlackProfile> {
|
|
37
|
+
clientId: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const slack = (options: SlackOptions) => {
|
|
41
|
+
return {
|
|
42
|
+
id: "slack",
|
|
43
|
+
name: "Slack",
|
|
44
|
+
createAuthorizationURL({ state, scopes, redirectURI }) {
|
|
45
|
+
const _scopes = options.disableDefaultScope
|
|
46
|
+
? []
|
|
47
|
+
: ["openid", "profile", "email"];
|
|
48
|
+
if (scopes) _scopes.push(...scopes);
|
|
49
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
50
|
+
const url = new URL("https://slack.com/openid/connect/authorize");
|
|
51
|
+
url.searchParams.set("scope", _scopes.join(" "));
|
|
52
|
+
url.searchParams.set("response_type", "code");
|
|
53
|
+
url.searchParams.set("client_id", options.clientId);
|
|
54
|
+
url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
|
|
55
|
+
url.searchParams.set("state", state);
|
|
56
|
+
return url;
|
|
57
|
+
},
|
|
58
|
+
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
59
|
+
return validateAuthorizationCode({
|
|
60
|
+
code,
|
|
61
|
+
redirectURI,
|
|
62
|
+
options,
|
|
63
|
+
tokenEndpoint: "https://slack.com/api/openid.connect.token",
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
refreshAccessToken: options.refreshAccessToken
|
|
67
|
+
? options.refreshAccessToken
|
|
68
|
+
: async (refreshToken) => {
|
|
69
|
+
return refreshAccessToken({
|
|
70
|
+
refreshToken,
|
|
71
|
+
options: {
|
|
72
|
+
clientId: options.clientId,
|
|
73
|
+
clientKey: options.clientKey,
|
|
74
|
+
clientSecret: options.clientSecret,
|
|
75
|
+
},
|
|
76
|
+
tokenEndpoint: "https://slack.com/api/openid.connect.token",
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
async getUserInfo(token) {
|
|
80
|
+
if (options.getUserInfo) {
|
|
81
|
+
return options.getUserInfo(token);
|
|
82
|
+
}
|
|
83
|
+
const { data: profile, error } = await betterFetch<SlackProfile>(
|
|
84
|
+
"https://slack.com/api/openid.connect.userInfo",
|
|
85
|
+
{
|
|
86
|
+
headers: {
|
|
87
|
+
authorization: `Bearer ${token.accessToken}`,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
if (error) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const userMap = await options.mapProfileToUser?.(profile);
|
|
97
|
+
return {
|
|
98
|
+
user: {
|
|
99
|
+
id: profile["https://slack.com/user_id"],
|
|
100
|
+
name: profile.name || "",
|
|
101
|
+
email: profile.email,
|
|
102
|
+
emailVerified: profile.email_verified,
|
|
103
|
+
image: profile.picture || profile["https://slack.com/user_image_512"],
|
|
104
|
+
...userMap,
|
|
105
|
+
},
|
|
106
|
+
data: profile,
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
options,
|
|
110
|
+
} satisfies OAuthProvider<SlackProfile>;
|
|
111
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
3
|
+
import {
|
|
4
|
+
createAuthorizationURL,
|
|
5
|
+
refreshAccessToken,
|
|
6
|
+
validateAuthorizationCode,
|
|
7
|
+
} from "../oauth2";
|
|
8
|
+
|
|
9
|
+
export interface SpotifyProfile {
|
|
10
|
+
id: string;
|
|
11
|
+
display_name: string;
|
|
12
|
+
email: string;
|
|
13
|
+
images: {
|
|
14
|
+
url: string;
|
|
15
|
+
}[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface SpotifyOptions extends ProviderOptions<SpotifyProfile> {
|
|
19
|
+
clientId: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const spotify = (options: SpotifyOptions) => {
|
|
23
|
+
return {
|
|
24
|
+
id: "spotify",
|
|
25
|
+
name: "Spotify",
|
|
26
|
+
createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
|
|
27
|
+
const _scopes = options.disableDefaultScope ? [] : ["user-read-email"];
|
|
28
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
29
|
+
if (scopes) _scopes.push(...scopes);
|
|
30
|
+
return createAuthorizationURL({
|
|
31
|
+
id: "spotify",
|
|
32
|
+
options,
|
|
33
|
+
authorizationEndpoint: "https://accounts.spotify.com/authorize",
|
|
34
|
+
scopes: _scopes,
|
|
35
|
+
state,
|
|
36
|
+
codeVerifier,
|
|
37
|
+
redirectURI,
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
41
|
+
return validateAuthorizationCode({
|
|
42
|
+
code,
|
|
43
|
+
codeVerifier,
|
|
44
|
+
redirectURI,
|
|
45
|
+
options,
|
|
46
|
+
tokenEndpoint: "https://accounts.spotify.com/api/token",
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
refreshAccessToken: options.refreshAccessToken
|
|
50
|
+
? options.refreshAccessToken
|
|
51
|
+
: async (refreshToken) => {
|
|
52
|
+
return refreshAccessToken({
|
|
53
|
+
refreshToken,
|
|
54
|
+
options: {
|
|
55
|
+
clientId: options.clientId,
|
|
56
|
+
clientKey: options.clientKey,
|
|
57
|
+
clientSecret: options.clientSecret,
|
|
58
|
+
},
|
|
59
|
+
tokenEndpoint: "https://accounts.spotify.com/api/token",
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
async getUserInfo(token) {
|
|
63
|
+
if (options.getUserInfo) {
|
|
64
|
+
return options.getUserInfo(token);
|
|
65
|
+
}
|
|
66
|
+
const { data: profile, error } = await betterFetch<SpotifyProfile>(
|
|
67
|
+
"https://api.spotify.com/v1/me",
|
|
68
|
+
{
|
|
69
|
+
method: "GET",
|
|
70
|
+
headers: {
|
|
71
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
if (error) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const userMap = await options.mapProfileToUser?.(profile);
|
|
79
|
+
return {
|
|
80
|
+
user: {
|
|
81
|
+
id: profile.id,
|
|
82
|
+
name: profile.display_name,
|
|
83
|
+
email: profile.email,
|
|
84
|
+
image: profile.images[0]?.url,
|
|
85
|
+
emailVerified: false,
|
|
86
|
+
...userMap,
|
|
87
|
+
},
|
|
88
|
+
data: profile,
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
options,
|
|
92
|
+
} satisfies OAuthProvider<SpotifyProfile>;
|
|
93
|
+
};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
3
|
+
import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* [More info](https://developers.tiktok.com/doc/tiktok-api-v2-get-user-info/)
|
|
7
|
+
*/
|
|
8
|
+
export interface TiktokProfile extends Record<string, any> {
|
|
9
|
+
data: {
|
|
10
|
+
user: {
|
|
11
|
+
/**
|
|
12
|
+
* The unique identification of the user in the current application.Open id
|
|
13
|
+
* for the client.
|
|
14
|
+
*
|
|
15
|
+
* To return this field, add `fields=open_id` in the user profile request's query parameter.
|
|
16
|
+
*/
|
|
17
|
+
open_id: string;
|
|
18
|
+
/**
|
|
19
|
+
* The unique identification of the user across different apps for the same developer.
|
|
20
|
+
* For example, if a partner has X number of clients,
|
|
21
|
+
* it will get X number of open_id for the same TikTok user,
|
|
22
|
+
* but one persistent union_id for the particular user.
|
|
23
|
+
*
|
|
24
|
+
* To return this field, add `fields=union_id` in the user profile request's query parameter.
|
|
25
|
+
*/
|
|
26
|
+
union_id?: string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* User's profile image.
|
|
29
|
+
*
|
|
30
|
+
* To return this field, add `fields=avatar_url` in the user profile request's query parameter.
|
|
31
|
+
*/
|
|
32
|
+
avatar_url?: string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* User`s profile image in 100x100 size.
|
|
35
|
+
*
|
|
36
|
+
* To return this field, add `fields=avatar_url_100` in the user profile request's query parameter.
|
|
37
|
+
*/
|
|
38
|
+
avatar_url_100?: string | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* User's profile image with higher resolution
|
|
41
|
+
*
|
|
42
|
+
* To return this field, add `fields=avatar_url_100` in the user profile request's query parameter.
|
|
43
|
+
*/
|
|
44
|
+
avatar_large_url: string;
|
|
45
|
+
/**
|
|
46
|
+
* User's profile name
|
|
47
|
+
*
|
|
48
|
+
* To return this field, add `fields=display_name` in the user profile request's query parameter.
|
|
49
|
+
*/
|
|
50
|
+
display_name: string;
|
|
51
|
+
/**
|
|
52
|
+
* User's username.
|
|
53
|
+
*
|
|
54
|
+
* To return this field, add `fields=username` in the user profile request's query parameter.
|
|
55
|
+
*/
|
|
56
|
+
username: string;
|
|
57
|
+
/** @note Email is currently unsupported by TikTok */
|
|
58
|
+
email?: string | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* User's bio description if there is a valid one.
|
|
61
|
+
*
|
|
62
|
+
* To return this field, add `fields=bio_description` in the user profile request's query parameter.
|
|
63
|
+
*/
|
|
64
|
+
bio_description?: string | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* The link to user's TikTok profile page.
|
|
67
|
+
*
|
|
68
|
+
* To return this field, add `fields=profile_deep_link` in the user profile request's query parameter.
|
|
69
|
+
*/
|
|
70
|
+
profile_deep_link?: string | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Whether TikTok has provided a verified badge to the account after confirming
|
|
73
|
+
* that it belongs to the user it represents.
|
|
74
|
+
*
|
|
75
|
+
* To return this field, add `fields=is_verified` in the user profile request's query parameter.
|
|
76
|
+
*/
|
|
77
|
+
is_verified?: boolean | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* User's followers count.
|
|
80
|
+
*
|
|
81
|
+
* To return this field, add `fields=follower_count` in the user profile request's query parameter.
|
|
82
|
+
*/
|
|
83
|
+
follower_count?: number | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* The number of accounts that the user is following.
|
|
86
|
+
*
|
|
87
|
+
* To return this field, add `fields=following_count` in the user profile request's query parameter.
|
|
88
|
+
*/
|
|
89
|
+
following_count?: number | undefined;
|
|
90
|
+
/**
|
|
91
|
+
* The total number of likes received by the user across all of their videos.
|
|
92
|
+
*
|
|
93
|
+
* To return this field, add `fields=likes_count` in the user profile request's query parameter.
|
|
94
|
+
*/
|
|
95
|
+
likes_count?: number | undefined;
|
|
96
|
+
/**
|
|
97
|
+
* The total number of publicly posted videos by the user.
|
|
98
|
+
*
|
|
99
|
+
* To return this field, add `fields=video_count` in the user profile request's query parameter.
|
|
100
|
+
*/
|
|
101
|
+
video_count?: number | undefined;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
error?:
|
|
105
|
+
| {
|
|
106
|
+
/**
|
|
107
|
+
* The error category in string.
|
|
108
|
+
*/
|
|
109
|
+
code?: string;
|
|
110
|
+
/**
|
|
111
|
+
* The error message in string.
|
|
112
|
+
*/
|
|
113
|
+
message?: string;
|
|
114
|
+
/**
|
|
115
|
+
* The error message in string.
|
|
116
|
+
*/
|
|
117
|
+
log_id?: string;
|
|
118
|
+
}
|
|
119
|
+
| undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface TiktokOptions extends ProviderOptions {
|
|
123
|
+
// Client ID is not used in TikTok, we delete it from the options
|
|
124
|
+
clientId?: never | undefined;
|
|
125
|
+
clientSecret: string;
|
|
126
|
+
clientKey: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const tiktok = (options: TiktokOptions) => {
|
|
130
|
+
return {
|
|
131
|
+
id: "tiktok",
|
|
132
|
+
name: "TikTok",
|
|
133
|
+
createAuthorizationURL({ state, scopes, redirectURI }) {
|
|
134
|
+
const _scopes = options.disableDefaultScope ? [] : ["user.info.profile"];
|
|
135
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
136
|
+
if (scopes) _scopes.push(...scopes);
|
|
137
|
+
return new URL(
|
|
138
|
+
`https://www.tiktok.com/v2/auth/authorize?scope=${_scopes.join(
|
|
139
|
+
",",
|
|
140
|
+
)}&response_type=code&client_key=${options.clientKey}&redirect_uri=${encodeURIComponent(
|
|
141
|
+
options.redirectURI || redirectURI,
|
|
142
|
+
)}&state=${state}`,
|
|
143
|
+
);
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
147
|
+
return validateAuthorizationCode({
|
|
148
|
+
code,
|
|
149
|
+
redirectURI: options.redirectURI || redirectURI,
|
|
150
|
+
options: {
|
|
151
|
+
clientKey: options.clientKey,
|
|
152
|
+
clientSecret: options.clientSecret,
|
|
153
|
+
},
|
|
154
|
+
tokenEndpoint: "https://open.tiktokapis.com/v2/oauth/token/",
|
|
155
|
+
});
|
|
156
|
+
},
|
|
157
|
+
refreshAccessToken: options.refreshAccessToken
|
|
158
|
+
? options.refreshAccessToken
|
|
159
|
+
: async (refreshToken) => {
|
|
160
|
+
return refreshAccessToken({
|
|
161
|
+
refreshToken,
|
|
162
|
+
options: {
|
|
163
|
+
clientSecret: options.clientSecret,
|
|
164
|
+
},
|
|
165
|
+
tokenEndpoint: "https://open.tiktokapis.com/v2/oauth/token/",
|
|
166
|
+
authentication: "post",
|
|
167
|
+
extraParams: {
|
|
168
|
+
client_key: options.clientKey,
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
async getUserInfo(token) {
|
|
173
|
+
if (options.getUserInfo) {
|
|
174
|
+
return options.getUserInfo(token);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const fields = [
|
|
178
|
+
"open_id",
|
|
179
|
+
"avatar_large_url",
|
|
180
|
+
"display_name",
|
|
181
|
+
"username",
|
|
182
|
+
];
|
|
183
|
+
const { data: profile, error } = await betterFetch<TiktokProfile>(
|
|
184
|
+
`https://open.tiktokapis.com/v2/user/info/?fields=${fields.join(",")}`,
|
|
185
|
+
{
|
|
186
|
+
headers: {
|
|
187
|
+
authorization: `Bearer ${token.accessToken}`,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
if (error) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
user: {
|
|
198
|
+
email: profile.data.user.email || profile.data.user.username,
|
|
199
|
+
id: profile.data.user.open_id,
|
|
200
|
+
name: profile.data.user.display_name || profile.data.user.username,
|
|
201
|
+
image: profile.data.user.avatar_large_url,
|
|
202
|
+
emailVerified: false,
|
|
203
|
+
},
|
|
204
|
+
data: profile,
|
|
205
|
+
};
|
|
206
|
+
},
|
|
207
|
+
options,
|
|
208
|
+
} satisfies OAuthProvider<TiktokProfile, TiktokOptions>;
|
|
209
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { decodeJwt } from "jose";
|
|
2
|
+
import { logger } from "../env";
|
|
3
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
4
|
+
import {
|
|
5
|
+
createAuthorizationURL,
|
|
6
|
+
refreshAccessToken,
|
|
7
|
+
validateAuthorizationCode,
|
|
8
|
+
} from "../oauth2";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @see https://dev.twitch.tv/docs/authentication/getting-tokens-oidc/#requesting-claims
|
|
12
|
+
*/
|
|
13
|
+
export interface TwitchProfile {
|
|
14
|
+
/**
|
|
15
|
+
* The sub of the user
|
|
16
|
+
*/
|
|
17
|
+
sub: string;
|
|
18
|
+
/**
|
|
19
|
+
* The preferred username of the user
|
|
20
|
+
*/
|
|
21
|
+
preferred_username: string;
|
|
22
|
+
/**
|
|
23
|
+
* The email of the user
|
|
24
|
+
*/
|
|
25
|
+
email: string;
|
|
26
|
+
/**
|
|
27
|
+
* Indicate if this user has a verified email.
|
|
28
|
+
*/
|
|
29
|
+
email_verified: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* The picture of the user
|
|
32
|
+
*/
|
|
33
|
+
picture: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface TwitchOptions extends ProviderOptions<TwitchProfile> {
|
|
37
|
+
clientId: string;
|
|
38
|
+
claims?: string[] | undefined;
|
|
39
|
+
}
|
|
40
|
+
export const twitch = (options: TwitchOptions) => {
|
|
41
|
+
return {
|
|
42
|
+
id: "twitch",
|
|
43
|
+
name: "Twitch",
|
|
44
|
+
createAuthorizationURL({ state, scopes, redirectURI }) {
|
|
45
|
+
const _scopes = options.disableDefaultScope
|
|
46
|
+
? []
|
|
47
|
+
: ["user:read:email", "openid"];
|
|
48
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
49
|
+
if (scopes) _scopes.push(...scopes);
|
|
50
|
+
return createAuthorizationURL({
|
|
51
|
+
id: "twitch",
|
|
52
|
+
redirectURI,
|
|
53
|
+
options,
|
|
54
|
+
authorizationEndpoint: "https://id.twitch.tv/oauth2/authorize",
|
|
55
|
+
scopes: _scopes,
|
|
56
|
+
state,
|
|
57
|
+
claims: options.claims || [
|
|
58
|
+
"email",
|
|
59
|
+
"email_verified",
|
|
60
|
+
"preferred_username",
|
|
61
|
+
"picture",
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
66
|
+
return validateAuthorizationCode({
|
|
67
|
+
code,
|
|
68
|
+
redirectURI,
|
|
69
|
+
options,
|
|
70
|
+
tokenEndpoint: "https://id.twitch.tv/oauth2/token",
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
refreshAccessToken: options.refreshAccessToken
|
|
74
|
+
? options.refreshAccessToken
|
|
75
|
+
: async (refreshToken) => {
|
|
76
|
+
return refreshAccessToken({
|
|
77
|
+
refreshToken,
|
|
78
|
+
options: {
|
|
79
|
+
clientId: options.clientId,
|
|
80
|
+
clientKey: options.clientKey,
|
|
81
|
+
clientSecret: options.clientSecret,
|
|
82
|
+
},
|
|
83
|
+
tokenEndpoint: "https://id.twitch.tv/oauth2/token",
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
async getUserInfo(token) {
|
|
87
|
+
if (options.getUserInfo) {
|
|
88
|
+
return options.getUserInfo(token);
|
|
89
|
+
}
|
|
90
|
+
const idToken = token.idToken;
|
|
91
|
+
if (!idToken) {
|
|
92
|
+
logger.error("No idToken found in token");
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const profile = decodeJwt(idToken) as TwitchProfile;
|
|
96
|
+
const userMap = await options.mapProfileToUser?.(profile);
|
|
97
|
+
return {
|
|
98
|
+
user: {
|
|
99
|
+
id: profile.sub,
|
|
100
|
+
name: profile.preferred_username,
|
|
101
|
+
email: profile.email,
|
|
102
|
+
image: profile.picture,
|
|
103
|
+
emailVerified: profile.email_verified,
|
|
104
|
+
...userMap,
|
|
105
|
+
},
|
|
106
|
+
data: profile,
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
options,
|
|
110
|
+
} satisfies OAuthProvider<TwitchProfile>;
|
|
111
|
+
};
|