@better-auth/core 1.3.26 → 1.3.28
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 +60 -9
- package/build.config.ts +7 -0
- package/dist/db/adapter/index.cjs +2 -0
- package/dist/db/adapter/index.d.cts +14 -0
- package/dist/db/adapter/index.d.mts +14 -0
- package/dist/db/adapter/index.d.ts +14 -0
- package/dist/db/adapter/index.mjs +1 -0
- package/dist/db/index.cjs +89 -0
- package/dist/db/index.d.cts +16 -107
- package/dist/db/index.d.mts +16 -107
- package/dist/db/index.d.ts +16 -107
- package/dist/db/index.mjs +69 -0
- package/dist/env/index.cjs +312 -0
- package/dist/env/index.d.cts +36 -0
- package/dist/env/index.d.mts +36 -0
- package/dist/env/index.d.ts +36 -0
- package/dist/env/index.mjs +297 -0
- package/dist/error/index.cjs +44 -0
- package/dist/error/index.d.cts +33 -0
- package/dist/error/index.d.mts +33 -0
- package/dist/error/index.d.ts +33 -0
- package/dist/error/index.mjs +41 -0
- package/dist/index.d.cts +179 -1
- package/dist/index.d.mts +179 -1
- package/dist/index.d.ts +179 -1
- package/dist/middleware/index.cjs +25 -0
- package/dist/middleware/index.d.cts +14 -0
- package/dist/middleware/index.d.mts +14 -0
- package/dist/middleware/index.d.ts +14 -0
- package/dist/middleware/index.mjs +21 -0
- package/dist/oauth2/index.cjs +368 -0
- package/dist/oauth2/index.d.cts +100 -0
- package/dist/oauth2/index.d.mts +100 -0
- package/dist/oauth2/index.d.ts +100 -0
- package/dist/oauth2/index.mjs +357 -0
- package/dist/shared/core.BJPBStdk.d.ts +1693 -0
- package/dist/shared/core.Bl6TpxyD.d.mts +181 -0
- package/dist/shared/core.Bqe5IGAi.d.ts +13 -0
- package/dist/shared/core.BwoNUcJQ.d.cts +53 -0
- package/dist/shared/core.BwoNUcJQ.d.mts +53 -0
- package/dist/shared/core.BwoNUcJQ.d.ts +53 -0
- package/dist/shared/core.CajxAutx.d.cts +143 -0
- package/dist/shared/core.CajxAutx.d.mts +143 -0
- package/dist/shared/core.CajxAutx.d.ts +143 -0
- package/dist/shared/core.CkkLHQWc.d.mts +1693 -0
- package/dist/shared/core.DkdZ1o38.d.ts +181 -0
- package/dist/shared/core.Dl-70uns.d.cts +84 -0
- package/dist/shared/core.Dl-70uns.d.mts +84 -0
- package/dist/shared/core.Dl-70uns.d.ts +84 -0
- package/dist/shared/core.DyEdx0m7.d.cts +181 -0
- package/dist/shared/core.E9DfzGLz.d.mts +13 -0
- package/dist/shared/core.HqYn20Fi.d.cts +13 -0
- package/dist/shared/core.gYIBmdi1.d.cts +1693 -0
- package/dist/social-providers/index.cjs +2793 -0
- package/dist/social-providers/index.d.cts +3903 -0
- package/dist/social-providers/index.d.mts +3903 -0
- package/dist/social-providers/index.d.ts +3903 -0
- package/dist/social-providers/index.mjs +2743 -0
- package/dist/utils/index.cjs +7 -0
- package/dist/utils/index.d.cts +10 -0
- package/dist/utils/index.d.mts +10 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.mjs +5 -0
- package/package.json +109 -2
- package/src/db/adapter/index.ts +448 -0
- package/src/db/index.ts +13 -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/type.ts +50 -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.test.ts +33 -0
- package/src/env/logger.ts +145 -0
- package/src/error/codes.ts +31 -0
- package/src/error/index.ts +11 -0
- package/src/index.ts +1 -1
- package/src/middleware/index.ts +33 -0
- package/src/oauth2/client-credentials-token.ts +102 -0
- package/src/oauth2/create-authorization-url.ts +85 -0
- package/src/oauth2/index.ts +22 -0
- package/src/oauth2/oauth-provider.ts +194 -0
- package/src/oauth2/refresh-access-token.ts +124 -0
- package/src/oauth2/utils.ts +36 -0
- package/src/oauth2/validate-authorization-code.ts +156 -0
- package/src/social-providers/apple.ts +213 -0
- package/src/social-providers/atlassian.ts +130 -0
- package/src/social-providers/cognito.ts +269 -0
- package/src/social-providers/discord.ts +172 -0
- package/src/social-providers/dropbox.ts +112 -0
- package/src/social-providers/facebook.ts +204 -0
- package/src/social-providers/figma.ts +115 -0
- package/src/social-providers/github.ts +154 -0
- package/src/social-providers/gitlab.ts +152 -0
- package/src/social-providers/google.ts +171 -0
- package/src/social-providers/huggingface.ts +116 -0
- package/src/social-providers/index.ts +118 -0
- package/src/social-providers/kakao.ts +178 -0
- package/src/social-providers/kick.ts +95 -0
- package/src/social-providers/line.ts +169 -0
- package/src/social-providers/linear.ts +120 -0
- package/src/social-providers/linkedin.ts +110 -0
- package/src/social-providers/microsoft-entra-id.ts +243 -0
- package/src/social-providers/naver.ts +112 -0
- package/src/social-providers/notion.ts +106 -0
- package/src/social-providers/paypal.ts +261 -0
- package/src/social-providers/reddit.ts +122 -0
- package/src/social-providers/roblox.ts +110 -0
- package/src/social-providers/salesforce.ts +157 -0
- package/src/social-providers/slack.ts +114 -0
- package/src/social-providers/spotify.ts +93 -0
- package/src/social-providers/tiktok.ts +211 -0
- package/src/social-providers/twitch.ts +111 -0
- package/src/social-providers/twitter.ts +194 -0
- package/src/social-providers/vk.ts +128 -0
- package/src/social-providers/zoom.ts +218 -0
- package/src/types/context.ts +313 -0
- package/src/types/cookie.ts +7 -0
- package/src/types/helper.ts +5 -0
- package/src/types/index.ts +20 -1
- package/src/types/init-options.ts +1161 -0
- package/src/types/plugin-client.ts +69 -0
- package/src/types/plugin.ts +134 -0
- package/src/utils/error-codes.ts +51 -0
- package/src/utils/index.ts +1 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
+
import { decodeJwt } from "jose";
|
|
3
|
+
import { BetterAuthError } from "../error";
|
|
4
|
+
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
|
5
|
+
import {
|
|
6
|
+
createAuthorizationURL,
|
|
7
|
+
validateAuthorizationCode,
|
|
8
|
+
} from "@better-auth/core/oauth2";
|
|
9
|
+
import { logger } from "@better-auth/core/env";
|
|
10
|
+
import { refreshAccessToken } from "@better-auth/core/oauth2";
|
|
11
|
+
|
|
12
|
+
export interface GoogleProfile {
|
|
13
|
+
aud: string;
|
|
14
|
+
azp: string;
|
|
15
|
+
email: string;
|
|
16
|
+
email_verified: boolean;
|
|
17
|
+
exp: number;
|
|
18
|
+
/**
|
|
19
|
+
* The family name of the user, or last name in most
|
|
20
|
+
* Western languages.
|
|
21
|
+
*/
|
|
22
|
+
family_name: string;
|
|
23
|
+
/**
|
|
24
|
+
* The given name of the user, or first name in most
|
|
25
|
+
* Western languages.
|
|
26
|
+
*/
|
|
27
|
+
given_name: string;
|
|
28
|
+
hd?: string;
|
|
29
|
+
iat: number;
|
|
30
|
+
iss: string;
|
|
31
|
+
jti?: string;
|
|
32
|
+
locale?: string;
|
|
33
|
+
name: string;
|
|
34
|
+
nbf?: number;
|
|
35
|
+
picture: string;
|
|
36
|
+
sub: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface GoogleOptions extends ProviderOptions<GoogleProfile> {
|
|
40
|
+
clientId: string;
|
|
41
|
+
/**
|
|
42
|
+
* The access type to use for the authorization code request
|
|
43
|
+
*/
|
|
44
|
+
accessType?: "offline" | "online";
|
|
45
|
+
/**
|
|
46
|
+
* The display mode to use for the authorization code request
|
|
47
|
+
*/
|
|
48
|
+
display?: "page" | "popup" | "touch" | "wap";
|
|
49
|
+
/**
|
|
50
|
+
* The hosted domain of the user
|
|
51
|
+
*/
|
|
52
|
+
hd?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const google = (options: GoogleOptions) => {
|
|
56
|
+
return {
|
|
57
|
+
id: "google",
|
|
58
|
+
name: "Google",
|
|
59
|
+
async createAuthorizationURL({
|
|
60
|
+
state,
|
|
61
|
+
scopes,
|
|
62
|
+
codeVerifier,
|
|
63
|
+
redirectURI,
|
|
64
|
+
loginHint,
|
|
65
|
+
display,
|
|
66
|
+
}) {
|
|
67
|
+
if (!options.clientId || !options.clientSecret) {
|
|
68
|
+
logger.error(
|
|
69
|
+
"Client Id and Client Secret is required for Google. Make sure to provide them in the options.",
|
|
70
|
+
);
|
|
71
|
+
throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
|
|
72
|
+
}
|
|
73
|
+
if (!codeVerifier) {
|
|
74
|
+
throw new BetterAuthError("codeVerifier is required for Google");
|
|
75
|
+
}
|
|
76
|
+
const _scopes = options.disableDefaultScope
|
|
77
|
+
? []
|
|
78
|
+
: ["email", "profile", "openid"];
|
|
79
|
+
options.scope && _scopes.push(...options.scope);
|
|
80
|
+
scopes && _scopes.push(...scopes);
|
|
81
|
+
const url = await createAuthorizationURL({
|
|
82
|
+
id: "google",
|
|
83
|
+
options,
|
|
84
|
+
authorizationEndpoint: "https://accounts.google.com/o/oauth2/auth",
|
|
85
|
+
scopes: _scopes,
|
|
86
|
+
state,
|
|
87
|
+
codeVerifier,
|
|
88
|
+
redirectURI,
|
|
89
|
+
prompt: options.prompt,
|
|
90
|
+
accessType: options.accessType,
|
|
91
|
+
display: display || options.display,
|
|
92
|
+
loginHint,
|
|
93
|
+
hd: options.hd,
|
|
94
|
+
additionalParams: {
|
|
95
|
+
include_granted_scopes: "true",
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
return url;
|
|
99
|
+
},
|
|
100
|
+
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
101
|
+
return validateAuthorizationCode({
|
|
102
|
+
code,
|
|
103
|
+
codeVerifier,
|
|
104
|
+
redirectURI,
|
|
105
|
+
options,
|
|
106
|
+
tokenEndpoint: "https://oauth2.googleapis.com/token",
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
refreshAccessToken: options.refreshAccessToken
|
|
110
|
+
? options.refreshAccessToken
|
|
111
|
+
: async (refreshToken) => {
|
|
112
|
+
return refreshAccessToken({
|
|
113
|
+
refreshToken,
|
|
114
|
+
options: {
|
|
115
|
+
clientId: options.clientId,
|
|
116
|
+
clientKey: options.clientKey,
|
|
117
|
+
clientSecret: options.clientSecret,
|
|
118
|
+
},
|
|
119
|
+
tokenEndpoint: "https://www.googleapis.com/oauth2/v4/token",
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
async verifyIdToken(token, nonce) {
|
|
123
|
+
if (options.disableIdTokenSignIn) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if (options.verifyIdToken) {
|
|
127
|
+
return options.verifyIdToken(token, nonce);
|
|
128
|
+
}
|
|
129
|
+
const googlePublicKeyUrl = `https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${token}`;
|
|
130
|
+
const { data: tokenInfo } = await betterFetch<{
|
|
131
|
+
aud: string;
|
|
132
|
+
iss: string;
|
|
133
|
+
email: string;
|
|
134
|
+
email_verified: boolean;
|
|
135
|
+
name: string;
|
|
136
|
+
picture: string;
|
|
137
|
+
sub: string;
|
|
138
|
+
}>(googlePublicKeyUrl);
|
|
139
|
+
if (!tokenInfo) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
const isValid =
|
|
143
|
+
tokenInfo.aud === options.clientId &&
|
|
144
|
+
(tokenInfo.iss === "https://accounts.google.com" ||
|
|
145
|
+
tokenInfo.iss === "accounts.google.com");
|
|
146
|
+
return isValid;
|
|
147
|
+
},
|
|
148
|
+
async getUserInfo(token) {
|
|
149
|
+
if (options.getUserInfo) {
|
|
150
|
+
return options.getUserInfo(token);
|
|
151
|
+
}
|
|
152
|
+
if (!token.idToken) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
const user = decodeJwt(token.idToken) as GoogleProfile;
|
|
156
|
+
const userMap = await options.mapProfileToUser?.(user);
|
|
157
|
+
return {
|
|
158
|
+
user: {
|
|
159
|
+
id: user.sub,
|
|
160
|
+
name: user.name,
|
|
161
|
+
email: user.email,
|
|
162
|
+
image: user.picture,
|
|
163
|
+
emailVerified: user.email_verified,
|
|
164
|
+
...userMap,
|
|
165
|
+
},
|
|
166
|
+
data: user,
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
options,
|
|
170
|
+
} satisfies OAuthProvider<GoogleProfile>;
|
|
171
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
+
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
|
3
|
+
import {
|
|
4
|
+
createAuthorizationURL,
|
|
5
|
+
validateAuthorizationCode,
|
|
6
|
+
refreshAccessToken,
|
|
7
|
+
} from "@better-auth/core/oauth2";
|
|
8
|
+
|
|
9
|
+
export interface HuggingFaceProfile {
|
|
10
|
+
sub: string;
|
|
11
|
+
name: string;
|
|
12
|
+
preferred_username: string;
|
|
13
|
+
profile: string;
|
|
14
|
+
picture: string;
|
|
15
|
+
website?: string;
|
|
16
|
+
email?: string;
|
|
17
|
+
email_verified?: boolean;
|
|
18
|
+
isPro: boolean;
|
|
19
|
+
canPay?: boolean;
|
|
20
|
+
orgs?: {
|
|
21
|
+
sub: string;
|
|
22
|
+
name: string;
|
|
23
|
+
picture: string;
|
|
24
|
+
preferred_username: string;
|
|
25
|
+
isEnterprise: boolean | "plus";
|
|
26
|
+
canPay?: boolean;
|
|
27
|
+
roleInOrg?: "admin" | "write" | "contributor" | "read";
|
|
28
|
+
pendingSSO?: boolean;
|
|
29
|
+
missingMFA?: boolean;
|
|
30
|
+
resourceGroups?: {
|
|
31
|
+
sub: string;
|
|
32
|
+
name: string;
|
|
33
|
+
role: "admin" | "write" | "contributor" | "read";
|
|
34
|
+
}[];
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface HuggingFaceOptions
|
|
39
|
+
extends ProviderOptions<HuggingFaceProfile> {
|
|
40
|
+
clientId: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const huggingface = (options: HuggingFaceOptions) => {
|
|
44
|
+
return {
|
|
45
|
+
id: "huggingface",
|
|
46
|
+
name: "Hugging Face",
|
|
47
|
+
createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
|
|
48
|
+
const _scopes = options.disableDefaultScope
|
|
49
|
+
? []
|
|
50
|
+
: ["openid", "profile", "email"];
|
|
51
|
+
options.scope && _scopes.push(...options.scope);
|
|
52
|
+
scopes && _scopes.push(...scopes);
|
|
53
|
+
return createAuthorizationURL({
|
|
54
|
+
id: "huggingface",
|
|
55
|
+
options,
|
|
56
|
+
authorizationEndpoint: "https://huggingface.co/oauth/authorize",
|
|
57
|
+
scopes: _scopes,
|
|
58
|
+
state,
|
|
59
|
+
codeVerifier,
|
|
60
|
+
redirectURI,
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
64
|
+
return validateAuthorizationCode({
|
|
65
|
+
code,
|
|
66
|
+
codeVerifier,
|
|
67
|
+
redirectURI,
|
|
68
|
+
options,
|
|
69
|
+
tokenEndpoint: "https://huggingface.co/oauth/token",
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
refreshAccessToken: options.refreshAccessToken
|
|
73
|
+
? options.refreshAccessToken
|
|
74
|
+
: async (refreshToken) => {
|
|
75
|
+
return refreshAccessToken({
|
|
76
|
+
refreshToken,
|
|
77
|
+
options: {
|
|
78
|
+
clientId: options.clientId,
|
|
79
|
+
clientKey: options.clientKey,
|
|
80
|
+
clientSecret: options.clientSecret,
|
|
81
|
+
},
|
|
82
|
+
tokenEndpoint: "https://huggingface.co/oauth/token",
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
async getUserInfo(token) {
|
|
86
|
+
if (options.getUserInfo) {
|
|
87
|
+
return options.getUserInfo(token);
|
|
88
|
+
}
|
|
89
|
+
const { data: profile, error } = await betterFetch<HuggingFaceProfile>(
|
|
90
|
+
"https://huggingface.co/oauth/userinfo",
|
|
91
|
+
{
|
|
92
|
+
method: "GET",
|
|
93
|
+
headers: {
|
|
94
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
if (error) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const userMap = await options.mapProfileToUser?.(profile);
|
|
102
|
+
return {
|
|
103
|
+
user: {
|
|
104
|
+
id: profile.sub,
|
|
105
|
+
name: profile.name || profile.preferred_username,
|
|
106
|
+
email: profile.email,
|
|
107
|
+
image: profile.picture,
|
|
108
|
+
emailVerified: profile.email_verified ?? false,
|
|
109
|
+
...userMap,
|
|
110
|
+
},
|
|
111
|
+
data: profile,
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
options,
|
|
115
|
+
} satisfies OAuthProvider<HuggingFaceProfile>;
|
|
116
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { apple } from "./apple";
|
|
3
|
+
import { atlassian } from "./atlassian";
|
|
4
|
+
import { cognito } from "./cognito";
|
|
5
|
+
import { discord } from "./discord";
|
|
6
|
+
import { facebook } from "./facebook";
|
|
7
|
+
import { figma } from "./figma";
|
|
8
|
+
import { github } from "./github";
|
|
9
|
+
import { google } from "./google";
|
|
10
|
+
import { kick } from "./kick";
|
|
11
|
+
import { huggingface } from "./huggingface";
|
|
12
|
+
import { microsoft } from "./microsoft-entra-id";
|
|
13
|
+
import { slack } from "./slack";
|
|
14
|
+
import { notion } from "./notion";
|
|
15
|
+
import { spotify } from "./spotify";
|
|
16
|
+
import { twitch } from "./twitch";
|
|
17
|
+
import { twitter } from "./twitter";
|
|
18
|
+
import { dropbox } from "./dropbox";
|
|
19
|
+
import { linear } from "./linear";
|
|
20
|
+
import { linkedin } from "./linkedin";
|
|
21
|
+
import { gitlab } from "./gitlab";
|
|
22
|
+
import { tiktok } from "./tiktok";
|
|
23
|
+
import { reddit } from "./reddit";
|
|
24
|
+
import { roblox } from "./roblox";
|
|
25
|
+
import { salesforce } from "./salesforce";
|
|
26
|
+
import { vk } from "./vk";
|
|
27
|
+
import { zoom } from "./zoom";
|
|
28
|
+
import { kakao } from "./kakao";
|
|
29
|
+
import { naver } from "./naver";
|
|
30
|
+
import { line } from "./line";
|
|
31
|
+
import { paypal } from "./paypal";
|
|
32
|
+
|
|
33
|
+
export const socialProviders = {
|
|
34
|
+
apple,
|
|
35
|
+
atlassian,
|
|
36
|
+
cognito,
|
|
37
|
+
discord,
|
|
38
|
+
facebook,
|
|
39
|
+
figma,
|
|
40
|
+
github,
|
|
41
|
+
microsoft,
|
|
42
|
+
google,
|
|
43
|
+
huggingface,
|
|
44
|
+
slack,
|
|
45
|
+
spotify,
|
|
46
|
+
twitch,
|
|
47
|
+
twitter,
|
|
48
|
+
dropbox,
|
|
49
|
+
kick,
|
|
50
|
+
linear,
|
|
51
|
+
linkedin,
|
|
52
|
+
gitlab,
|
|
53
|
+
tiktok,
|
|
54
|
+
reddit,
|
|
55
|
+
roblox,
|
|
56
|
+
salesforce,
|
|
57
|
+
vk,
|
|
58
|
+
zoom,
|
|
59
|
+
notion,
|
|
60
|
+
kakao,
|
|
61
|
+
naver,
|
|
62
|
+
line,
|
|
63
|
+
paypal,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const socialProviderList = Object.keys(socialProviders) as [
|
|
67
|
+
"github",
|
|
68
|
+
...(keyof typeof socialProviders)[],
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
export const SocialProviderListEnum = z
|
|
72
|
+
.enum(socialProviderList)
|
|
73
|
+
.or(z.string()) as z.ZodType<SocialProviderList[number] | (string & {})>;
|
|
74
|
+
|
|
75
|
+
export type SocialProvider = z.infer<typeof SocialProviderListEnum>;
|
|
76
|
+
|
|
77
|
+
export type SocialProviders = {
|
|
78
|
+
[K in SocialProviderList[number]]?: Parameters<
|
|
79
|
+
(typeof socialProviders)[K]
|
|
80
|
+
>[0] & {
|
|
81
|
+
enabled?: boolean;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export * from "./apple";
|
|
86
|
+
export * from "./atlassian";
|
|
87
|
+
export * from "./cognito";
|
|
88
|
+
export * from "./discord";
|
|
89
|
+
export * from "./dropbox";
|
|
90
|
+
export * from "./facebook";
|
|
91
|
+
export * from "./figma";
|
|
92
|
+
export * from "./github";
|
|
93
|
+
export * from "./linear";
|
|
94
|
+
export * from "./linkedin";
|
|
95
|
+
export * from "./gitlab";
|
|
96
|
+
export * from "./google";
|
|
97
|
+
export * from "./kick";
|
|
98
|
+
export * from "./linkedin";
|
|
99
|
+
export * from "./microsoft-entra-id";
|
|
100
|
+
export * from "./notion";
|
|
101
|
+
export * from "./reddit";
|
|
102
|
+
export * from "./roblox";
|
|
103
|
+
export * from "./salesforce";
|
|
104
|
+
export * from "./spotify";
|
|
105
|
+
export * from "./tiktok";
|
|
106
|
+
export * from "./twitch";
|
|
107
|
+
export * from "./twitter";
|
|
108
|
+
export * from "./vk";
|
|
109
|
+
export * from "./zoom";
|
|
110
|
+
export * from "./kick";
|
|
111
|
+
export * from "./huggingface";
|
|
112
|
+
export * from "./slack";
|
|
113
|
+
export * from "./kakao";
|
|
114
|
+
export * from "./naver";
|
|
115
|
+
export * from "./line";
|
|
116
|
+
export * from "./paypal";
|
|
117
|
+
|
|
118
|
+
export type SocialProviderList = typeof socialProviderList;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
+
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
|
3
|
+
import {
|
|
4
|
+
createAuthorizationURL,
|
|
5
|
+
validateAuthorizationCode,
|
|
6
|
+
refreshAccessToken,
|
|
7
|
+
} from "@better-auth/core/oauth2";
|
|
8
|
+
|
|
9
|
+
interface Partner {
|
|
10
|
+
/** Partner-specific ID (consent required: kakaotalk_message) */
|
|
11
|
+
uuid?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Profile {
|
|
15
|
+
/** Nickname (consent required: profile/nickname) */
|
|
16
|
+
nickname?: string;
|
|
17
|
+
/** Thumbnail image URL (consent required: profile/profile image) */
|
|
18
|
+
thumbnail_image_url?: string;
|
|
19
|
+
/** Profile image URL (consent required: profile/profile image) */
|
|
20
|
+
profile_image_url?: string;
|
|
21
|
+
/** Whether the profile image is the default */
|
|
22
|
+
is_default_image?: boolean;
|
|
23
|
+
/** Whether the nickname is the default */
|
|
24
|
+
is_default_nickname?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface KakaoAccount {
|
|
28
|
+
/** Consent required: profile info (nickname/profile image) */
|
|
29
|
+
profile_needs_agreement?: boolean;
|
|
30
|
+
/** Consent required: nickname */
|
|
31
|
+
profile_nickname_needs_agreement?: boolean;
|
|
32
|
+
/** Consent required: profile image */
|
|
33
|
+
profile_image_needs_agreement?: boolean;
|
|
34
|
+
/** Profile info */
|
|
35
|
+
profile?: Profile;
|
|
36
|
+
/** Consent required: name */
|
|
37
|
+
name_needs_agreement?: boolean;
|
|
38
|
+
/** Name */
|
|
39
|
+
name?: string;
|
|
40
|
+
/** Consent required: email */
|
|
41
|
+
email_needs_agreement?: boolean;
|
|
42
|
+
/** Email valid */
|
|
43
|
+
is_email_valid?: boolean;
|
|
44
|
+
/** Email verified */
|
|
45
|
+
is_email_verified?: boolean;
|
|
46
|
+
/** Email */
|
|
47
|
+
email?: string;
|
|
48
|
+
/** Consent required: age range */
|
|
49
|
+
age_range_needs_agreement?: boolean;
|
|
50
|
+
/** Age range */
|
|
51
|
+
age_range?: string;
|
|
52
|
+
/** Consent required: birth year */
|
|
53
|
+
birthyear_needs_agreement?: boolean;
|
|
54
|
+
/** Birth year (YYYY) */
|
|
55
|
+
birthyear?: string;
|
|
56
|
+
/** Consent required: birthday */
|
|
57
|
+
birthday_needs_agreement?: boolean;
|
|
58
|
+
/** Birthday (MMDD) */
|
|
59
|
+
birthday?: string;
|
|
60
|
+
/** Birthday type (SOLAR/LUNAR) */
|
|
61
|
+
birthday_type?: string;
|
|
62
|
+
/** Whether birthday is in a leap month */
|
|
63
|
+
is_leap_month?: boolean;
|
|
64
|
+
/** Consent required: gender */
|
|
65
|
+
gender_needs_agreement?: boolean;
|
|
66
|
+
/** Gender (male/female) */
|
|
67
|
+
gender?: string;
|
|
68
|
+
/** Consent required: phone number */
|
|
69
|
+
phone_number_needs_agreement?: boolean;
|
|
70
|
+
/** Phone number */
|
|
71
|
+
phone_number?: string;
|
|
72
|
+
/** Consent required: CI */
|
|
73
|
+
ci_needs_agreement?: boolean;
|
|
74
|
+
/** CI (unique identifier) */
|
|
75
|
+
ci?: string;
|
|
76
|
+
/** CI authentication time (UTC) */
|
|
77
|
+
ci_authenticated_at?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface KakaoProfile {
|
|
81
|
+
/** Kakao user ID */
|
|
82
|
+
id: number;
|
|
83
|
+
/**
|
|
84
|
+
* Whether the user has signed up (only present if auto-connection is disabled)
|
|
85
|
+
* false: preregistered, true: registered
|
|
86
|
+
*/
|
|
87
|
+
has_signed_up?: boolean;
|
|
88
|
+
/** UTC datetime when the user connected the service */
|
|
89
|
+
connected_at?: string;
|
|
90
|
+
/** UTC datetime when the user signed up via Kakao Sync */
|
|
91
|
+
synched_at?: string;
|
|
92
|
+
/** Custom user properties */
|
|
93
|
+
properties?: Record<string, any>;
|
|
94
|
+
/** Kakao account info */
|
|
95
|
+
kakao_account: KakaoAccount;
|
|
96
|
+
/** Partner info */
|
|
97
|
+
for_partner?: Partner;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface KakaoOptions extends ProviderOptions<KakaoProfile> {
|
|
101
|
+
clientId: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const kakao = (options: KakaoOptions) => {
|
|
105
|
+
return {
|
|
106
|
+
id: "kakao",
|
|
107
|
+
name: "Kakao",
|
|
108
|
+
createAuthorizationURL({ state, scopes, redirectURI }) {
|
|
109
|
+
const _scopes = options.disableDefaultScope
|
|
110
|
+
? []
|
|
111
|
+
: ["account_email", "profile_image", "profile_nickname"];
|
|
112
|
+
options.scope && _scopes.push(...options.scope);
|
|
113
|
+
scopes && _scopes.push(...scopes);
|
|
114
|
+
return createAuthorizationURL({
|
|
115
|
+
id: "kakao",
|
|
116
|
+
options,
|
|
117
|
+
authorizationEndpoint: "https://kauth.kakao.com/oauth/authorize",
|
|
118
|
+
scopes: _scopes,
|
|
119
|
+
state,
|
|
120
|
+
redirectURI,
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
124
|
+
return validateAuthorizationCode({
|
|
125
|
+
code,
|
|
126
|
+
redirectURI,
|
|
127
|
+
options,
|
|
128
|
+
tokenEndpoint: "https://kauth.kakao.com/oauth/token",
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
refreshAccessToken: options.refreshAccessToken
|
|
132
|
+
? options.refreshAccessToken
|
|
133
|
+
: async (refreshToken) => {
|
|
134
|
+
return refreshAccessToken({
|
|
135
|
+
refreshToken,
|
|
136
|
+
options: {
|
|
137
|
+
clientId: options.clientId,
|
|
138
|
+
clientKey: options.clientKey,
|
|
139
|
+
clientSecret: options.clientSecret,
|
|
140
|
+
},
|
|
141
|
+
tokenEndpoint: "https://kauth.kakao.com/oauth/token",
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
async getUserInfo(token) {
|
|
145
|
+
if (options.getUserInfo) {
|
|
146
|
+
return options.getUserInfo(token);
|
|
147
|
+
}
|
|
148
|
+
const { data: profile, error } = await betterFetch<KakaoProfile>(
|
|
149
|
+
"https://kapi.kakao.com/v2/user/me",
|
|
150
|
+
{
|
|
151
|
+
headers: {
|
|
152
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
if (error || !profile) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const userMap = await options.mapProfileToUser?.(profile);
|
|
160
|
+
const account = profile.kakao_account || {};
|
|
161
|
+
const kakaoProfile = account.profile || {};
|
|
162
|
+
const user = {
|
|
163
|
+
id: String(profile.id),
|
|
164
|
+
name: kakaoProfile.nickname || account.name || undefined,
|
|
165
|
+
email: account.email,
|
|
166
|
+
image:
|
|
167
|
+
kakaoProfile.profile_image_url || kakaoProfile.thumbnail_image_url,
|
|
168
|
+
emailVerified: !!account.is_email_valid && !!account.is_email_verified,
|
|
169
|
+
...userMap,
|
|
170
|
+
};
|
|
171
|
+
return {
|
|
172
|
+
user,
|
|
173
|
+
data: profile,
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
options,
|
|
177
|
+
} satisfies OAuthProvider<KakaoProfile>;
|
|
178
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
+
import {
|
|
3
|
+
createAuthorizationURL,
|
|
4
|
+
validateAuthorizationCode,
|
|
5
|
+
} from "@better-auth/core/oauth2";
|
|
6
|
+
import type { OAuthProvider, ProviderOptions } from "@better-auth/core/oauth2";
|
|
7
|
+
|
|
8
|
+
export interface KickProfile {
|
|
9
|
+
/**
|
|
10
|
+
* The user id of the user
|
|
11
|
+
*/
|
|
12
|
+
user_id: string;
|
|
13
|
+
/**
|
|
14
|
+
* The name of the user
|
|
15
|
+
*/
|
|
16
|
+
name: string;
|
|
17
|
+
/**
|
|
18
|
+
* The email of the user
|
|
19
|
+
*/
|
|
20
|
+
email: string;
|
|
21
|
+
/**
|
|
22
|
+
* The picture of the user
|
|
23
|
+
*/
|
|
24
|
+
profile_picture: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface KickOptions extends ProviderOptions<KickProfile> {
|
|
28
|
+
clientId: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const kick = (options: KickOptions) => {
|
|
32
|
+
return {
|
|
33
|
+
id: "kick",
|
|
34
|
+
name: "Kick",
|
|
35
|
+
createAuthorizationURL({ state, scopes, redirectURI, codeVerifier }) {
|
|
36
|
+
const _scopes = options.disableDefaultScope ? [] : ["user:read"];
|
|
37
|
+
options.scope && _scopes.push(...options.scope);
|
|
38
|
+
scopes && _scopes.push(...scopes);
|
|
39
|
+
|
|
40
|
+
return createAuthorizationURL({
|
|
41
|
+
id: "kick",
|
|
42
|
+
redirectURI,
|
|
43
|
+
options,
|
|
44
|
+
authorizationEndpoint: "https://id.kick.com/oauth/authorize",
|
|
45
|
+
scopes: _scopes,
|
|
46
|
+
codeVerifier,
|
|
47
|
+
state,
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
async validateAuthorizationCode({ code, redirectURI, codeVerifier }) {
|
|
51
|
+
return validateAuthorizationCode({
|
|
52
|
+
code,
|
|
53
|
+
redirectURI,
|
|
54
|
+
options,
|
|
55
|
+
tokenEndpoint: "https://id.kick.com/oauth/token",
|
|
56
|
+
codeVerifier,
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
async getUserInfo(token) {
|
|
60
|
+
if (options.getUserInfo) {
|
|
61
|
+
return options.getUserInfo(token);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const { data, error } = await betterFetch<{
|
|
65
|
+
data: KickProfile[];
|
|
66
|
+
}>("https://api.kick.com/public/v1/users", {
|
|
67
|
+
method: "GET",
|
|
68
|
+
headers: {
|
|
69
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (error) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const profile = data.data[0]!;
|
|
78
|
+
|
|
79
|
+
const userMap = await options.mapProfileToUser?.(profile);
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
user: {
|
|
83
|
+
id: profile.user_id,
|
|
84
|
+
name: profile.name,
|
|
85
|
+
email: profile.email,
|
|
86
|
+
image: profile.profile_picture,
|
|
87
|
+
emailVerified: true,
|
|
88
|
+
...userMap,
|
|
89
|
+
},
|
|
90
|
+
data: profile,
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
options,
|
|
94
|
+
} satisfies OAuthProvider<KickProfile>;
|
|
95
|
+
};
|