sa2kit 3.2.1 → 3.2.3
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/common/auth/components/index.js +18 -0
- package/dist/common/auth/components/index.js.map +1 -1
- package/dist/common/auth/components/index.mjs +18 -0
- package/dist/common/auth/components/index.mjs.map +1 -1
- package/dist/common/auth/server/index.d.mts +86 -3
- package/dist/common/auth/server/index.d.ts +86 -3
- package/dist/common/auth/server/index.js +617 -1
- package/dist/common/auth/server/index.js.map +1 -1
- package/dist/common/auth/server/index.mjs +602 -3
- package/dist/common/auth/server/index.mjs.map +1 -1
- package/package.json +8 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { authDrizzleSchema } from '../../../chunk-EBHPTFG6.mjs';
|
|
1
|
+
import { account, authDrizzleSchema } from '../../../chunk-EBHPTFG6.mjs';
|
|
2
2
|
export { account, accountRelations, authDrizzleSchema, session, sessionRelations, user, userRelations, userRole, verification, verifications } from '../../../chunk-EBHPTFG6.mjs';
|
|
3
3
|
import '../../../chunk-MAI35PU6.mjs';
|
|
4
4
|
import { betterAuth } from 'better-auth';
|
|
5
5
|
import { drizzleAdapter } from '@better-auth/drizzle-adapter';
|
|
6
6
|
import { bearer, emailOTP, phoneNumber } from 'better-auth/plugins';
|
|
7
|
+
import { and, eq } from 'drizzle-orm';
|
|
8
|
+
import { hashPassword } from 'better-auth/crypto';
|
|
7
9
|
import { toNextJsHandler } from 'better-auth/next-js';
|
|
8
10
|
|
|
9
11
|
// src/common/auth/server/plugins/dev-otp.ts
|
|
@@ -17,6 +19,70 @@ function createDevOtpLogger(enabled) {
|
|
|
17
19
|
}
|
|
18
20
|
var defaultPhoneValidator = (phoneNumber2) => /^1\d{10}$/.test(phoneNumber2);
|
|
19
21
|
var defaultTempEmailFromPhone = (phoneNumber2) => `${phoneNumber2.replace(/\D/g, "")}@phone.sa2kit.local`;
|
|
22
|
+
function createId() {
|
|
23
|
+
return globalThis.crypto?.randomUUID?.() ?? `acc_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
24
|
+
}
|
|
25
|
+
async function upsertCredentialPassword(db, userId, plainPassword) {
|
|
26
|
+
const database = db;
|
|
27
|
+
const now = /* @__PURE__ */ new Date();
|
|
28
|
+
const passwordHash = await hashPassword(plainPassword);
|
|
29
|
+
const existing = await database.select({ id: account.id }).from(account).where(and(eq(account.userId, userId), eq(account.providerId, "credential"))).limit(1);
|
|
30
|
+
if (existing[0]) {
|
|
31
|
+
await database.update(account).set({ password: passwordHash, updatedAt: now }).where(eq(account.id, existing[0].id));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
await database.insert(account).values({
|
|
35
|
+
id: createId(),
|
|
36
|
+
accountId: userId,
|
|
37
|
+
providerId: "credential",
|
|
38
|
+
userId,
|
|
39
|
+
password: passwordHash,
|
|
40
|
+
createdAt: now,
|
|
41
|
+
updatedAt: now
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/common/auth/server/phone-signup-intent.ts
|
|
46
|
+
var pendingByPhone = /* @__PURE__ */ new Map();
|
|
47
|
+
var TTL_MS = 5 * 60 * 1e3;
|
|
48
|
+
function normalizePhone(phoneNumber2) {
|
|
49
|
+
return phoneNumber2.replace(/\D/g, "");
|
|
50
|
+
}
|
|
51
|
+
function stashPhoneSignupPassword(phoneNumber2, password) {
|
|
52
|
+
pendingByPhone.set(normalizePhone(phoneNumber2), {
|
|
53
|
+
password,
|
|
54
|
+
expiresAt: Date.now() + TTL_MS
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function consumePhoneSignupPassword(phoneNumber2) {
|
|
58
|
+
const key = normalizePhone(phoneNumber2);
|
|
59
|
+
const entry = pendingByPhone.get(key);
|
|
60
|
+
pendingByPhone.delete(key);
|
|
61
|
+
if (!entry) return void 0;
|
|
62
|
+
if (entry.expiresAt < Date.now()) return void 0;
|
|
63
|
+
return entry.password;
|
|
64
|
+
}
|
|
65
|
+
async function handlePhoneSignupIntentRequest(request) {
|
|
66
|
+
if (request.method !== "POST") {
|
|
67
|
+
return new Response("Method Not Allowed", { status: 405 });
|
|
68
|
+
}
|
|
69
|
+
let body;
|
|
70
|
+
try {
|
|
71
|
+
body = await request.json();
|
|
72
|
+
} catch {
|
|
73
|
+
return Response.json({ error: "invalid_json" }, { status: 400 });
|
|
74
|
+
}
|
|
75
|
+
const phoneNumber2 = String(body.phoneNumber ?? "").trim();
|
|
76
|
+
const password = String(body.password ?? "");
|
|
77
|
+
if (!defaultPhoneValidator(phoneNumber2)) {
|
|
78
|
+
return Response.json({ error: "invalid_phone" }, { status: 400 });
|
|
79
|
+
}
|
|
80
|
+
if (password.length < 6) {
|
|
81
|
+
return Response.json({ error: "invalid_password" }, { status: 400 });
|
|
82
|
+
}
|
|
83
|
+
stashPhoneSignupPassword(phoneNumber2, password);
|
|
84
|
+
return Response.json({ ok: true });
|
|
85
|
+
}
|
|
20
86
|
|
|
21
87
|
// src/common/auth/server/create-auth.ts
|
|
22
88
|
function createSa2kitAuth(config) {
|
|
@@ -64,7 +130,13 @@ function createSa2kitAuth(config) {
|
|
|
64
130
|
async sendOTP({ phoneNumber: phone, code }) {
|
|
65
131
|
devLog?.("sms", phone, code);
|
|
66
132
|
if (config.sms?.sendOTP) {
|
|
67
|
-
|
|
133
|
+
await config.sms.sendOTP(phone, code);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
async callbackOnVerification({ phoneNumber: phone, user: user2 }) {
|
|
137
|
+
const pendingPassword = consumePhoneSignupPassword(phone);
|
|
138
|
+
if (pendingPassword && user2?.id) {
|
|
139
|
+
await upsertCredentialPassword(config.db, String(user2.id), pendingPassword);
|
|
68
140
|
}
|
|
69
141
|
},
|
|
70
142
|
signUpOnVerification: {
|
|
@@ -76,6 +148,533 @@ function createSa2kitAuth(config) {
|
|
|
76
148
|
});
|
|
77
149
|
return auth;
|
|
78
150
|
}
|
|
151
|
+
|
|
152
|
+
// src/common/auth/server/env/auth-env-catalog.ts
|
|
153
|
+
var AUTH_FEATURES = [
|
|
154
|
+
{
|
|
155
|
+
id: "core",
|
|
156
|
+
name: "\u6838\u5FC3\u8BA4\u8BC1",
|
|
157
|
+
description: "Better Auth \u670D\u52A1\u7AEF\u57FA\u7840\u80FD\u529B\uFF08\u4F1A\u8BDD\u3001Cookie\u3001API \u8DEF\u7531\uFF09",
|
|
158
|
+
envKeys: ["BETTER_AUTH_SECRET", "BETTER_AUTH_URL"]
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "client",
|
|
162
|
+
name: "Web \u5BA2\u6237\u7AEF",
|
|
163
|
+
description: "\u6D4F\u89C8\u5668\u7AEF AuthProvider / \u767B\u5F55\u5F39\u7A97\u8BF7\u6C42\u6B63\u786E origin",
|
|
164
|
+
envKeys: ["NEXT_PUBLIC_APP_URL"]
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: "trusted_origins",
|
|
168
|
+
name: "\u8DE8\u57DF\u4FE1\u4EFB\u6E90",
|
|
169
|
+
description: "\u591A\u57DF\u540D / \u9884\u89C8\u73AF\u5883 CORS \u4E0E Cookie",
|
|
170
|
+
envKeys: ["BETTER_AUTH_TRUSTED_ORIGINS"]
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: "sms_phone_otp",
|
|
174
|
+
name: "\u624B\u673A\u77ED\u4FE1 OTP",
|
|
175
|
+
description: "\u624B\u673A\u53F7\u6CE8\u518C / \u767B\u5F55 / \u627E\u56DE\u5BC6\u7801\u7684\u77ED\u4FE1\u9A8C\u8BC1\u7801",
|
|
176
|
+
envKeys: [
|
|
177
|
+
"SA2KIT_SMS_PROVIDER",
|
|
178
|
+
"ALIYUN_SMS_ACCESS_KEY_ID",
|
|
179
|
+
"ALIYUN_SMS_ACCESS_KEY_SECRET",
|
|
180
|
+
"ALIYUN_SMS_SIGN_NAME",
|
|
181
|
+
"ALIYUN_SMS_TEMPLATE_CODE",
|
|
182
|
+
"ALIYUN_SMS_COUNTRY_CODE",
|
|
183
|
+
"ALIYUN_SMS_CODE_VALID_MINUTES",
|
|
184
|
+
"ALIYUN_SMS_ENDPOINT"
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id: "email_otp",
|
|
189
|
+
name: "\u90AE\u7BB1 OTP",
|
|
190
|
+
description: "\u90AE\u7BB1\u9A8C\u8BC1\u7801\uFF08\u9700\u5728 createSa2kitAuth \u4F20\u5165 email.sendVerificationOTP\uFF09",
|
|
191
|
+
envKeys: ["SA2KIT_EMAIL_PROVIDER"]
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: "database",
|
|
195
|
+
name: "\u6570\u636E\u5E93",
|
|
196
|
+
description: "Drizzle + PostgreSQL \u6301\u4E45\u5316 user/session\uFF08\u7531\u5BBF\u4E3B\u9879\u76EE\u914D\u7F6E\uFF09",
|
|
197
|
+
envKeys: ["DATABASE_URL"]
|
|
198
|
+
}
|
|
199
|
+
];
|
|
200
|
+
var AUTH_ENV_CATALOG = [
|
|
201
|
+
{
|
|
202
|
+
key: "BETTER_AUTH_SECRET",
|
|
203
|
+
featureId: "core",
|
|
204
|
+
required: true,
|
|
205
|
+
placement: ["env_file", "github_secret", "runtime_env"],
|
|
206
|
+
description: "Better Auth \u7B7E\u540D\u5BC6\u94A5\uFF0C\u81F3\u5C11 32 \u5B57\u7B26\u3002\u53EF\u517C\u5BB9\u8BFB\u53D6 NEXTAUTH_SECRET\u3002",
|
|
207
|
+
example: "openssl rand -base64 32",
|
|
208
|
+
secret: true
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
key: "BETTER_AUTH_URL",
|
|
212
|
+
featureId: "core",
|
|
213
|
+
required: true,
|
|
214
|
+
placement: ["env_file", "github_secret", "runtime_env"],
|
|
215
|
+
description: "\u5BF9\u5916\u53EF\u8BBF\u95EE\u7684\u7AD9\u70B9 URL\uFF08\u542B\u534F\u8BAE\u4E0E\u7AEF\u53E3\uFF09\u3002\u53EF\u56DE\u9000 NEXT_PUBLIC_APP_URL / NEXTAUTH_URL\u3002",
|
|
216
|
+
example: "https://example.com"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
key: "NEXT_PUBLIC_APP_URL",
|
|
220
|
+
featureId: "client",
|
|
221
|
+
required: true,
|
|
222
|
+
placement: ["env_file", "github_secret", "runtime_env"],
|
|
223
|
+
description: "\u6D4F\u89C8\u5668\u7AEF auth client \u7684 baseURL\u3002",
|
|
224
|
+
example: "https://example.com"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
key: "BETTER_AUTH_TRUSTED_ORIGINS",
|
|
228
|
+
featureId: "trusted_origins",
|
|
229
|
+
required: false,
|
|
230
|
+
placement: ["env_file", "github_secret", "runtime_env"],
|
|
231
|
+
description: "\u9017\u53F7\u5206\u9694\u7684\u53EF\u4FE1 origin \u5217\u8868\uFF0C\u7528\u4E8E\u9884\u89C8\u57DF / \u591A\u57DF\u540D\u90E8\u7F72\u3002",
|
|
232
|
+
example: "https://example.com,https://www.example.com"
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
key: "SA2KIT_SMS_PROVIDER",
|
|
236
|
+
featureId: "sms_phone_otp",
|
|
237
|
+
required: false,
|
|
238
|
+
placement: ["env_file", "github_secret", "runtime_env"],
|
|
239
|
+
description: "\u77ED\u4FE1 provider\uFF1Aconsole\uFF08\u5F00\u53D1\u65E5\u5FD7\uFF09| aliyun-pnvs\uFF08\u751F\u4EA7\uFF09| none\u3002\u5F00\u53D1\u9ED8\u8BA4 console\uFF0C\u751F\u4EA7\u672A\u914D\u7F6E\u5219\u65E0\u6CD5\u53D1\u77ED\u4FE1\u3002",
|
|
240
|
+
example: "aliyun-pnvs"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
key: "ALIYUN_SMS_ACCESS_KEY_ID",
|
|
244
|
+
featureId: "sms_phone_otp",
|
|
245
|
+
required: false,
|
|
246
|
+
placement: ["env_file", "github_secret", "runtime_env"],
|
|
247
|
+
description: "\u963F\u91CC\u4E91 RAM AccessKey ID\uFF08\u77ED\u4FE1\u8BA4\u8BC1 SendSmsVerifyCode\uFF09\u3002",
|
|
248
|
+
secret: true
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
key: "ALIYUN_SMS_ACCESS_KEY_SECRET",
|
|
252
|
+
featureId: "sms_phone_otp",
|
|
253
|
+
required: false,
|
|
254
|
+
placement: ["env_file", "github_secret", "runtime_env"],
|
|
255
|
+
description: "\u963F\u91CC\u4E91 RAM AccessKey Secret\u3002",
|
|
256
|
+
secret: true
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
key: "ALIYUN_SMS_SIGN_NAME",
|
|
260
|
+
featureId: "sms_phone_otp",
|
|
261
|
+
required: false,
|
|
262
|
+
placement: ["env_file", "github_secret", "runtime_env"],
|
|
263
|
+
description: "\u77ED\u4FE1\u8BA4\u8BC1\u63A7\u5236\u53F0\u8D60\u9001/\u7533\u8BF7\u7684\u7B7E\u540D\u540D\u79F0\u3002",
|
|
264
|
+
example: "\u901F\u901A\u4E92\u8054\u9A8C\u8BC1\u7801"
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
key: "ALIYUN_SMS_TEMPLATE_CODE",
|
|
268
|
+
featureId: "sms_phone_otp",
|
|
269
|
+
required: false,
|
|
270
|
+
placement: ["env_file", "github_secret", "runtime_env"],
|
|
271
|
+
description: "\u77ED\u4FE1\u8BA4\u8BC1\u63A7\u5236\u53F0\u8D60\u9001/\u7533\u8BF7\u7684\u6A21\u677F CODE\u3002",
|
|
272
|
+
example: "100001"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
key: "ALIYUN_SMS_COUNTRY_CODE",
|
|
276
|
+
featureId: "sms_phone_otp",
|
|
277
|
+
required: false,
|
|
278
|
+
placement: ["env_file", "runtime_env"],
|
|
279
|
+
description: "\u56FD\u5BB6\u7801\uFF0C\u9ED8\u8BA4 86\uFF08\u4E2D\u56FD\u5927\u9646\uFF09\u3002",
|
|
280
|
+
example: "86"
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
key: "ALIYUN_SMS_CODE_VALID_MINUTES",
|
|
284
|
+
featureId: "sms_phone_otp",
|
|
285
|
+
required: false,
|
|
286
|
+
placement: ["env_file", "runtime_env"],
|
|
287
|
+
description: "\u77ED\u4FE1\u6A21\u677F\u4E2D\u5C55\u793A\u7684\u6709\u6548\u5206\u949F\u6570\uFF0C\u9ED8\u8BA4 5\uFF08\u4E0E Better Auth OTP \u6709\u6548\u671F\u72EC\u7ACB\uFF09\u3002",
|
|
288
|
+
example: "5"
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
key: "ALIYUN_SMS_ENDPOINT",
|
|
292
|
+
featureId: "sms_phone_otp",
|
|
293
|
+
required: false,
|
|
294
|
+
placement: ["env_file", "runtime_env"],
|
|
295
|
+
description: "\u53EF\u9009\uFF0C\u9ED8\u8BA4 https://dypnsapi.aliyuncs.com"
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
key: "SA2KIT_EMAIL_PROVIDER",
|
|
299
|
+
featureId: "email_otp",
|
|
300
|
+
required: false,
|
|
301
|
+
placement: ["env_file", "github_secret", "runtime_env"],
|
|
302
|
+
description: "\u90AE\u7BB1 OTP \u7531\u5BBF\u4E3B\u5B9E\u73B0 config.email.sendVerificationOTP\uFF1B\u6B64\u53D8\u91CF\u4EC5\u7528\u4E8E env \u68C0\u67E5\u63D0\u793A\u3002",
|
|
303
|
+
example: "resend | smtp | console"
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
key: "DATABASE_URL",
|
|
307
|
+
featureId: "database",
|
|
308
|
+
required: true,
|
|
309
|
+
placement: ["env_file", "github_secret", "runtime_env", "database"],
|
|
310
|
+
description: "PostgreSQL \u8FDE\u63A5\u4E32\uFF08\u5BBF\u4E3B\u9879\u76EE Drizzle \u4F7F\u7528\uFF0C\u975E sa2kit \u76F4\u63A5\u8BFB\u53D6\uFF09\u3002",
|
|
311
|
+
secret: true
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
key: "SA2KIT_AUTH_LOG_OTP",
|
|
315
|
+
featureId: "sms_phone_otp",
|
|
316
|
+
required: false,
|
|
317
|
+
placement: ["env_file", "runtime_env"],
|
|
318
|
+
description: "\u8BBE\u4E3A 1 \u65F6\u5728\u975E\u751F\u4EA7\u73AF\u5883\u5C06 OTP \u6253\u5370\u5230\u670D\u52A1\u7AEF\u65E5\u5FD7\uFF1B\u751F\u4EA7\u73AF\u5883\u5FFD\u7565\u3002\u8BBE\u4E3A 0 \u53EF\u5173\u95ED\u5F00\u53D1\u65E5\u5FD7\u3002",
|
|
319
|
+
example: "1"
|
|
320
|
+
}
|
|
321
|
+
];
|
|
322
|
+
var AUTH_ENV_ALIASES = {
|
|
323
|
+
BETTER_AUTH_SECRET: ["NEXTAUTH_SECRET"],
|
|
324
|
+
BETTER_AUTH_URL: ["NEXT_PUBLIC_APP_URL", "NEXTAUTH_URL"]
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// src/common/auth/server/sms/providers/aliyun-pnvs.ts
|
|
328
|
+
async function loadPopCore() {
|
|
329
|
+
try {
|
|
330
|
+
const mod = await import('@alicloud/pop-core');
|
|
331
|
+
return mod.default ?? mod;
|
|
332
|
+
} catch {
|
|
333
|
+
throw new Error(
|
|
334
|
+
"\u542F\u7528 SA2KIT_SMS_PROVIDER=aliyun-pnvs \u9700\u8981\u5B89\u88C5 @alicloud/pop-core\uFF1Apnpm add @alicloud/pop-core"
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function createAliyunPnvsSmsProvider(config) {
|
|
339
|
+
return {
|
|
340
|
+
async sendOTP(phoneNumber2, code) {
|
|
341
|
+
const Core = await loadPopCore();
|
|
342
|
+
const client = new Core({
|
|
343
|
+
accessKeyId: config.accessKeyId,
|
|
344
|
+
accessKeySecret: config.accessKeySecret,
|
|
345
|
+
endpoint: config.endpoint ?? "https://dypnsapi.aliyuncs.com",
|
|
346
|
+
apiVersion: "2017-05-25"
|
|
347
|
+
});
|
|
348
|
+
const minutes = String(config.codeValidMinutes ?? 5);
|
|
349
|
+
const result = await client.request(
|
|
350
|
+
"SendSmsVerifyCode",
|
|
351
|
+
{
|
|
352
|
+
PhoneNumber: phoneNumber2,
|
|
353
|
+
CountryCode: config.countryCode ?? "86",
|
|
354
|
+
SignName: config.signName,
|
|
355
|
+
TemplateCode: config.templateCode,
|
|
356
|
+
TemplateParam: JSON.stringify({ code, min: minutes })
|
|
357
|
+
},
|
|
358
|
+
{ method: "POST" }
|
|
359
|
+
);
|
|
360
|
+
if (result.Code !== "OK" && result.Success !== true) {
|
|
361
|
+
throw new Error(
|
|
362
|
+
`\u963F\u91CC\u4E91\u77ED\u4FE1\u8BA4\u8BC1\u53D1\u9001\u5931\u8D25: ${result.Message ?? result.Code ?? "unknown error"}`
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// src/common/auth/server/sms/providers/console.ts
|
|
370
|
+
function createConsoleSmsProvider() {
|
|
371
|
+
return {
|
|
372
|
+
async sendOTP(phoneNumber2, code) {
|
|
373
|
+
console.info(`[sa2kit/auth][sms][console] ${phoneNumber2} => ${code}`);
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/common/auth/server/sms/create-sms-provider-from-env.ts
|
|
379
|
+
function readEnv(key) {
|
|
380
|
+
const value = process.env[key];
|
|
381
|
+
return value && value.trim().length > 0 ? value.trim() : void 0;
|
|
382
|
+
}
|
|
383
|
+
function resolveSmsProviderId(explicit) {
|
|
384
|
+
if (explicit) return explicit;
|
|
385
|
+
const fromEnv = readEnv("SA2KIT_SMS_PROVIDER");
|
|
386
|
+
if (fromEnv) return fromEnv;
|
|
387
|
+
if (process.env.NODE_ENV !== "production") return "console";
|
|
388
|
+
return void 0;
|
|
389
|
+
}
|
|
390
|
+
function createSmsProviderFromEnv(options) {
|
|
391
|
+
const providerId = resolveSmsProviderId(options?.providerId);
|
|
392
|
+
if (!providerId || providerId === "none") return void 0;
|
|
393
|
+
if (providerId === "console") {
|
|
394
|
+
return createConsoleSmsProvider();
|
|
395
|
+
}
|
|
396
|
+
if (providerId === "aliyun-pnvs") {
|
|
397
|
+
const accessKeyId = readEnv("ALIYUN_SMS_ACCESS_KEY_ID");
|
|
398
|
+
const accessKeySecret = readEnv("ALIYUN_SMS_ACCESS_KEY_SECRET");
|
|
399
|
+
const signName = readEnv("ALIYUN_SMS_SIGN_NAME");
|
|
400
|
+
const templateCode = readEnv("ALIYUN_SMS_TEMPLATE_CODE");
|
|
401
|
+
if (!accessKeyId || !accessKeySecret || !signName || !templateCode) {
|
|
402
|
+
throw new Error(
|
|
403
|
+
"SA2KIT_SMS_PROVIDER=aliyun-pnvs \u9700\u8981 ALIYUN_SMS_ACCESS_KEY_ID\u3001ALIYUN_SMS_ACCESS_KEY_SECRET\u3001ALIYUN_SMS_SIGN_NAME\u3001ALIYUN_SMS_TEMPLATE_CODE"
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
const codeValidMinutes = Number.parseInt(readEnv("ALIYUN_SMS_CODE_VALID_MINUTES") ?? "5", 10);
|
|
407
|
+
return createAliyunPnvsSmsProvider({
|
|
408
|
+
accessKeyId,
|
|
409
|
+
accessKeySecret,
|
|
410
|
+
signName,
|
|
411
|
+
templateCode,
|
|
412
|
+
countryCode: readEnv("ALIYUN_SMS_COUNTRY_CODE") ?? "86",
|
|
413
|
+
codeValidMinutes: Number.isFinite(codeValidMinutes) ? codeValidMinutes : 5,
|
|
414
|
+
endpoint: readEnv("ALIYUN_SMS_ENDPOINT")
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
throw new Error(`\u672A\u77E5\u7684 SA2KIT_SMS_PROVIDER: ${providerId}`);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// src/common/auth/server/env/resolve-auth-env.ts
|
|
421
|
+
function readEnv2(key) {
|
|
422
|
+
const value = process.env[key];
|
|
423
|
+
return value && value.trim().length > 0 ? value.trim() : void 0;
|
|
424
|
+
}
|
|
425
|
+
function readEnvWithAliases(key) {
|
|
426
|
+
const direct = readEnv2(key);
|
|
427
|
+
if (direct) return direct;
|
|
428
|
+
for (const alias of AUTH_ENV_ALIASES[key] ?? []) {
|
|
429
|
+
const value = readEnv2(alias);
|
|
430
|
+
if (value) return value;
|
|
431
|
+
}
|
|
432
|
+
return void 0;
|
|
433
|
+
}
|
|
434
|
+
function isNextBuildPhaseWithoutRuntimeEnv() {
|
|
435
|
+
return process.env.NEXT_PHASE === "phase-production-build" || process.env.NEXT_PHASE === "phase-export";
|
|
436
|
+
}
|
|
437
|
+
function parseTrustedOrigins(baseURL) {
|
|
438
|
+
const fromEnv = readEnv2("BETTER_AUTH_TRUSTED_ORIGINS");
|
|
439
|
+
const defaults = [baseURL, "http://localhost:3000", "http://127.0.0.1:3000"];
|
|
440
|
+
const extra = fromEnv ? fromEnv.split(",").map((item) => item.trim()).filter(Boolean) : [];
|
|
441
|
+
return [...defaults, ...extra].filter((origin, index, list) => list.indexOf(origin) === index);
|
|
442
|
+
}
|
|
443
|
+
function resolveAuthEnv(input) {
|
|
444
|
+
const baseURL = input.baseURL ?? readEnvWithAliases("BETTER_AUTH_URL") ?? readEnv2("NEXT_PUBLIC_APP_URL") ?? "http://localhost:3000";
|
|
445
|
+
const secret = input.secret ?? readEnvWithAliases("BETTER_AUTH_SECRET") ?? (process.env.NODE_ENV !== "production" || isNextBuildPhaseWithoutRuntimeEnv() ? "dev-better-auth-secret-min-32-chars!!" : void 0);
|
|
446
|
+
if (!secret || secret.length < 32) {
|
|
447
|
+
throw new Error("BETTER_AUTH_SECRET \u81F3\u5C11 32 \u5B57\u7B26\uFF08\u6216\u5F00\u53D1\u73AF\u5883\u4F7F\u7528\u9ED8\u8BA4 dev secret\uFF09");
|
|
448
|
+
}
|
|
449
|
+
const smsProvider = createSmsProviderFromEnv();
|
|
450
|
+
const sms = input.sms ?? (smsProvider ? { sendOTP: smsProvider.sendOTP.bind(smsProvider) } : void 0);
|
|
451
|
+
const config = {
|
|
452
|
+
db: input.db,
|
|
453
|
+
baseURL,
|
|
454
|
+
secret,
|
|
455
|
+
trustedOrigins: input.trustedOrigins ?? parseTrustedOrigins(baseURL),
|
|
456
|
+
basePath: input.basePath,
|
|
457
|
+
sms,
|
|
458
|
+
email: input.email,
|
|
459
|
+
phoneNumberValidator: input.phoneNumberValidator,
|
|
460
|
+
logOtpInDev: input.logOtpInDev ?? (process.env.SA2KIT_AUTH_LOG_OTP === "1" || process.env.NODE_ENV !== "production")
|
|
461
|
+
};
|
|
462
|
+
const envSnapshot = {
|
|
463
|
+
BETTER_AUTH_SECRET: readEnvWithAliases("BETTER_AUTH_SECRET") ? "[set]" : void 0,
|
|
464
|
+
BETTER_AUTH_URL: readEnvWithAliases("BETTER_AUTH_URL"),
|
|
465
|
+
NEXT_PUBLIC_APP_URL: readEnv2("NEXT_PUBLIC_APP_URL"),
|
|
466
|
+
BETTER_AUTH_TRUSTED_ORIGINS: readEnv2("BETTER_AUTH_TRUSTED_ORIGINS"),
|
|
467
|
+
SA2KIT_SMS_PROVIDER: String(resolveSmsProviderId() ?? ""),
|
|
468
|
+
ALIYUN_SMS_ACCESS_KEY_ID: readEnv2("ALIYUN_SMS_ACCESS_KEY_ID") ? "[set]" : void 0,
|
|
469
|
+
ALIYUN_SMS_ACCESS_KEY_SECRET: readEnv2("ALIYUN_SMS_ACCESS_KEY_SECRET") ? "[set]" : void 0,
|
|
470
|
+
ALIYUN_SMS_SIGN_NAME: readEnv2("ALIYUN_SMS_SIGN_NAME"),
|
|
471
|
+
ALIYUN_SMS_TEMPLATE_CODE: readEnv2("ALIYUN_SMS_TEMPLATE_CODE"),
|
|
472
|
+
SA2KIT_EMAIL_PROVIDER: readEnv2("SA2KIT_EMAIL_PROVIDER"),
|
|
473
|
+
DATABASE_URL: readEnv2("DATABASE_URL") ? "[set]" : void 0,
|
|
474
|
+
NODE_ENV: process.env.NODE_ENV
|
|
475
|
+
};
|
|
476
|
+
return { config, envSnapshot };
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// src/common/auth/server/env/check-auth-env.ts
|
|
480
|
+
function isSet(snapshot, key) {
|
|
481
|
+
const value = snapshot[key];
|
|
482
|
+
return value !== void 0 && value !== "[unset]" && value.length > 0;
|
|
483
|
+
}
|
|
484
|
+
function featureById(id) {
|
|
485
|
+
return AUTH_FEATURES.find((f) => f.id === id);
|
|
486
|
+
}
|
|
487
|
+
function varsForFeature(featureId) {
|
|
488
|
+
return AUTH_ENV_CATALOG.filter((item) => item.featureId === featureId);
|
|
489
|
+
}
|
|
490
|
+
function checkAuthEnv(envSnapshot) {
|
|
491
|
+
const issues = [];
|
|
492
|
+
const enabledFeatures = [];
|
|
493
|
+
const disabledFeatures = [];
|
|
494
|
+
const checkRequiredFeature = (featureId, productionOnly = false) => {
|
|
495
|
+
const feature = featureById(featureId);
|
|
496
|
+
const requiredVars = varsForFeature(featureId).filter((item) => item.required);
|
|
497
|
+
const missing = requiredVars.filter((item) => !isSet(envSnapshot, item.key)).map((item) => item.key);
|
|
498
|
+
if (missing.length === 0) {
|
|
499
|
+
enabledFeatures.push(featureId);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
if (productionOnly && envSnapshot.NODE_ENV !== "production") {
|
|
503
|
+
disabledFeatures.push(featureId);
|
|
504
|
+
issues.push({
|
|
505
|
+
level: "info",
|
|
506
|
+
featureId,
|
|
507
|
+
featureName: feature.name,
|
|
508
|
+
message: `\u5F00\u53D1\u73AF\u5883\u53EF\u6682\u7F3A\uFF1A${missing.join(", ")}`,
|
|
509
|
+
missingKeys: missing
|
|
510
|
+
});
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
disabledFeatures.push(featureId);
|
|
514
|
+
issues.push({
|
|
515
|
+
level: "error",
|
|
516
|
+
featureId,
|
|
517
|
+
featureName: feature.name,
|
|
518
|
+
message: `\u7F3A\u5C11\u5FC5\u9700\u73AF\u5883\u53D8\u91CF\uFF1A${missing.join(", ")}`,
|
|
519
|
+
missingKeys: missing
|
|
520
|
+
});
|
|
521
|
+
};
|
|
522
|
+
checkRequiredFeature("core");
|
|
523
|
+
checkRequiredFeature("client", true);
|
|
524
|
+
checkRequiredFeature("database", true);
|
|
525
|
+
const smsFeature = featureById("sms_phone_otp");
|
|
526
|
+
const smsProvider = envSnapshot.SA2KIT_SMS_PROVIDER;
|
|
527
|
+
if (smsProvider === "aliyun-pnvs") {
|
|
528
|
+
const aliyunKeys = [
|
|
529
|
+
"ALIYUN_SMS_ACCESS_KEY_ID",
|
|
530
|
+
"ALIYUN_SMS_ACCESS_KEY_SECRET",
|
|
531
|
+
"ALIYUN_SMS_SIGN_NAME",
|
|
532
|
+
"ALIYUN_SMS_TEMPLATE_CODE"
|
|
533
|
+
];
|
|
534
|
+
const missing = aliyunKeys.filter((key) => !isSet(envSnapshot, key));
|
|
535
|
+
if (missing.length === 0) {
|
|
536
|
+
enabledFeatures.push("sms_phone_otp");
|
|
537
|
+
} else {
|
|
538
|
+
disabledFeatures.push("sms_phone_otp");
|
|
539
|
+
issues.push({
|
|
540
|
+
level: "error",
|
|
541
|
+
featureId: "sms_phone_otp",
|
|
542
|
+
featureName: smsFeature.name,
|
|
543
|
+
message: `SA2KIT_SMS_PROVIDER=aliyun-pnvs \u4F46\u7F3A\u5C11\uFF1A${missing.join(", ")}`,
|
|
544
|
+
missingKeys: missing,
|
|
545
|
+
hints: ["\u5728 GitHub Secrets \u6216 .env.production \u4E2D\u914D\u7F6E ALIYUN_SMS_*"]
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
} else if (smsProvider === "console") {
|
|
549
|
+
enabledFeatures.push("sms_phone_otp");
|
|
550
|
+
if (envSnapshot.NODE_ENV === "production") {
|
|
551
|
+
issues.push({
|
|
552
|
+
level: "warning",
|
|
553
|
+
featureId: "sms_phone_otp",
|
|
554
|
+
featureName: smsFeature.name,
|
|
555
|
+
message: "\u751F\u4EA7\u73AF\u5883\u4ECD\u5728\u4F7F\u7528 SA2KIT_SMS_PROVIDER=console\uFF0C\u9A8C\u8BC1\u7801\u4EC5\u8F93\u51FA\u5230\u65E5\u5FD7\uFF0C\u7528\u6237\u6536\u4E0D\u5230\u77ED\u4FE1\u3002",
|
|
556
|
+
hints: ["\u751F\u4EA7\u8BF7\u6539\u4E3A aliyun-pnvs \u5E76\u914D\u7F6E ALIYUN_SMS_*"]
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
} else {
|
|
560
|
+
disabledFeatures.push("sms_phone_otp");
|
|
561
|
+
issues.push({
|
|
562
|
+
level: envSnapshot.NODE_ENV === "production" ? "warning" : "info",
|
|
563
|
+
featureId: "sms_phone_otp",
|
|
564
|
+
featureName: smsFeature.name,
|
|
565
|
+
message: envSnapshot.NODE_ENV === "production" ? "\u672A\u914D\u7F6E\u77ED\u4FE1 provider\uFF0C\u624B\u673A\u53F7 OTP \u65E0\u6CD5\u9001\u8FBE\uFF08\u7528\u6237\u6536\u4E0D\u5230\u9A8C\u8BC1\u7801\uFF09\u3002" : "\u672A\u914D\u7F6E\u77ED\u4FE1 provider\uFF1B\u5F00\u53D1\u73AF\u5883\u53EF\u8BBE\u7F6E SA2KIT_SMS_PROVIDER=console \u5E76\u5728\u670D\u52A1\u7AEF\u65E5\u5FD7\u67E5\u770B OTP\u3002",
|
|
566
|
+
hints: [
|
|
567
|
+
"\u5F00\u53D1\uFF1ASA2KIT_SMS_PROVIDER=console",
|
|
568
|
+
"\u751F\u4EA7\uFF1ASA2KIT_SMS_PROVIDER=aliyun-pnvs + ALIYUN_SMS_*"
|
|
569
|
+
]
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
if (isSet(envSnapshot, "SA2KIT_EMAIL_PROVIDER")) {
|
|
573
|
+
enabledFeatures.push("email_otp");
|
|
574
|
+
} else {
|
|
575
|
+
disabledFeatures.push("email_otp");
|
|
576
|
+
issues.push({
|
|
577
|
+
level: "info",
|
|
578
|
+
featureId: "email_otp",
|
|
579
|
+
featureName: featureById("email_otp").name,
|
|
580
|
+
message: "\u672A\u58F0\u660E SA2KIT_EMAIL_PROVIDER\uFF1B\u82E5\u9700\u90AE\u7BB1\u9A8C\u8BC1\u7801\u8BF7\u5728 createSa2kitAuth \u914D\u7F6E email.sendVerificationOTP\u3002"
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
if (isSet(envSnapshot, "BETTER_AUTH_TRUSTED_ORIGINS")) {
|
|
584
|
+
enabledFeatures.push("trusted_origins");
|
|
585
|
+
} else {
|
|
586
|
+
disabledFeatures.push("trusted_origins");
|
|
587
|
+
issues.push({
|
|
588
|
+
level: "info",
|
|
589
|
+
featureId: "trusted_origins",
|
|
590
|
+
featureName: featureById("trusted_origins").name,
|
|
591
|
+
message: "\u672A\u8BBE\u7F6E BETTER_AUTH_TRUSTED_ORIGINS\uFF0C\u4EC5\u4F7F\u7528 baseURL + localhost \u9ED8\u8BA4\u503C\u3002"
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
const ok = !issues.some((issue) => issue.level === "error");
|
|
595
|
+
return { ok, issues, enabledFeatures, disabledFeatures };
|
|
596
|
+
}
|
|
597
|
+
var loggedOnce = false;
|
|
598
|
+
function logAuthEnvReport(report, options) {
|
|
599
|
+
if (loggedOnce && !options?.force) return;
|
|
600
|
+
loggedOnce = true;
|
|
601
|
+
const lines = ["[sa2kit/auth] \u73AF\u5883\u914D\u7F6E\u68C0\u67E5"];
|
|
602
|
+
for (const issue of report.issues) {
|
|
603
|
+
const prefix = issue.level === "error" ? "\u2717" : issue.level === "warning" ? "\u26A0" : "\u25CB";
|
|
604
|
+
lines.push(`${prefix} ${issue.featureName}: ${issue.message}`);
|
|
605
|
+
if (issue.hints?.length) {
|
|
606
|
+
for (const hint of issue.hints) {
|
|
607
|
+
lines.push(` \u2192 ${hint}`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
if (report.issues.length === 0) {
|
|
612
|
+
lines.push("\u2713 \u6240\u6709\u5DF2\u542F\u7528\u80FD\u529B\u7684\u73AF\u5883\u53D8\u91CF\u5747\u5DF2\u5C31\u7EEA");
|
|
613
|
+
}
|
|
614
|
+
lines.push(` \u5DF2\u542F\u7528: ${report.enabledFeatures.join(", ") || "\u65E0"}`);
|
|
615
|
+
console.info(lines.join("\n"));
|
|
616
|
+
}
|
|
617
|
+
function formatAuthEnvSetupMarkdown(report) {
|
|
618
|
+
const lines = [
|
|
619
|
+
"# sa2kit Auth \u73AF\u5883\u53D8\u91CF",
|
|
620
|
+
"",
|
|
621
|
+
"\u5B8C\u6574\u8BF4\u660E\u89C1 sa2kit \u6587\u6863\uFF1A`docs/auth-env.md`",
|
|
622
|
+
"",
|
|
623
|
+
"## \u529F\u80FD \u2194 \u73AF\u5883\u53D8\u91CF",
|
|
624
|
+
""
|
|
625
|
+
];
|
|
626
|
+
for (const feature of AUTH_FEATURES) {
|
|
627
|
+
lines.push(`### ${feature.name} (\`${feature.id}\`)`);
|
|
628
|
+
lines.push("");
|
|
629
|
+
lines.push(feature.description);
|
|
630
|
+
lines.push("");
|
|
631
|
+
const vars = varsForFeature(feature.id);
|
|
632
|
+
lines.push("| \u53D8\u91CF | \u5FC5\u9700 | \u5B58\u653E\u4F4D\u7F6E | \u8BF4\u660E |");
|
|
633
|
+
lines.push("|------|------|----------|------|");
|
|
634
|
+
for (const item of vars) {
|
|
635
|
+
lines.push(
|
|
636
|
+
`| \`${item.key}\` | ${item.required ? "\u662F" : "\u5426"} | ${item.placement.join(", ")} | ${item.description} |`
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
lines.push("");
|
|
640
|
+
}
|
|
641
|
+
if (report) {
|
|
642
|
+
lines.push("## \u5F53\u524D\u68C0\u67E5\u7ED3\u679C");
|
|
643
|
+
lines.push("");
|
|
644
|
+
for (const issue of report.issues) {
|
|
645
|
+
lines.push(`- **${issue.featureName}** (${issue.level}): ${issue.message}`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return lines.join("\n");
|
|
649
|
+
}
|
|
650
|
+
function checkAuthEnvFromProcessEnv(extraSnapshot) {
|
|
651
|
+
const snapshot = {
|
|
652
|
+
BETTER_AUTH_SECRET: process.env.BETTER_AUTH_SECRET || process.env.NEXTAUTH_SECRET ? "[set]" : void 0,
|
|
653
|
+
BETTER_AUTH_URL: process.env.BETTER_AUTH_URL ?? process.env.NEXT_PUBLIC_APP_URL,
|
|
654
|
+
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
|
|
655
|
+
BETTER_AUTH_TRUSTED_ORIGINS: process.env.BETTER_AUTH_TRUSTED_ORIGINS,
|
|
656
|
+
SA2KIT_SMS_PROVIDER: process.env.SA2KIT_SMS_PROVIDER ?? (process.env.NODE_ENV !== "production" ? "console" : void 0),
|
|
657
|
+
ALIYUN_SMS_ACCESS_KEY_ID: process.env.ALIYUN_SMS_ACCESS_KEY_ID ? "[set]" : void 0,
|
|
658
|
+
ALIYUN_SMS_ACCESS_KEY_SECRET: process.env.ALIYUN_SMS_ACCESS_KEY_SECRET ? "[set]" : void 0,
|
|
659
|
+
ALIYUN_SMS_SIGN_NAME: process.env.ALIYUN_SMS_SIGN_NAME,
|
|
660
|
+
ALIYUN_SMS_TEMPLATE_CODE: process.env.ALIYUN_SMS_TEMPLATE_CODE,
|
|
661
|
+
SA2KIT_EMAIL_PROVIDER: process.env.SA2KIT_EMAIL_PROVIDER,
|
|
662
|
+
DATABASE_URL: process.env.DATABASE_URL ? "[set]" : void 0,
|
|
663
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
664
|
+
...extraSnapshot
|
|
665
|
+
};
|
|
666
|
+
return checkAuthEnv(snapshot);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// src/common/auth/server/create-auth-from-env.ts
|
|
670
|
+
function createSa2kitAuthFromEnv(input, options) {
|
|
671
|
+
const resolved = resolveAuthEnv(input);
|
|
672
|
+
const report = checkAuthEnv(resolved.envSnapshot);
|
|
673
|
+
if (options?.logEnvReport !== false) {
|
|
674
|
+
logAuthEnvReport(report);
|
|
675
|
+
}
|
|
676
|
+
return createSa2kitAuth(resolved.config);
|
|
677
|
+
}
|
|
79
678
|
function mountNextAuthHandler(auth) {
|
|
80
679
|
return toNextJsHandler(auth);
|
|
81
680
|
}
|
|
@@ -123,6 +722,6 @@ function createSessionValidator(auth) {
|
|
|
123
722
|
};
|
|
124
723
|
}
|
|
125
724
|
|
|
126
|
-
export { createAuthRouteHandlers, createSa2kitAuth, createSessionValidator, defaultPhoneValidator, defaultTempEmailFromPhone, getSessionUser, getSessionUserNumeric, mountAuthHandler, mountNextAuthHandler };
|
|
725
|
+
export { AUTH_ENV_ALIASES, AUTH_ENV_CATALOG, AUTH_FEATURES, checkAuthEnv, checkAuthEnvFromProcessEnv, consumePhoneSignupPassword, createAliyunPnvsSmsProvider, createAuthRouteHandlers, createConsoleSmsProvider, createSa2kitAuth, createSa2kitAuthFromEnv, createSessionValidator, createSmsProviderFromEnv, defaultPhoneValidator, defaultTempEmailFromPhone, formatAuthEnvSetupMarkdown, getSessionUser, getSessionUserNumeric, handlePhoneSignupIntentRequest, logAuthEnvReport, mountAuthHandler, mountNextAuthHandler, resolveAuthEnv, resolveSmsProviderId, stashPhoneSignupPassword, upsertCredentialPassword };
|
|
127
726
|
//# sourceMappingURL=index.mjs.map
|
|
128
727
|
//# sourceMappingURL=index.mjs.map
|