@better-auth/core 1.5.0-beta.13 → 1.5.0-beta.16
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/dist/api/index.d.mts +2 -0
- package/dist/context/global.mjs +1 -1
- package/dist/db/adapter/factory.mjs +12 -2
- package/dist/db/adapter/factory.mjs.map +1 -1
- package/dist/db/adapter/index.d.mts +4 -2
- package/dist/db/adapter/index.mjs +18 -1
- package/dist/db/adapter/index.mjs.map +1 -0
- package/dist/oauth2/refresh-access-token.mjs +4 -0
- package/dist/oauth2/refresh-access-token.mjs.map +1 -1
- package/dist/oauth2/validate-authorization-code.d.mts +2 -2
- package/dist/social-providers/apple.mjs +19 -15
- package/dist/social-providers/apple.mjs.map +1 -1
- package/dist/social-providers/cognito.mjs +2 -2
- package/dist/social-providers/cognito.mjs.map +1 -1
- package/dist/social-providers/github.mjs +1 -1
- package/dist/social-providers/github.mjs.map +1 -1
- package/dist/social-providers/gitlab.mjs +1 -1
- package/dist/social-providers/gitlab.mjs.map +1 -1
- package/dist/social-providers/google.mjs +14 -10
- package/dist/social-providers/google.mjs.map +1 -1
- package/dist/social-providers/huggingface.mjs +1 -1
- package/dist/social-providers/huggingface.mjs.map +1 -1
- package/dist/social-providers/index.d.mts +51 -2
- package/dist/social-providers/index.mjs +3 -1
- package/dist/social-providers/index.mjs.map +1 -1
- package/dist/social-providers/kakao.d.mts +1 -1
- package/dist/social-providers/kakao.mjs +1 -1
- package/dist/social-providers/kakao.mjs.map +1 -1
- package/dist/social-providers/line.mjs +1 -1
- package/dist/social-providers/line.mjs.map +1 -1
- package/dist/social-providers/naver.mjs +1 -1
- package/dist/social-providers/naver.mjs.map +1 -1
- package/dist/social-providers/notion.mjs +1 -1
- package/dist/social-providers/notion.mjs.map +1 -1
- package/dist/social-providers/paybin.mjs +1 -1
- package/dist/social-providers/paybin.mjs.map +1 -1
- package/dist/social-providers/polar.mjs +1 -1
- package/dist/social-providers/polar.mjs.map +1 -1
- package/dist/social-providers/railway.d.mts +68 -0
- package/dist/social-providers/railway.mjs +78 -0
- package/dist/social-providers/railway.mjs.map +1 -0
- package/dist/social-providers/tiktok.mjs +1 -1
- package/dist/social-providers/tiktok.mjs.map +1 -1
- package/dist/social-providers/vercel.mjs +1 -1
- package/dist/social-providers/vercel.mjs.map +1 -1
- package/dist/types/context.d.mts +5 -0
- package/dist/types/init-options.d.mts +20 -2
- package/dist/types/plugin.d.mts +1 -1
- package/package.json +8 -4
- package/src/db/adapter/factory.ts +22 -2
- package/src/db/adapter/index.ts +17 -15
- package/src/oauth2/refresh-access-token.test.ts +90 -0
- package/src/oauth2/refresh-access-token.ts +8 -0
- package/src/oauth2/validate-token.test.ts +1 -13
- package/src/social-providers/apple.ts +28 -24
- package/src/social-providers/cognito.ts +6 -5
- package/src/social-providers/github.ts +1 -1
- package/src/social-providers/gitlab.ts +1 -1
- package/src/social-providers/google.ts +16 -12
- package/src/social-providers/huggingface.ts +1 -1
- package/src/social-providers/index.ts +3 -0
- package/src/social-providers/kakao.ts +1 -1
- package/src/social-providers/line.ts +1 -1
- package/src/social-providers/naver.ts +1 -1
- package/src/social-providers/notion.ts +1 -1
- package/src/social-providers/paybin.ts +1 -5
- package/src/social-providers/polar.ts +1 -1
- package/src/social-providers/railway.ts +100 -0
- package/src/social-providers/tiktok.ts +2 -1
- package/src/social-providers/vercel.ts +1 -1
- package/src/types/context.ts +5 -0
- package/src/types/init-options.ts +36 -4
- package/src/types/plugin.ts +2 -1
- package/src/utils/deprecate.test.ts +0 -1
- package/.turbo/turbo-build.log +0 -265
- package/tsconfig.json +0 -7
- package/tsdown.config.ts +0 -35
- package/vitest.config.ts +0 -3
|
@@ -119,6 +119,7 @@ export async function refreshAccessToken({
|
|
|
119
119
|
access_token: string;
|
|
120
120
|
refresh_token?: string | undefined;
|
|
121
121
|
expires_in?: number | undefined;
|
|
122
|
+
refresh_token_expires_in?: number | undefined;
|
|
122
123
|
token_type?: string | undefined;
|
|
123
124
|
scope?: string | undefined;
|
|
124
125
|
id_token?: string | undefined;
|
|
@@ -145,5 +146,12 @@ export async function refreshAccessToken({
|
|
|
145
146
|
);
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
if (data.refresh_token_expires_in) {
|
|
150
|
+
const now = new Date();
|
|
151
|
+
tokens.refreshTokenExpiresAt = new Date(
|
|
152
|
+
now.getTime() + data.refresh_token_expires_in * 1000,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
148
156
|
return tokens;
|
|
149
157
|
}
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import type { JWK } from "jose";
|
|
2
2
|
import { exportJWK, generateKeyPair, SignJWT } from "jose";
|
|
3
|
-
import {
|
|
4
|
-
afterAll,
|
|
5
|
-
beforeAll,
|
|
6
|
-
beforeEach,
|
|
7
|
-
describe,
|
|
8
|
-
expect,
|
|
9
|
-
it,
|
|
10
|
-
vi,
|
|
11
|
-
} from "vitest";
|
|
3
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
12
4
|
import { validateToken } from "./validate-authorization-code";
|
|
13
5
|
|
|
14
6
|
describe("validateToken", () => {
|
|
@@ -24,10 +16,6 @@ describe("validateToken", () => {
|
|
|
24
16
|
globalThis.fetch = originalFetch;
|
|
25
17
|
});
|
|
26
18
|
|
|
27
|
-
beforeEach(() => {
|
|
28
|
-
mockedFetch.mockReset();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
19
|
async function createTestJWKS(alg: string, crv?: string) {
|
|
32
20
|
const { publicKey, privateKey } = await generateKeyPair(alg, {
|
|
33
21
|
crv,
|
|
@@ -112,30 +112,34 @@ export const apple = (options: AppleOptions) => {
|
|
|
112
112
|
if (options.verifyIdToken) {
|
|
113
113
|
return options.verifyIdToken(token, nonce);
|
|
114
114
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
jwtClaims[field]
|
|
115
|
+
try {
|
|
116
|
+
const decodedHeader = decodeProtectedHeader(token);
|
|
117
|
+
const { kid, alg: jwtAlg } = decodedHeader;
|
|
118
|
+
if (!kid || !jwtAlg) return false;
|
|
119
|
+
const publicKey = await getApplePublicKey(kid);
|
|
120
|
+
const { payload: jwtClaims } = await jwtVerify(token, publicKey, {
|
|
121
|
+
algorithms: [jwtAlg],
|
|
122
|
+
issuer: "https://appleid.apple.com",
|
|
123
|
+
audience:
|
|
124
|
+
options.audience && options.audience.length
|
|
125
|
+
? options.audience
|
|
126
|
+
: options.appBundleIdentifier
|
|
127
|
+
? options.appBundleIdentifier
|
|
128
|
+
: options.clientId,
|
|
129
|
+
maxTokenAge: "1h",
|
|
130
|
+
});
|
|
131
|
+
["email_verified", "is_private_email"].forEach((field) => {
|
|
132
|
+
if (jwtClaims[field] !== undefined) {
|
|
133
|
+
jwtClaims[field] = Boolean(jwtClaims[field]);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
if (nonce && jwtClaims.nonce !== nonce) {
|
|
137
|
+
return false;
|
|
133
138
|
}
|
|
134
|
-
|
|
135
|
-
|
|
139
|
+
return !!jwtClaims;
|
|
140
|
+
} catch {
|
|
136
141
|
return false;
|
|
137
142
|
}
|
|
138
|
-
return !!jwtClaims;
|
|
139
143
|
},
|
|
140
144
|
refreshAccessToken: options.refreshAccessToken
|
|
141
145
|
? options.refreshAccessToken
|
|
@@ -158,15 +162,15 @@ export const apple = (options: AppleOptions) => {
|
|
|
158
162
|
return null;
|
|
159
163
|
}
|
|
160
164
|
|
|
161
|
-
// TODO: "
|
|
165
|
+
// TODO: "" masking will be removed when the name field is made optional
|
|
162
166
|
let name: string;
|
|
163
167
|
if (token.user?.name) {
|
|
164
168
|
const firstName = token.user.name.firstName || "";
|
|
165
169
|
const lastName = token.user.name.lastName || "";
|
|
166
170
|
const fullName = `${firstName} ${lastName}`.trim();
|
|
167
|
-
name = fullName
|
|
171
|
+
name = fullName;
|
|
168
172
|
} else {
|
|
169
|
-
name = profile.name || "
|
|
173
|
+
name = profile.name || "";
|
|
170
174
|
}
|
|
171
175
|
|
|
172
176
|
const emailVerified =
|
|
@@ -178,10 +178,7 @@ export const cognito = (options: CognitoOptions) => {
|
|
|
178
178
|
return null;
|
|
179
179
|
}
|
|
180
180
|
const name =
|
|
181
|
-
profile.name ||
|
|
182
|
-
profile.given_name ||
|
|
183
|
-
profile.username ||
|
|
184
|
-
profile.email;
|
|
181
|
+
profile.name || profile.given_name || profile.username || "";
|
|
185
182
|
const enrichedProfile = {
|
|
186
183
|
...profile,
|
|
187
184
|
name,
|
|
@@ -220,7 +217,11 @@ export const cognito = (options: CognitoOptions) => {
|
|
|
220
217
|
return {
|
|
221
218
|
user: {
|
|
222
219
|
id: userInfo.sub,
|
|
223
|
-
name:
|
|
220
|
+
name:
|
|
221
|
+
userInfo.name ||
|
|
222
|
+
userInfo.given_name ||
|
|
223
|
+
userInfo.username ||
|
|
224
|
+
"",
|
|
224
225
|
email: userInfo.email,
|
|
225
226
|
image: userInfo.picture,
|
|
226
227
|
emailVerified: userInfo.email_verified,
|
|
@@ -170,7 +170,7 @@ export const github = (options: GithubOptions) => {
|
|
|
170
170
|
return {
|
|
171
171
|
user: {
|
|
172
172
|
id: profile.id,
|
|
173
|
-
name: profile.name || profile.login,
|
|
173
|
+
name: profile.name || profile.login || "",
|
|
174
174
|
email: profile.email,
|
|
175
175
|
image: profile.avatar_url,
|
|
176
176
|
emailVerified,
|
|
@@ -141,7 +141,7 @@ export const gitlab = (options: GitlabOptions) => {
|
|
|
141
141
|
return {
|
|
142
142
|
user: {
|
|
143
143
|
id: profile.id,
|
|
144
|
-
name: profile.name ?? profile.username,
|
|
144
|
+
name: profile.name ?? profile.username ?? "",
|
|
145
145
|
email: profile.email,
|
|
146
146
|
image: profile.avatar_url,
|
|
147
147
|
emailVerified: profile.email_verified ?? false,
|
|
@@ -130,22 +130,26 @@ export const google = (options: GoogleOptions) => {
|
|
|
130
130
|
// Verify JWT integrity
|
|
131
131
|
// See https://developers.google.com/identity/sign-in/web/backend-auth#verify-the-integrity-of-the-id-token
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
try {
|
|
134
|
+
const { kid, alg: jwtAlg } = decodeProtectedHeader(token);
|
|
135
|
+
if (!kid || !jwtAlg) return false;
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
137
|
+
const publicKey = await getGooglePublicKey(kid);
|
|
138
|
+
const { payload: jwtClaims } = await jwtVerify(token, publicKey, {
|
|
139
|
+
algorithms: [jwtAlg],
|
|
140
|
+
issuer: ["https://accounts.google.com", "accounts.google.com"],
|
|
141
|
+
audience: options.clientId,
|
|
142
|
+
maxTokenAge: "1h",
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (nonce && jwtClaims.nonce !== nonce) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
143
148
|
|
|
144
|
-
|
|
149
|
+
return true;
|
|
150
|
+
} catch {
|
|
145
151
|
return false;
|
|
146
152
|
}
|
|
147
|
-
|
|
148
|
-
return true;
|
|
149
153
|
},
|
|
150
154
|
async getUserInfo(token) {
|
|
151
155
|
if (options.getUserInfo) {
|
|
@@ -104,7 +104,7 @@ export const huggingface = (options: HuggingFaceOptions) => {
|
|
|
104
104
|
return {
|
|
105
105
|
user: {
|
|
106
106
|
id: profile.sub,
|
|
107
|
-
name: profile.name || profile.preferred_username,
|
|
107
|
+
name: profile.name || profile.preferred_username || "",
|
|
108
108
|
email: profile.email,
|
|
109
109
|
image: profile.picture,
|
|
110
110
|
emailVerified: profile.email_verified ?? false,
|
|
@@ -22,6 +22,7 @@ import { notion } from "./notion";
|
|
|
22
22
|
import { paybin } from "./paybin";
|
|
23
23
|
import { paypal } from "./paypal";
|
|
24
24
|
import { polar } from "./polar";
|
|
25
|
+
import { railway } from "./railway";
|
|
25
26
|
import { reddit } from "./reddit";
|
|
26
27
|
import { roblox } from "./roblox";
|
|
27
28
|
import { salesforce } from "./salesforce";
|
|
@@ -67,6 +68,7 @@ export const socialProviders = {
|
|
|
67
68
|
paybin,
|
|
68
69
|
paypal,
|
|
69
70
|
polar,
|
|
71
|
+
railway,
|
|
70
72
|
vercel,
|
|
71
73
|
};
|
|
72
74
|
|
|
@@ -113,6 +115,7 @@ export * from "./notion";
|
|
|
113
115
|
export * from "./paybin";
|
|
114
116
|
export * from "./paypal";
|
|
115
117
|
export * from "./polar";
|
|
118
|
+
export * from "./railway";
|
|
116
119
|
export * from "./reddit";
|
|
117
120
|
export * from "./roblox";
|
|
118
121
|
export * from "./salesforce";
|
|
@@ -161,7 +161,7 @@ export const kakao = (options: KakaoOptions) => {
|
|
|
161
161
|
const kakaoProfile = account.profile || {};
|
|
162
162
|
const user = {
|
|
163
163
|
id: String(profile.id),
|
|
164
|
-
name: kakaoProfile.nickname || account.name ||
|
|
164
|
+
name: kakaoProfile.nickname || account.name || "",
|
|
165
165
|
email: account.email,
|
|
166
166
|
image:
|
|
167
167
|
kakaoProfile.profile_image_url || kakaoProfile.thumbnail_image_url,
|
|
@@ -147,7 +147,7 @@ export const line = (options: LineOptions) => {
|
|
|
147
147
|
const userMap = await options.mapProfileToUser?.(profile as any);
|
|
148
148
|
// ID preference order
|
|
149
149
|
const id = (profile as any).sub || (profile as any).userId;
|
|
150
|
-
const name = (profile as any).name || (profile as any).displayName;
|
|
150
|
+
const name = (profile as any).name || (profile as any).displayName || "";
|
|
151
151
|
const image =
|
|
152
152
|
(profile as any).picture || (profile as any).pictureUrl || undefined;
|
|
153
153
|
const email = (profile as any).email;
|
|
@@ -96,7 +96,7 @@ export const naver = (options: NaverOptions) => {
|
|
|
96
96
|
const res = profile.response || {};
|
|
97
97
|
const user = {
|
|
98
98
|
id: res.id,
|
|
99
|
-
name: res.name || res.nickname,
|
|
99
|
+
name: res.name || res.nickname || "",
|
|
100
100
|
email: res.email,
|
|
101
101
|
image: res.profile_image,
|
|
102
102
|
emailVerified: false,
|
|
@@ -94,7 +94,7 @@ export const notion = (options: NotionOptions) => {
|
|
|
94
94
|
return {
|
|
95
95
|
user: {
|
|
96
96
|
id: userProfile.id,
|
|
97
|
-
name: userProfile.name || "
|
|
97
|
+
name: userProfile.name || "",
|
|
98
98
|
email: userProfile.person?.email || null,
|
|
99
99
|
image: userProfile.avatar_url,
|
|
100
100
|
emailVerified: false,
|
|
@@ -104,11 +104,7 @@ export const paybin = (options: PaybinOptions) => {
|
|
|
104
104
|
return {
|
|
105
105
|
user: {
|
|
106
106
|
id: user.sub,
|
|
107
|
-
name:
|
|
108
|
-
user.name ||
|
|
109
|
-
user.preferred_username ||
|
|
110
|
-
(user.email ? user.email.split("@")[0] : "User") ||
|
|
111
|
-
"User",
|
|
107
|
+
name: user.name || user.preferred_username || "",
|
|
112
108
|
email: user.email,
|
|
113
109
|
image: user.picture,
|
|
114
110
|
emailVerified: user.email_verified || false,
|
|
@@ -96,7 +96,7 @@ export const polar = (options: PolarOptions) => {
|
|
|
96
96
|
return {
|
|
97
97
|
user: {
|
|
98
98
|
id: profile.id,
|
|
99
|
-
name: profile.public_name || profile.username,
|
|
99
|
+
name: profile.public_name || profile.username || "",
|
|
100
100
|
email: profile.email,
|
|
101
101
|
image: profile.avatar_url,
|
|
102
102
|
emailVerified: profile.email_verified ?? false,
|
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
const authorizationEndpoint = "https://backboard.railway.com/oauth/auth";
|
|
10
|
+
const tokenEndpoint = "https://backboard.railway.com/oauth/token";
|
|
11
|
+
const userinfoEndpoint = "https://backboard.railway.com/oauth/me";
|
|
12
|
+
|
|
13
|
+
export interface RailwayProfile {
|
|
14
|
+
/** The user's unique ID (OAuth `sub` claim). */
|
|
15
|
+
sub: string;
|
|
16
|
+
/** The user's email address. */
|
|
17
|
+
email: string;
|
|
18
|
+
/** The user's display name. */
|
|
19
|
+
name: string;
|
|
20
|
+
/** URL of the user's profile picture. */
|
|
21
|
+
picture: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface RailwayOptions extends ProviderOptions<RailwayProfile> {
|
|
25
|
+
clientId: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const railway = (options: RailwayOptions) => {
|
|
29
|
+
return {
|
|
30
|
+
id: "railway",
|
|
31
|
+
name: "Railway",
|
|
32
|
+
createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
|
|
33
|
+
const _scopes = options.disableDefaultScope
|
|
34
|
+
? []
|
|
35
|
+
: ["openid", "email", "profile"];
|
|
36
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
37
|
+
if (scopes) _scopes.push(...scopes);
|
|
38
|
+
return createAuthorizationURL({
|
|
39
|
+
id: "railway",
|
|
40
|
+
options,
|
|
41
|
+
authorizationEndpoint,
|
|
42
|
+
scopes: _scopes,
|
|
43
|
+
state,
|
|
44
|
+
codeVerifier,
|
|
45
|
+
redirectURI,
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
49
|
+
return validateAuthorizationCode({
|
|
50
|
+
code,
|
|
51
|
+
codeVerifier,
|
|
52
|
+
redirectURI,
|
|
53
|
+
options,
|
|
54
|
+
tokenEndpoint,
|
|
55
|
+
authentication: "basic",
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
refreshAccessToken: options.refreshAccessToken
|
|
59
|
+
? options.refreshAccessToken
|
|
60
|
+
: async (refreshToken) => {
|
|
61
|
+
return refreshAccessToken({
|
|
62
|
+
refreshToken,
|
|
63
|
+
options: {
|
|
64
|
+
clientId: options.clientId,
|
|
65
|
+
clientKey: options.clientKey,
|
|
66
|
+
clientSecret: options.clientSecret,
|
|
67
|
+
},
|
|
68
|
+
tokenEndpoint,
|
|
69
|
+
authentication: "basic",
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
async getUserInfo(token) {
|
|
73
|
+
if (options.getUserInfo) {
|
|
74
|
+
return options.getUserInfo(token);
|
|
75
|
+
}
|
|
76
|
+
const { data: profile, error } = await betterFetch<RailwayProfile>(
|
|
77
|
+
userinfoEndpoint,
|
|
78
|
+
{ headers: { authorization: `Bearer ${token.accessToken}` } },
|
|
79
|
+
);
|
|
80
|
+
if (error || !profile) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const userMap = await options.mapProfileToUser?.(profile);
|
|
84
|
+
// Railway does not provide an email_verified claim.
|
|
85
|
+
// We default to false for security consistency.
|
|
86
|
+
return {
|
|
87
|
+
user: {
|
|
88
|
+
id: profile.sub,
|
|
89
|
+
name: profile.name,
|
|
90
|
+
email: profile.email,
|
|
91
|
+
image: profile.picture,
|
|
92
|
+
emailVerified: false,
|
|
93
|
+
...userMap,
|
|
94
|
+
},
|
|
95
|
+
data: profile,
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
options,
|
|
99
|
+
} satisfies OAuthProvider<RailwayProfile>;
|
|
100
|
+
};
|
|
@@ -197,7 +197,8 @@ export const tiktok = (options: TiktokOptions) => {
|
|
|
197
197
|
user: {
|
|
198
198
|
email: profile.data.user.email || profile.data.user.username,
|
|
199
199
|
id: profile.data.user.open_id,
|
|
200
|
-
name:
|
|
200
|
+
name:
|
|
201
|
+
profile.data.user.display_name || profile.data.user.username || "",
|
|
201
202
|
image: profile.data.user.avatar_large_url,
|
|
202
203
|
emailVerified: false,
|
|
203
204
|
},
|
|
@@ -73,7 +73,7 @@ export const vercel = (options: VercelOptions) => {
|
|
|
73
73
|
return {
|
|
74
74
|
user: {
|
|
75
75
|
id: profile.sub,
|
|
76
|
-
name: profile.name ?? profile.preferred_username,
|
|
76
|
+
name: profile.name ?? profile.preferred_username ?? "",
|
|
77
77
|
email: profile.email,
|
|
78
78
|
image: profile.picture,
|
|
79
79
|
emailVerified: profile.email_verified ?? false,
|
package/src/types/context.ts
CHANGED
|
@@ -265,6 +265,11 @@ export type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> =
|
|
|
265
265
|
InfoContext & {
|
|
266
266
|
options: Options;
|
|
267
267
|
trustedOrigins: string[];
|
|
268
|
+
/**
|
|
269
|
+
* Resolved list of trusted providers for account linking.
|
|
270
|
+
* Populated from "account.accountLinking.trustedProviders" (supports static array or async function).
|
|
271
|
+
*/
|
|
272
|
+
trustedProviders: string[];
|
|
268
273
|
/**
|
|
269
274
|
* Verifies whether url is a trusted origin according to the "trustedOrigins" configuration
|
|
270
275
|
* @param url The url to verify against the "trustedOrigins" configuration
|
|
@@ -887,11 +887,43 @@ export type BetterAuthOptions = {
|
|
|
887
887
|
*/
|
|
888
888
|
disableImplicitLinking?: boolean;
|
|
889
889
|
/**
|
|
890
|
-
* List of trusted providers
|
|
890
|
+
* List of trusted providers. Can be a static array or a function
|
|
891
|
+
* that returns providers dynamically. The function is called
|
|
892
|
+
* during context init (with `request` undefined) and again
|
|
893
|
+
* on each request (with the incoming Request). It must be
|
|
894
|
+
* resilient to `request` being undefined.
|
|
895
|
+
*
|
|
896
|
+
* @example
|
|
897
|
+
* ```ts
|
|
898
|
+
* trustedProviders: ["google", "github"]
|
|
899
|
+
* ```
|
|
900
|
+
*
|
|
901
|
+
* @example
|
|
902
|
+
* ```ts
|
|
903
|
+
* trustedProviders: async (request) => {
|
|
904
|
+
* if (!request) return [];
|
|
905
|
+
* const providers = await getTrustedProvidersForTenant(request);
|
|
906
|
+
* return providers;
|
|
907
|
+
* }
|
|
908
|
+
* ```
|
|
891
909
|
*/
|
|
892
|
-
trustedProviders?:
|
|
893
|
-
|
|
894
|
-
|
|
910
|
+
trustedProviders?:
|
|
911
|
+
| Array<
|
|
912
|
+
LiteralUnion<
|
|
913
|
+
SocialProviderList[number] | "email-password",
|
|
914
|
+
string
|
|
915
|
+
>
|
|
916
|
+
>
|
|
917
|
+
| ((
|
|
918
|
+
request?: Request | undefined,
|
|
919
|
+
) => Awaitable<
|
|
920
|
+
Array<
|
|
921
|
+
LiteralUnion<
|
|
922
|
+
SocialProviderList[number] | "email-password",
|
|
923
|
+
string
|
|
924
|
+
>
|
|
925
|
+
>
|
|
926
|
+
>);
|
|
895
927
|
/**
|
|
896
928
|
* If enabled (true), this will allow users to manually linking accounts with different email addresses than the main user.
|
|
897
929
|
*
|
package/src/types/plugin.ts
CHANGED
|
@@ -45,7 +45,8 @@ export type BetterAuthPlugin = BetterAuthPluginErrorCodePart & {
|
|
|
45
45
|
init?:
|
|
46
46
|
| ((ctx: AuthContext) =>
|
|
47
47
|
| Awaitable<{
|
|
48
|
-
context?: DeepPartial<Omit<AuthContext, "options"
|
|
48
|
+
context?: DeepPartial<Omit<AuthContext, "options">> &
|
|
49
|
+
Record<string, unknown>;
|
|
49
50
|
options?: Partial<BetterAuthOptions>;
|
|
50
51
|
}>
|
|
51
52
|
| void
|