@nextsparkjs/core 0.1.0-beta.195 → 0.1.0-beta.197
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/README.md +2 -0
- package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
- package/dist/components/auth/forms/SignupForm.js +55 -45
- package/dist/components/billing/InvoicesPagination.js +2 -2
- package/dist/components/dashboard/block-editor/config-panel.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/config-panel.js +3 -2
- package/dist/components/dashboard/block-editor/entity-fields-sidebar.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/entity-fields-sidebar.js +3 -2
- package/dist/components/dashboard/navigation/DynamicNavigation.js +2 -2
- package/dist/components/settings/layouts/SettingsSidebar.d.ts.map +1 -1
- package/dist/components/settings/layouts/SettingsSidebar.js +0 -1
- package/dist/components/teams/CreateTeamButton.js +2 -2
- package/dist/components/teams/CreateTeamDialog.js +1 -1
- package/dist/components/teams/InviteMemberButton.js +3 -3
- package/dist/components/teams/InviteMemberDialog.js +1 -1
- package/dist/components/teams/TeamPendingInvitations.js +2 -2
- package/dist/components/users/tables/UsersTable.d.ts.map +1 -1
- package/dist/components/users/tables/UsersTable.js +3 -4
- package/dist/hooks/useAuthReadiness.d.ts +1 -0
- package/dist/hooks/useAuthReadiness.d.ts.map +1 -1
- package/dist/hooks/useAuthReadiness.js +3 -1
- package/dist/lib/actions/entity.actions.js +2 -2
- package/dist/lib/actions/team.actions.js +2 -2
- package/dist/lib/actions/user.actions.js +4 -4
- package/dist/lib/api/api-error.d.ts +2 -0
- package/dist/lib/api/api-error.d.ts.map +1 -1
- package/dist/lib/api/api-error.js +3 -1
- package/dist/lib/api/auth/dual-auth.d.ts +2 -1
- package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
- package/dist/lib/api/auth/dual-auth.js +25 -6
- package/dist/lib/api/auth/scope-policy.d.ts +6 -3
- package/dist/lib/api/auth/scope-policy.d.ts.map +1 -1
- package/dist/lib/api/auth.d.ts.map +1 -1
- package/dist/lib/api/auth.js +7 -0
- package/dist/lib/api/cors-response.d.ts +3 -2
- package/dist/lib/api/cors-response.d.ts.map +1 -1
- package/dist/lib/api/cors-response.js +8 -6
- package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
- package/dist/lib/api/entity/generic-handler.js +66 -15
- package/dist/lib/api/entity-handler.js +2 -2
- package/dist/lib/api/helpers.d.ts.map +1 -1
- package/dist/lib/api/helpers.js +9 -24
- package/dist/lib/api/permission-middleware.d.ts.map +1 -1
- package/dist/lib/api/permission-middleware.js +0 -2
- package/dist/lib/api/post-categories.d.ts +29 -0
- package/dist/lib/api/post-categories.d.ts.map +1 -0
- package/dist/lib/api/post-categories.js +33 -0
- package/dist/lib/api/rate-limit.d.ts +8 -0
- package/dist/lib/api/rate-limit.d.ts.map +1 -1
- package/dist/lib/api/rate-limit.js +34 -3
- package/dist/lib/api/request-origin.d.ts.map +1 -1
- package/dist/lib/api/request-origin.js +1 -1
- package/dist/lib/auth/auth-methods.d.ts +5 -2
- package/dist/lib/auth/auth-methods.d.ts.map +1 -1
- package/dist/lib/auth/auth-methods.js +4 -2
- package/dist/lib/auth/authorization-session.d.ts +14 -0
- package/dist/lib/auth/authorization-session.d.ts.map +1 -0
- package/dist/lib/auth/authorization-session.js +10 -0
- package/dist/lib/auth/request-session.d.ts +5 -0
- package/dist/lib/auth/request-session.d.ts.map +1 -1
- package/dist/lib/auth/request-session.js +3 -0
- package/dist/lib/auth/runtime-readiness.d.ts +7 -4
- package/dist/lib/auth/runtime-readiness.d.ts.map +1 -1
- package/dist/lib/auth/runtime-readiness.js +9 -6
- package/dist/lib/auth/sample-accounts.d.ts +18 -0
- package/dist/lib/auth/sample-accounts.d.ts.map +1 -0
- package/dist/lib/auth/sample-accounts.js +25 -0
- package/dist/lib/auth/suspension.d.ts +22 -0
- package/dist/lib/auth/suspension.d.ts.map +1 -0
- package/dist/lib/auth/suspension.js +43 -0
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +20 -17
- package/dist/lib/cache/public-entity-cache.d.ts +21 -0
- package/dist/lib/cache/public-entity-cache.d.ts.map +1 -0
- package/dist/lib/cache/public-entity-cache.js +25 -0
- package/dist/lib/config/app.config.d.ts.map +1 -1
- package/dist/lib/config/app.config.js +6 -7
- package/dist/lib/config/types.d.ts +8 -6
- package/dist/lib/config/types.d.ts.map +1 -1
- package/dist/lib/db-pool.d.ts +31 -0
- package/dist/lib/db-pool.d.ts.map +1 -0
- package/dist/lib/db-pool.js +75 -0
- package/dist/lib/db.d.ts +1 -1
- package/dist/lib/db.d.ts.map +1 -1
- package/dist/lib/db.js +11 -14
- package/dist/lib/entities/api-generator.js +5 -5
- package/dist/lib/entities/entity-hooks.d.ts.map +1 -1
- package/dist/lib/entities/entity-hooks.js +4 -0
- package/dist/lib/mcp/errors.d.ts +2 -0
- package/dist/lib/mcp/errors.d.ts.map +1 -1
- package/dist/lib/mcp/errors.js +3 -1
- package/dist/lib/mcp/index.d.ts +1 -1
- package/dist/lib/mcp/index.d.ts.map +1 -1
- package/dist/lib/mcp/index.js +2 -1
- package/dist/lib/oauth/token-refresh.d.ts.map +1 -1
- package/dist/lib/oauth/token-refresh.js +3 -6
- package/dist/lib/permissions/check.d.ts +9 -0
- package/dist/lib/permissions/check.d.ts.map +1 -1
- package/dist/lib/permissions/check.js +11 -1
- package/dist/lib/services/generic-entity.service.d.ts.map +1 -1
- package/dist/lib/services/generic-entity.service.js +41 -7
- package/dist/lib/services/transactional-meta.service.d.ts.map +1 -1
- package/dist/lib/services/transactional-meta.service.js +7 -8
- package/dist/lib/teams/actions.d.ts.map +1 -1
- package/dist/lib/teams/actions.js +0 -1
- package/dist/lib/validation.d.ts +5 -0
- package/dist/lib/validation.d.ts.map +1 -1
- package/dist/lib/validation.js +2 -0
- package/dist/messages/de/billing.json +12 -1
- package/dist/messages/de/common.json +2 -0
- package/dist/messages/de/docs.json +1 -0
- package/dist/messages/de/features.json +16 -4
- package/dist/messages/de/home.json +5 -0
- package/dist/messages/de/index.d.ts +68 -25
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/de/index.js +2 -0
- package/dist/messages/de/index.ts +2 -0
- package/dist/messages/de/settings.json +1 -0
- package/dist/messages/de/teams.json +3 -18
- package/dist/messages/en/billing.json +1 -1
- package/dist/messages/en/common.json +2 -0
- package/dist/messages/en/docs.json +1 -0
- package/dist/messages/en/features.json +16 -4
- package/dist/messages/en/home.json +5 -0
- package/dist/messages/en/index.d.ts +58 -25
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/en/index.js +2 -0
- package/dist/messages/en/index.ts +2 -0
- package/dist/messages/en/settings.json +1 -0
- package/dist/messages/en/teams.json +3 -18
- package/dist/messages/es/billing.json +1 -1
- package/dist/messages/es/common.json +2 -0
- package/dist/messages/es/docs.json +1 -0
- package/dist/messages/es/features.json +16 -4
- package/dist/messages/es/home.json +5 -0
- package/dist/messages/es/index.d.ts +41 -8
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/es/index.js +2 -0
- package/dist/messages/es/index.ts +2 -0
- package/dist/messages/es/settings.json +1 -0
- package/dist/messages/es/teams.json +3 -0
- package/dist/messages/fr/billing.json +12 -1
- package/dist/messages/fr/common.json +2 -0
- package/dist/messages/fr/docs.json +1 -0
- package/dist/messages/fr/features.json +16 -4
- package/dist/messages/fr/home.json +5 -0
- package/dist/messages/fr/index.d.ts +68 -25
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/fr/index.js +2 -0
- package/dist/messages/fr/index.ts +2 -0
- package/dist/messages/fr/settings.json +1 -0
- package/dist/messages/fr/teams.json +3 -18
- package/dist/messages/it/billing.json +12 -1
- package/dist/messages/it/common.json +2 -0
- package/dist/messages/it/docs.json +1 -0
- package/dist/messages/it/features.json +16 -4
- package/dist/messages/it/home.json +5 -0
- package/dist/messages/it/index.d.ts +68 -25
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/it/index.js +2 -0
- package/dist/messages/it/index.ts +2 -0
- package/dist/messages/it/settings.json +1 -0
- package/dist/messages/it/teams.json +3 -18
- package/dist/messages/pt/billing.json +12 -1
- package/dist/messages/pt/common.json +2 -0
- package/dist/messages/pt/docs.json +1 -0
- package/dist/messages/pt/features.json +16 -4
- package/dist/messages/pt/home.json +5 -0
- package/dist/messages/pt/index.d.ts +68 -25
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/messages/pt/index.js +2 -0
- package/dist/messages/pt/index.ts +2 -0
- package/dist/messages/pt/settings.json +1 -0
- package/dist/messages/pt/teams.json +3 -18
- package/dist/migrations/029_sample_accounts_outside_development.sql +55 -0
- package/dist/migrations/030_taxonomies_team_writes.sql +29 -0
- package/dist/migrations/031_users_role_suspended.sql +41 -0
- package/dist/migrations/090_sample_data.sql +10 -0
- package/dist/routes/(auth)/signup/page.cc.d.ts +3 -1
- package/dist/routes/(auth)/signup/page.cc.d.ts.map +1 -1
- package/dist/routes/(auth)/signup/page.cc.js +2 -2
- package/dist/routes/(auth)/signup/page.d.ts +3 -1
- package/dist/routes/(auth)/signup/page.d.ts.map +1 -1
- package/dist/routes/(auth)/signup/page.js +4 -3
- package/dist/routes/_internal/area-access.js +2 -2
- package/dist/routes/_internal/entity-permission-layout.d.ts.map +1 -1
- package/dist/routes/_internal/entity-permission-layout.js +0 -7
- package/dist/routes/api/devtools/config/entities/route.js +2 -2
- package/dist/routes/api/devtools/config/theme/route.js +2 -2
- package/dist/routes/api/devtools/tests/[...path]/route.js +2 -2
- package/dist/routes/api/devtools/tests/route.js +2 -2
- package/dist/routes/api/mcp/route.d.ts.map +1 -1
- package/dist/routes/api/mcp/route.js +7 -1
- package/dist/routes/api/superadmin/subscriptions/route.js +2 -2
- package/dist/routes/api/superadmin/teams/[teamId]/route.js +2 -2
- package/dist/routes/api/superadmin/teams/route.js +2 -2
- package/dist/routes/api/superadmin/users/[userId]/route.d.ts +5 -3
- package/dist/routes/api/superadmin/users/[userId]/route.d.ts.map +1 -1
- package/dist/routes/api/superadmin/users/[userId]/route.js +46 -23
- package/dist/routes/api/superadmin/users/route.js +2 -2
- package/dist/routes/api/user/delete-account/route.d.ts.map +1 -1
- package/dist/routes/api/user/delete-account/route.js +2 -3
- package/dist/routes/api/user/plan-flags/route.d.ts.map +1 -1
- package/dist/routes/api/user/plan-flags/route.js +5 -5
- package/dist/routes/api/user/profile/route.js +3 -3
- package/dist/routes/api/v1/auth/signup-with-invite/route.d.ts.map +1 -1
- package/dist/routes/api/v1/auth/signup-with-invite/route.js +80 -44
- package/dist/routes/api/v1/media/upload/route.d.ts +2 -1
- package/dist/routes/api/v1/media/upload/route.d.ts.map +1 -1
- package/dist/routes/api/v1/media/upload/route.js +10 -2
- package/dist/routes/api/v1/post-categories/[id]/route.d.ts.map +1 -1
- package/dist/routes/api/v1/post-categories/[id]/route.js +39 -34
- package/dist/routes/api/v1/post-categories/route.d.ts.map +1 -1
- package/dist/routes/api/v1/post-categories/route.js +18 -14
- package/dist/routes/api/v1/team-invitations/route.d.ts.map +1 -1
- package/dist/routes/api/v1/team-invitations/route.js +12 -5
- package/dist/routes/api/v1/teams/[teamId]/members/route.d.ts.map +1 -1
- package/dist/routes/api/v1/teams/[teamId]/members/route.js +12 -10
- package/dist/routes/api/v1/teams/switch/route.d.ts.map +1 -1
- package/dist/routes/api/v1/teams/switch/route.js +4 -1
- package/dist/routes/dashboard/settings/password/page.d.ts.map +1 -1
- package/dist/routes/dashboard/settings/password/page.js +10 -12
- package/dist/routes/devtools/config/page.js +1 -1
- package/dist/routes/devtools/page.js +1 -1
- package/dist/routes/devtools/scheduled-actions/page.js +1 -1
- package/dist/routes/devtools/tests/[[...path]]/page.js +1 -1
- package/dist/routes/superadmin/users/page.js +2 -2
- package/dist/templates/ci-workflows/github/cypress-regression.yml +1 -1
- package/dist/templates/ci-workflows/github/cypress-smoke.yml +2 -1
- package/dist/templates/next.config.mjs +11 -31
- package/dist/templates/projects/blog/package.json +1 -1
- package/dist/templates/projects/crm/package.json +1 -1
- package/dist/templates/projects/productivity/package.json +1 -1
- package/dist/templates/projects/starter/README.md +2 -0
- package/dist/templates/projects/starter/config/app.config.ts +8 -0
- package/dist/templates/projects/starter/docs/public/01-overview/01-introduction.md +2 -0
- package/dist/templates/projects/starter/templates/dashboard/analytics/layout.tsx +14 -0
- package/dist/templates/proxy.ts +18 -8
- package/dist/templates/public/uploads/README.md +5 -94
- package/migrations/029_sample_accounts_outside_development.sql +55 -0
- package/migrations/030_taxonomies_team_writes.sql +29 -0
- package/migrations/031_users_role_suspended.sql +41 -0
- package/migrations/090_sample_data.sql +10 -0
- package/package.json +3 -3
- package/scripts/build/registry/__tests__/build-output.test.mjs +1 -1
- package/scripts/build/registry/__tests__/project-mode-root-first.test.mjs +6 -6
- package/scripts/build/registry/__tests__/write-places.test.mjs +1 -1
- package/scripts/build/registry/contracts/__tests__/contracts-wire.test.mjs +1 -1
- package/scripts/build/registry/contracts/__tests__/contracts.test.mjs +1 -1
- package/scripts/build/registry/host/README.md +2 -2
- package/scripts/build/registry/host/__tests__/facade-emitter.test.mjs +14 -13
- package/scripts/build/registry/host/__tests__/host-mode.test.mjs +1 -1
- package/scripts/build/registry/host/__tests__/host-project-composition.test.mjs +1 -1
- package/scripts/build/registry/host/__tests__/host-project.test.mjs +2 -2
- package/scripts/build/registry/host/__tests__/host-proxy-areas.test.mjs +99 -1
- package/scripts/build/registry/host/__tests__/host-robustness.test.mjs +1 -1
- package/scripts/build/registry/host/facade-emitter.mjs +1 -1
- package/scripts/build/registry/host/next-route-exports.json +2 -2
- package/scripts/build/registry/host/proxy-areas.mjs +92 -2
- package/scripts/db/run-migrations.mjs +30 -10
- package/scripts/db/sample-data.mjs +44 -0
- package/scripts/db/verify-theme-migrations.mjs +7 -1
- package/templates/ci-workflows/github/cypress-regression.yml +1 -1
- package/templates/ci-workflows/github/cypress-smoke.yml +2 -1
- package/templates/next.config.mjs +11 -31
- package/templates/projects/blog/package.json +1 -1
- package/templates/projects/crm/package.json +1 -1
- package/templates/projects/productivity/package.json +1 -1
- package/templates/projects/starter/README.md +2 -0
- package/templates/projects/starter/config/app.config.ts +8 -0
- package/templates/projects/starter/docs/public/01-overview/01-introduction.md +2 -0
- package/templates/projects/starter/templates/dashboard/analytics/layout.tsx +14 -0
- package/templates/proxy.ts +18 -8
- package/templates/public/uploads/README.md +5 -94
- package/tests/jest/__mocks__/@nextsparkjs/registries/api-presets-registry.ts +2 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/mcp-registry.ts +2 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +5 -4
- package/dist/templates/projects/starter/public/brand/.gitkeep +0 -0
- package/dist/templates/public/uploads/.gitignore +0 -6
- package/dist/templates/public/uploads/temp/.gitkeep +0 -1
- package/templates/projects/starter/public/brand/.gitkeep +0 -0
- package/templates/public/uploads/.gitignore +0 -6
- package/templates/public/uploads/temp/.gitkeep +0 -1
|
@@ -43,6 +43,34 @@ function checkRateLimit(identifier, limit = 1e3, windowMs = 6e4) {
|
|
|
43
43
|
limit
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
+
const apiKeyChecks = /* @__PURE__ */ new WeakMap();
|
|
47
|
+
function apiKeyRateLimitResponse(request, auth) {
|
|
48
|
+
let result = apiKeyChecks.get(request);
|
|
49
|
+
if (!result) {
|
|
50
|
+
result = checkRateLimit(auth.keyId);
|
|
51
|
+
apiKeyChecks.set(request, result);
|
|
52
|
+
}
|
|
53
|
+
if (result.allowed) return null;
|
|
54
|
+
const retryAfter = Math.ceil((result.resetTime - Date.now()) / 1e3);
|
|
55
|
+
return NextResponse.json(
|
|
56
|
+
{
|
|
57
|
+
success: false,
|
|
58
|
+
error: "Rate limit exceeded",
|
|
59
|
+
message: `Too many requests. Limit: ${result.limit} requests per minute`,
|
|
60
|
+
code: "RATE_LIMIT_EXCEEDED",
|
|
61
|
+
retryAfter
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
status: 429,
|
|
65
|
+
headers: {
|
|
66
|
+
"X-RateLimit-Limit": result.limit.toString(),
|
|
67
|
+
"X-RateLimit-Remaining": "0",
|
|
68
|
+
"X-RateLimit-Reset": result.resetTime.toString(),
|
|
69
|
+
"Retry-After": retryAfter.toString()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
}
|
|
46
74
|
async function checkDistributedRateLimit(identifier, type = "api") {
|
|
47
75
|
if (isRateLimitingDisabled()) {
|
|
48
76
|
return { allowed: true, remaining: 999, resetTime: Date.now() + 6e4, limit: 999 };
|
|
@@ -225,9 +253,11 @@ function withRateLimitTier(handler, tier = "api") {
|
|
|
225
253
|
return createRateLimitErrorResponse(rateLimitResult);
|
|
226
254
|
}
|
|
227
255
|
const response = await handler(request, ...args);
|
|
228
|
-
response.
|
|
229
|
-
|
|
230
|
-
|
|
256
|
+
if (response.status !== 429) {
|
|
257
|
+
response.headers.set("X-RateLimit-Limit", rateLimitResult.limit.toString());
|
|
258
|
+
response.headers.set("X-RateLimit-Remaining", Math.max(0, rateLimitResult.remaining).toString());
|
|
259
|
+
response.headers.set("X-RateLimit-Reset", rateLimitResult.resetTime.toString());
|
|
260
|
+
}
|
|
231
261
|
return response;
|
|
232
262
|
};
|
|
233
263
|
return async (request, ...args) => {
|
|
@@ -237,6 +267,7 @@ function withRateLimitTier(handler, tier = "api") {
|
|
|
237
267
|
}
|
|
238
268
|
export {
|
|
239
269
|
addRateLimitHeaders,
|
|
270
|
+
apiKeyRateLimitResponse,
|
|
240
271
|
applyRateLimit,
|
|
241
272
|
checkDistributedRateLimit,
|
|
242
273
|
checkRateLimit,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-origin.d.ts","sourceRoot":"","sources":["../../../src/lib/api/request-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"request-origin.d.ts","sourceRoot":"","sources":["../../../src/lib/api/request-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAgEvD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY,GAAG,WAAW,CAoBnF"}
|
|
@@ -36,7 +36,7 @@ function withoutSessionCookies(request) {
|
|
|
36
36
|
}
|
|
37
37
|
function isTrustedWriteOrigin(request, origin) {
|
|
38
38
|
const path = requestPathname(request);
|
|
39
|
-
if (path
|
|
39
|
+
if (path === void 0 || isAppOnlyCorsPath(path)) {
|
|
40
40
|
const lan = normalizeCorsEnvironment(process.env.NODE_ENV || "development") !== "production" && isPrivateLanOrigin(origin);
|
|
41
41
|
return lan || appOrigins().includes(origin);
|
|
42
42
|
}
|
|
@@ -46,7 +46,10 @@ export declare function getPrimaryEmailMethod(methods: readonly AuthLoginMethod[
|
|
|
46
46
|
export declare function matchAuthPreset(methods: readonly AuthLoginMethod[]): AuthPreset | null;
|
|
47
47
|
/**
|
|
48
48
|
* Server-side: should Better Auth's email + password endpoints be enabled?
|
|
49
|
-
*
|
|
49
|
+
* An explicit `emailAndPassword.enabled` decides. Otherwise they are on when
|
|
50
|
+
* `methods` lists `'email-password'`, and outside production (so the dev
|
|
51
|
+
* keyring, `db:seed` users and Cypress sign in with a password); a production
|
|
52
|
+
* app on the passwordless preset serves no password endpoint.
|
|
50
53
|
*/
|
|
51
|
-
export declare function isPasswordLoginEnabled(authConfig?: Pick<AuthConfig, 'emailAndPassword'> | null): boolean;
|
|
54
|
+
export declare function isPasswordLoginEnabled(authConfig?: Pick<AuthConfig, 'methods' | 'emailAndPassword'> | null, nodeEnv?: string | undefined): boolean;
|
|
52
55
|
//# sourceMappingURL=auth-methods.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-methods.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/auth-methods.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE9E,uDAAuD;AACvD,eAAO,MAAM,kBAAkB,EAAE,SAAS,eAAe,EAA8C,CAAA;AAEvG,uEAAuE;AACvE,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC,CAKjF,CAAA;AAED,mEAAmE;AACnE,eAAO,MAAM,oBAAoB,EAAE,SAAS,eAAe,EAA8B,CAAA;AAEzF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,GAChF,eAAe,EAAE,CAkBnB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAEzG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,OAAO,CAEjF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,eAAe,EAAE,GAClC,OAAO,CAAC,eAAe,EAAE,WAAW,GAAG,gBAAgB,CAAC,GAAG,IAAI,CAKjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,UAAU,GAAG,IAAI,CAMtF;AAED
|
|
1
|
+
{"version":3,"file":"auth-methods.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/auth-methods.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE9E,uDAAuD;AACvD,eAAO,MAAM,kBAAkB,EAAE,SAAS,eAAe,EAA8C,CAAA;AAEvG,uEAAuE;AACvE,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC,CAKjF,CAAA;AAED,mEAAmE;AACnE,eAAO,MAAM,oBAAoB,EAAE,SAAS,eAAe,EAA8B,CAAA;AAEzF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,GAChF,eAAe,EAAE,CAkBnB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAEzG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,OAAO,CAEjF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,eAAe,EAAE,GAClC,OAAO,CAAC,eAAe,EAAE,WAAW,GAAG,gBAAgB,CAAC,GAAG,IAAI,CAKjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,UAAU,GAAG,IAAI,CAMtF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,kBAAkB,CAAC,GAAG,IAAI,EACpE,OAAO,GAAE,MAAM,GAAG,SAAgC,GACjD,OAAO,CAIT"}
|
|
@@ -45,9 +45,11 @@ function matchAuthPreset(methods) {
|
|
|
45
45
|
}
|
|
46
46
|
return null;
|
|
47
47
|
}
|
|
48
|
-
function isPasswordLoginEnabled(authConfig) {
|
|
48
|
+
function isPasswordLoginEnabled(authConfig, nodeEnv = process.env.NODE_ENV) {
|
|
49
49
|
var _a;
|
|
50
|
-
|
|
50
|
+
const declared = (_a = authConfig == null ? void 0 : authConfig.emailAndPassword) == null ? void 0 : _a.enabled;
|
|
51
|
+
if (typeof declared === "boolean") return declared;
|
|
52
|
+
return ((authConfig == null ? void 0 : authConfig.methods) ?? []).includes("email-password") || nodeEnv !== "production";
|
|
51
53
|
}
|
|
52
54
|
export {
|
|
53
55
|
AUTH_LOGIN_METHODS,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type TypedSession } from '../auth';
|
|
2
|
+
/**
|
|
3
|
+
* The request's session as the database has it now, for authorization reads: the API entry points (dual-auth,
|
|
4
|
+
* `validateAndAuthenticateRequest`), the /api/user routes, the server actions, generateEntityAPI, the /superadmin and
|
|
5
|
+
* /devtools API and page checks, and the proxy template's gate.
|
|
6
|
+
*
|
|
7
|
+
* Better Auth's cookie cache (`session.cookieCache`, on by default for 5 minutes) answers `getSession` from the
|
|
8
|
+
* signed `session_data` cookie without reading the database, so a signed-out or revoked session, or a role changed
|
|
9
|
+
* since sign-in, would keep its old answer until that cookie expires. `disableCookieCache` reads the session and its
|
|
10
|
+
* user from the database instead (one query). Reads that only display data keep the cache. A suspended account's
|
|
11
|
+
* session is no session (lib/auth/suspension.ts).
|
|
12
|
+
*/
|
|
13
|
+
export declare function getAuthorizationSession(headers: Headers): Promise<TypedSession | null>;
|
|
14
|
+
//# sourceMappingURL=authorization-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorization-session.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/authorization-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,YAAY,EAAE,MAAM,SAAS,CAAA;AAGjD;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAG5F"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { auth } from "../auth.js";
|
|
2
|
+
import { isSuspendedRole } from "./suspension.js";
|
|
3
|
+
async function getAuthorizationSession(headers) {
|
|
4
|
+
var _a;
|
|
5
|
+
const session = await auth.api.getSession({ headers, query: { disableCookieCache: true } });
|
|
6
|
+
return session && !isSuspendedRole((_a = session.user) == null ? void 0 : _a.role) ? session : null;
|
|
7
|
+
}
|
|
8
|
+
export {
|
|
9
|
+
getAuthorizationSession
|
|
10
|
+
};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { type TypedSession } from '../auth';
|
|
2
2
|
/** The request's verified session, or null; one lookup per request however many callers ask. */
|
|
3
3
|
export declare const getRequestSession: () => Promise<TypedSession | null>;
|
|
4
|
+
/**
|
|
5
|
+
* The same, read from the database instead of better-auth's cookie cache, once per request: the /superadmin and
|
|
6
|
+
* /devtools checks use it, so a sign-out or a role change counts at once (lib/auth/authorization-session.ts).
|
|
7
|
+
*/
|
|
8
|
+
export declare const getAuthorizationRequestSession: () => Promise<TypedSession | null>;
|
|
4
9
|
/** The team the session chose (the activeTeamId cookie, only when this session wrote it), or null. */
|
|
5
10
|
export declare function getSessionActiveTeamId(session: TypedSession): Promise<string | null>;
|
|
6
11
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-session.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/request-session.ts"],"names":[],"mappings":"AAWA,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"request-session.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/request-session.ts"],"names":[],"mappings":"AAWA,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,SAAS,CAAA;AAK5D,gGAAgG;AAChG,eAAO,MAAM,iBAAiB,QAAmB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAsC,CAAA;AAElH;;;GAGG;AACH,eAAO,MAAM,8BAA8B,QAAmB,OAAO,CAAC,YAAY,GAAG,IAAI,CAA8C,CAAA;AAEvI,sGAAsG;AACtG,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAE1F;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAK/G"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { cache } from "react";
|
|
2
2
|
import { cookies, headers } from "next/headers";
|
|
3
3
|
import { getTypedSession } from "../auth.js";
|
|
4
|
+
import { getAuthorizationSession } from "./authorization-session.js";
|
|
4
5
|
import { ACTIVE_TEAM_COOKIE, activeTeamIdForSession } from "../teams/active-team-cookie.js";
|
|
5
6
|
import { getDashboardTeamId } from "../teams/dashboard-team.js";
|
|
6
7
|
const getRequestSession = cache(async () => getTypedSession(await headers()));
|
|
8
|
+
const getAuthorizationRequestSession = cache(async () => getAuthorizationSession(await headers()));
|
|
7
9
|
async function getSessionActiveTeamId(session) {
|
|
8
10
|
var _a, _b;
|
|
9
11
|
return activeTeamIdForSession((_a = (await cookies()).get(ACTIVE_TEAM_COOKIE)) == null ? void 0 : _a.value, (_b = session.session) == null ? void 0 : _b.id);
|
|
@@ -16,6 +18,7 @@ async function getDashboardPermissionContext() {
|
|
|
16
18
|
return { userId, teamId: await getDashboardTeamId(userId, await getSessionActiveTeamId(session)) };
|
|
17
19
|
}
|
|
18
20
|
export {
|
|
21
|
+
getAuthorizationRequestSession,
|
|
19
22
|
getDashboardPermissionContext,
|
|
20
23
|
getRequestSession,
|
|
21
24
|
getSessionActiveTeamId
|
|
@@ -8,12 +8,15 @@ export interface RuntimeAuthReadinessOptions {
|
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* Server-derived flows that do not follow the login UI intent (`auth.methods`).
|
|
11
|
-
* The password backend (`
|
|
12
|
-
*
|
|
13
|
-
* not
|
|
11
|
+
* The password backend (`isPasswordLoginEnabled`) can be on without
|
|
12
|
+
* `'email-password'` in `methods` (outside production, or declared), so
|
|
13
|
+
* invitation registration and password recovery are not inferred from
|
|
14
|
+
* `availableMethods`.
|
|
14
15
|
*/
|
|
15
16
|
export interface PublicAuthCapabilities {
|
|
16
|
-
/** `/api/v1/auth/signup-with-invite`
|
|
17
|
+
/** `/api/v1/auth/signup-with-invite` creates the invited account: always, with or without a password. */
|
|
18
|
+
invitationSignup: boolean;
|
|
19
|
+
/** `/api/v1/auth/signup-with-invite` takes a password (backend switch only); otherwise the account signs in by code. */
|
|
17
20
|
invitationPasswordSignup: boolean;
|
|
18
21
|
/** `/request-password-reset` can run: password backend + usable email delivery. */
|
|
19
22
|
passwordRecovery: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-readiness.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/runtime-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAGpB,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"runtime-readiness.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/runtime-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAGpB,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAIlE,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,aAAa,CAAA;AAEpB,KAAK,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAE5D,MAAM,WAAW,2BAA2B;IAC1C,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,WAAW,GAAG,kBAAkB,CAAC,GAAG,IAAI,CAAA;IAClF,GAAG,CAAC,EAAE,kBAAkB,CAAA;CACzB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,yGAAyG;IACzG,gBAAgB,EAAE,OAAO,CAAA;IACzB,wHAAwH;IACxH,wBAAwB,EAAE,OAAO,CAAA;IACjC,mFAAmF;IACnF,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,OAAO,GAAG,aAAa,CAAA;IAC/B,gBAAgB,EAAE,eAAe,EAAE,CAAA;IACnC,YAAY,EAAE,sBAAsB,CAAA;CACrC;AAaD,qEAAqE;AACrE,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,2BAAgC,GAAG,mBAAmB,CAEtG;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,2BAAgC,GAAG,IAAI,CAgBzF;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,2BAAgC,GAAG,mBAAmB,CAarG;AAiBD,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAE1F;AAED,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAE3F;AAmDD;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA4E1B;AAED,sFAAsF;AACtF,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,EAChC,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,QAAQ,CAAC,CAGnB"}
|
|
@@ -2,6 +2,7 @@ import "server-only";
|
|
|
2
2
|
import { AUTH_CONFIG } from "../config/index.js";
|
|
3
3
|
import { isPasswordLoginEnabled } from "./auth-methods.js";
|
|
4
4
|
import { warnClientIpSourceAtStartup } from "../api/client-ip.js";
|
|
5
|
+
import { warnSampleAccountsAtStartup } from "./sample-accounts.js";
|
|
5
6
|
import {
|
|
6
7
|
authReadinessConfigurationFromEnv,
|
|
7
8
|
evaluateAuthReadiness
|
|
@@ -21,6 +22,7 @@ function getRuntimeAuthReadiness(options = {}) {
|
|
|
21
22
|
}
|
|
22
23
|
function logAuthReadinessAtStartup(options = {}) {
|
|
23
24
|
warnClientIpSourceAtStartup(options.env ?? process.env);
|
|
25
|
+
void warnSampleAccountsAtStartup(options.env ?? process.env);
|
|
24
26
|
try {
|
|
25
27
|
const env = options.env ?? process.env;
|
|
26
28
|
if (env.NODE_ENV !== "production") return;
|
|
@@ -37,11 +39,12 @@ function logAuthReadinessAtStartup(options = {}) {
|
|
|
37
39
|
function getPublicAuthReadiness(options = {}) {
|
|
38
40
|
const result = getRuntimeAuthReadiness(options);
|
|
39
41
|
const authConfig = options.authConfig === void 0 ? AUTH_CONFIG : options.authConfig;
|
|
40
|
-
const passwordBackend = isPasswordLoginEnabled(authConfig);
|
|
42
|
+
const passwordBackend = isPasswordLoginEnabled(authConfig, (options.env ?? process.env).NODE_ENV);
|
|
41
43
|
return {
|
|
42
44
|
status: result.outcome === "ready" ? "ready" : "unavailable",
|
|
43
45
|
availableMethods: result.availableMethods,
|
|
44
46
|
capabilities: {
|
|
47
|
+
invitationSignup: true,
|
|
45
48
|
invitationPasswordSignup: passwordBackend,
|
|
46
49
|
passwordRecovery: passwordBackend && isRuntimeEmailAvailable(options)
|
|
47
50
|
}
|
|
@@ -68,7 +71,7 @@ function authRoutePath(pathname) {
|
|
|
68
71
|
const marker = "/api/auth";
|
|
69
72
|
const index = pathname.lastIndexOf(marker);
|
|
70
73
|
if (index === -1) return null;
|
|
71
|
-
const suffix = pathname.slice(index + marker.length);
|
|
74
|
+
const suffix = pathname.slice(index + marker.length).replace(/\/+$/, "");
|
|
72
75
|
return suffix || "/";
|
|
73
76
|
}
|
|
74
77
|
function jsonResponse(body, status = 200) {
|
|
@@ -103,7 +106,7 @@ async function requestedSocialProvider(request) {
|
|
|
103
106
|
}
|
|
104
107
|
}
|
|
105
108
|
async function getAuthReadinessResponse(request, options = {}) {
|
|
106
|
-
var _a, _b
|
|
109
|
+
var _a, _b;
|
|
107
110
|
const pathname = new URL(request.url).pathname;
|
|
108
111
|
const route = authRoutePath(pathname);
|
|
109
112
|
if (!route) return null;
|
|
@@ -138,10 +141,10 @@ async function getAuthReadinessResponse(request, options = {}) {
|
|
|
138
141
|
return unavailableResponse(method, diagnostics, pathname);
|
|
139
142
|
}
|
|
140
143
|
}
|
|
141
|
-
if (isPasswordOperation && (
|
|
144
|
+
if (isPasswordOperation && !isPasswordLoginEnabled(authConfig, (options.env ?? process.env).NODE_ENV)) {
|
|
142
145
|
return unavailableResponse("email-password", [{
|
|
143
146
|
code: "EMAIL_PASSWORD_POLICY_DISABLED",
|
|
144
|
-
message: "Password authentication is
|
|
147
|
+
message: "Password authentication is off: auth.methods has no email-password and auth.emailAndPassword.enabled is not true."
|
|
145
148
|
}], pathname);
|
|
146
149
|
}
|
|
147
150
|
if (isPasswordEmailDeliveryOperation || isGeneralEmailDeliveryOperation) {
|
|
@@ -158,7 +161,7 @@ async function getAuthReadinessResponse(request, options = {}) {
|
|
|
158
161
|
method = "google";
|
|
159
162
|
const readiness = evaluateProvider("google", options);
|
|
160
163
|
diagnostics = readiness.diagnostics;
|
|
161
|
-
if (((
|
|
164
|
+
if (((_b = (_a = authConfig == null ? void 0 : authConfig.providers) == null ? void 0 : _a.google) == null ? void 0 : _b.enabled) === false) {
|
|
162
165
|
return unavailableResponse(method, [{
|
|
163
166
|
code: "GOOGLE_POLICY_DISABLED",
|
|
164
167
|
message: "Google authentication is disabled by auth.providers.google.enabled."
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The password hashes the sample data ships with (core's 090_sample_data.sql and
|
|
3
|
+
* the project templates' sample data). A "credential" account that still holds
|
|
4
|
+
* one of them can be signed in to with a documented password.
|
|
5
|
+
* Keep in sync with migrations/029_sample_accounts_outside_development.sql.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SAMPLE_PASSWORD_HASHES: readonly ["22de14d5472248ed0bece911df908b2a:d29576424798ba6845d348a3767c0b0f38a00f2aca461b3b1d34b99a93cab06c86774c6edb183e6d6ec47457649b032a49a7b60a48f6f4f7fbbc4ea40258f19f", "3db9e98e2b4d3caca97fdf2783791cbc:34b293de615caf277a237773208858e960ea8aa10f1f5c5c309b632f192cac34d52ceafbd338385616f4929e4b1b6c055b67429c6722ffdb80b01d9bf4764866"];
|
|
8
|
+
type QueryRows = (text: string, params?: unknown[]) => Promise<Array<{
|
|
9
|
+
count: number;
|
|
10
|
+
}>>;
|
|
11
|
+
/**
|
|
12
|
+
* In production, logs one warning when accounts with a sample password exist.
|
|
13
|
+
* Never throws. When the query fails it logs one line with the error code or class name only
|
|
14
|
+
* (never the error object, which can carry the connection string).
|
|
15
|
+
*/
|
|
16
|
+
export declare function warnSampleAccountsAtStartup(env?: Record<string, string | undefined>, queryRows?: QueryRows): Promise<void>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=sample-accounts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sample-accounts.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/sample-accounts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,qVAGzB,CAAA;AAEV,KAAK,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAAA;AAExF;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,EACrD,SAAS,CAAC,EAAE,SAAS,GACpB,OAAO,CAAC,IAAI,CAAC,CAgBf"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const SAMPLE_PASSWORD_HASHES = [
|
|
2
|
+
"22de14d5472248ed0bece911df908b2a:d29576424798ba6845d348a3767c0b0f38a00f2aca461b3b1d34b99a93cab06c86774c6edb183e6d6ec47457649b032a49a7b60a48f6f4f7fbbc4ea40258f19f",
|
|
3
|
+
"3db9e98e2b4d3caca97fdf2783791cbc:34b293de615caf277a237773208858e960ea8aa10f1f5c5c309b632f192cac34d52ceafbd338385616f4929e4b1b6c055b67429c6722ffdb80b01d9bf4764866"
|
|
4
|
+
];
|
|
5
|
+
async function warnSampleAccountsAtStartup(env = process.env, queryRows) {
|
|
6
|
+
if (env.NODE_ENV !== "production") return;
|
|
7
|
+
try {
|
|
8
|
+
const run = queryRows ?? (await import("../db.js")).queryRows;
|
|
9
|
+
const [row] = await run(
|
|
10
|
+
`SELECT count(*)::int AS count FROM "account" WHERE "providerId" = 'credential' AND "password" = ANY($1)`,
|
|
11
|
+
[SAMPLE_PASSWORD_HASHES]
|
|
12
|
+
);
|
|
13
|
+
if (row && row.count > 0) {
|
|
14
|
+
console.warn(`[auth-readiness] ${row.count} account(s) still have a sample-data password; pnpm db:migrate disables them once; if it already ran, see the CHANGELOG upgrade notes`);
|
|
15
|
+
}
|
|
16
|
+
} catch (error) {
|
|
17
|
+
const safe = (value) => typeof value === "string" && /^[A-Za-z0-9_]{1,40}$/.test(value) ? value : void 0;
|
|
18
|
+
const reason = safe(error == null ? void 0 : error.code) ?? safe(error == null ? void 0 : error.name) ?? "unknown error";
|
|
19
|
+
console.warn(`[auth-readiness] could not check for sample-data passwords at startup (query failed: ${reason}); check DATABASE_URL, then run the read-only query in the 0.1.0-beta.196 CHANGELOG upgrade notes`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
SAMPLE_PASSWORD_HASHES,
|
|
24
|
+
warnSampleAccountsAtStartup
|
|
25
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A suspended account: `users.role = 'suspended'` (allowed by `check_users_role` since migration 031). It gets no new
|
|
3
|
+
* session (the session create hook in lib/auth.ts), and its existing session and its API keys are refused where core
|
|
4
|
+
* authenticates: dual-auth, `validateAndAuthenticateRequest`, getAuthorizationSession, validateApiKey and the proxy
|
|
5
|
+
* template.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SUSPENDED_ROLE = "suspended";
|
|
8
|
+
export declare function isSuspendedRole(role: unknown): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Suspends `userId` in one statement: sets the role, deletes the user's sessions and deactivates their active API keys
|
|
11
|
+
* (`status = 'inactive'`, so the audit log keeps the rows; unsuspending does not bring them back). A superadmin is never
|
|
12
|
+
* suspended. Returns null when no user was suspended (unknown id, or a superadmin).
|
|
13
|
+
*/
|
|
14
|
+
export declare function suspendUser(userId: string, actorId: string): Promise<{
|
|
15
|
+
sessions: number;
|
|
16
|
+
apiKeys: number;
|
|
17
|
+
} | null>;
|
|
18
|
+
/** Gives a suspended user back the `member` role. Their deactivated API keys stay deactivated. Null if not suspended. */
|
|
19
|
+
export declare function unsuspendUser(userId: string, actorId: string): Promise<{
|
|
20
|
+
id: string;
|
|
21
|
+
} | null>;
|
|
22
|
+
//# sourceMappingURL=suspension.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suspension.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/suspension.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,cAAc,cAAc,CAAA;AAEzC,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAEtD;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAuBxH;AAED,yHAAyH;AACzH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAOnG"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { queryWithRLS } from "../db.js";
|
|
2
|
+
import { invalidateApiKeyCache } from "../api/auth.js";
|
|
3
|
+
const SUSPENDED_ROLE = "suspended";
|
|
4
|
+
function isSuspendedRole(role) {
|
|
5
|
+
return role === SUSPENDED_ROLE;
|
|
6
|
+
}
|
|
7
|
+
async function suspendUser(userId, actorId) {
|
|
8
|
+
const [result] = await queryWithRLS(
|
|
9
|
+
`WITH suspended AS (
|
|
10
|
+
UPDATE "users" SET role = '${SUSPENDED_ROLE}', "updatedAt" = NOW()
|
|
11
|
+
WHERE id = $1 AND role <> 'superadmin'
|
|
12
|
+
RETURNING id
|
|
13
|
+
), sessions AS (
|
|
14
|
+
DELETE FROM "session" WHERE "userId" IN (SELECT id FROM suspended) RETURNING id
|
|
15
|
+
), keys AS (
|
|
16
|
+
UPDATE "api_key" SET status = 'inactive'
|
|
17
|
+
WHERE "userId" IN (SELECT id FROM suspended) AND status = 'active'
|
|
18
|
+
RETURNING "keyHash"
|
|
19
|
+
)
|
|
20
|
+
SELECT (SELECT COUNT(*) FROM suspended)::int AS suspended,
|
|
21
|
+
(SELECT COUNT(*) FROM sessions)::int AS sessions,
|
|
22
|
+
COALESCE((SELECT json_agg("keyHash") FROM keys), '[]'::json) AS "keyHashes"`,
|
|
23
|
+
[userId],
|
|
24
|
+
actorId
|
|
25
|
+
);
|
|
26
|
+
if (!(result == null ? void 0 : result.suspended)) return null;
|
|
27
|
+
for (const keyHash of result.keyHashes) invalidateApiKeyCache(keyHash);
|
|
28
|
+
return { sessions: result.sessions, apiKeys: result.keyHashes.length };
|
|
29
|
+
}
|
|
30
|
+
async function unsuspendUser(userId, actorId) {
|
|
31
|
+
const [row] = await queryWithRLS(
|
|
32
|
+
`UPDATE "users" SET role = 'member', "updatedAt" = NOW() WHERE id = $1 AND role = '${SUSPENDED_ROLE}' RETURNING id`,
|
|
33
|
+
[userId],
|
|
34
|
+
actorId
|
|
35
|
+
);
|
|
36
|
+
return row ?? null;
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
SUSPENDED_ROLE,
|
|
40
|
+
isSuspendedRole,
|
|
41
|
+
suspendUser,
|
|
42
|
+
unsuspendUser
|
|
43
|
+
};
|
package/dist/lib/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAWA,OAAO,EAA0F,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAmBjI;;;;;;;;;;;;;GAaG;AACH,wBAAsB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQlF;AAED;;;;;;;;;GASG;AACH,wBAAsB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnF;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyHD,eAAO,MAAM,IAAI,qEAsXf,CAAC;AAEH,MAAM,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;AACjD,MAAM,MAAM,WAAW,GAAG,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,kBAAkB,EAAE,QAAQ,EAAE,CAAC;CAC/C,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC5B,IAAI,EAAE,WAAW,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAO9B"}
|
package/dist/lib/auth.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { betterAuth } from "better-auth";
|
|
2
|
-
import {
|
|
1
|
+
import { betterAuth, APIError } from "better-auth";
|
|
2
|
+
import { createPool } from "./db-pool.js";
|
|
3
3
|
import { nextCookies } from "better-auth/next-js";
|
|
4
4
|
import { emailOTP } from "better-auth/plugins";
|
|
5
5
|
import { parseSSLConfig, stripSSLParams, queryOne } from "./db.js";
|
|
@@ -42,13 +42,13 @@ const databaseUrl = process.env.DATABASE_SERVICE_URL || process.env.DATABASE_URL
|
|
|
42
42
|
const cleanUrl = stripSSLParams(databaseUrl);
|
|
43
43
|
const isPoolerUrl = /:6543(\/|$|\?)/.test(databaseUrl) || /pooler/i.test(databaseUrl);
|
|
44
44
|
const connectionString = isPoolerUrl ? cleanUrl.includes("?") ? `${cleanUrl}&pgbouncer=true` : `${cleanUrl}?pgbouncer=true` : cleanUrl;
|
|
45
|
-
const
|
|
45
|
+
const AUTH_POOLS = /* @__PURE__ */ Symbol.for("nextspark.auth.pools");
|
|
46
|
+
const authPools = globalThis[AUTH_POOLS] ??= /* @__PURE__ */ new Map();
|
|
47
|
+
const pool = authPools.get(databaseUrl) ?? createPool("auth", {
|
|
46
48
|
connectionString,
|
|
47
|
-
ssl: parseSSLConfig(databaseUrl)
|
|
48
|
-
connectionTimeoutMillis: 1e4,
|
|
49
|
-
idleTimeoutMillis: 3e4,
|
|
50
|
-
max: 20
|
|
49
|
+
ssl: parseSSLConfig(databaseUrl)
|
|
51
50
|
});
|
|
51
|
+
authPools.set(databaseUrl, pool);
|
|
52
52
|
const sessionConfig = resolveSessionConfig(AUTH_CONFIG);
|
|
53
53
|
const otpConfig = resolveOtpConfig(AUTH_CONFIG);
|
|
54
54
|
function resolveNonOwnerSignupRole() {
|
|
@@ -137,10 +137,9 @@ const auth = betterAuth({
|
|
|
137
137
|
}
|
|
138
138
|
},
|
|
139
139
|
emailAndPassword: {
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
// `auth.emailAndPassword.enabled: false`.
|
|
140
|
+
// On when AUTH_CONFIG.methods lists 'email-password', and outside production
|
|
141
|
+
// (seeded test users, the dev keyring). A production passwordless app opts
|
|
142
|
+
// in with `auth.emailAndPassword.enabled: true` (API logins, test users).
|
|
144
143
|
enabled: isPasswordLoginEnabled(AUTH_CONFIG),
|
|
145
144
|
requireEmailVerification: true,
|
|
146
145
|
minPasswordLength: 8,
|
|
@@ -277,7 +276,7 @@ const auth = betterAuth({
|
|
|
277
276
|
if (registrationMode === "domain-restricted" || registrationMode === "domain-open") {
|
|
278
277
|
const allowedDomains = ((_d = (_c = AUTH_CONFIG) == null ? void 0 : _c.registration) == null ? void 0 : _d.allowedDomains) ?? [];
|
|
279
278
|
if (allowedDomains.length > 0 && !isDomainAllowed(user.email, allowedDomains)) {
|
|
280
|
-
console.log(`[Auth] Blocked registration for ${user.email}: domain not in allowedDomains (allowed: ${allowedDomains.join(", ")})`);
|
|
279
|
+
console.log(`[Auth] Blocked registration for a user at ${user.email.split("@").pop()}: domain not in allowedDomains (allowed: ${allowedDomains.join(", ")})`);
|
|
281
280
|
throw new Error(`DOMAIN_NOT_ALLOWED: Email domain not authorized. Please use an email from: ${allowedDomains.join(", ")}`);
|
|
282
281
|
}
|
|
283
282
|
}
|
|
@@ -333,18 +332,22 @@ const auth = betterAuth({
|
|
|
333
332
|
// Enforce domain restrictions on EVERY login (not just signup)
|
|
334
333
|
// @ts-expect-error — pre-existing type error, tracked in https://github.com/NextSpark-js/nextspark/issues/131
|
|
335
334
|
before: async (session) => {
|
|
336
|
-
var _a, _b, _c, _d, _e;
|
|
337
|
-
const
|
|
335
|
+
var _a, _b, _c, _d, _e, _f;
|
|
336
|
+
const owner = await pool.query("SELECT role FROM users WHERE id = $1 LIMIT 1", [session.userId]);
|
|
337
|
+
if (((_a = owner.rows[0]) == null ? void 0 : _a.role) === "suspended") {
|
|
338
|
+
throw new APIError("FORBIDDEN", { message: "This account is suspended", code: "ACCOUNT_SUSPENDED" });
|
|
339
|
+
}
|
|
340
|
+
const registrationMode = ((_c = (_b = AUTH_CONFIG) == null ? void 0 : _b.registration) == null ? void 0 : _c.mode) ?? "open";
|
|
338
341
|
if (registrationMode === "domain-restricted" || registrationMode === "domain-open") {
|
|
339
|
-
const allowedDomains = ((
|
|
342
|
+
const allowedDomains = ((_e = (_d = AUTH_CONFIG) == null ? void 0 : _d.registration) == null ? void 0 : _e.allowedDomains) ?? [];
|
|
340
343
|
if (allowedDomains.length > 0) {
|
|
341
344
|
const result = await pool.query(
|
|
342
345
|
"SELECT email FROM users WHERE id = $1 LIMIT 1",
|
|
343
346
|
[session.userId]
|
|
344
347
|
);
|
|
345
|
-
const email = (
|
|
348
|
+
const email = (_f = result.rows[0]) == null ? void 0 : _f.email;
|
|
346
349
|
if (email && !isDomainAllowed(email, allowedDomains)) {
|
|
347
|
-
console.log(`[Auth] Blocked sign-in for ${email}: domain not in allowedDomains`);
|
|
350
|
+
console.log(`[Auth] Blocked sign-in for a user at ${email.split("@").pop()}: domain not in allowedDomains`);
|
|
348
351
|
return false;
|
|
349
352
|
}
|
|
350
353
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { EntityConfig } from '../entities/types';
|
|
2
|
+
/**
|
|
3
|
+
* The public pages of an entity are cached (`'use cache'` tagged `entity:<slug>` in public-item-route.cc; ISR
|
|
4
|
+
* `revalidate` in the other host), so a write has to expire them. Every writer of an entity ends here: the
|
|
5
|
+
* generic REST handler, the entity handler, GenericEntityService and the server actions reach it through the
|
|
6
|
+
* `afterEntity*` hooks (entity-hooks.ts), and the one write that skips hooks (`deleteMany`) calls it itself.
|
|
7
|
+
*
|
|
8
|
+
* - Cache Components: `entity:<slug>` is on every cached read of the entity's items (found or not found), so
|
|
9
|
+
* one tag covers the old and the new slug of a rename, an unpublished item and a deleted one. It also
|
|
10
|
+
* drops the sibling items' entries; they are read again by their next visit.
|
|
11
|
+
* - ISR: Next tags a page by its route file path, route group included, so the path is `/(public)<basePath>` with
|
|
12
|
+
* 'layout' (item pages and archive of a base path) or the root item route with 'page'. `(public)` is the group the
|
|
13
|
+
* host generator puts those routes in (scripts/build/registry/host/entity-routes.mjs); a test keeps them equal.
|
|
14
|
+
* - Patterns are tagged `patterns` and are expired by their own writes.
|
|
15
|
+
*
|
|
16
|
+
* Outside a request (a script, a test) Next refuses the call; there is no cache to expire there.
|
|
17
|
+
*/
|
|
18
|
+
/** Route group of the generated public routes. Keep equal to the one in scripts/build/registry/host/entity-routes.mjs. */
|
|
19
|
+
export declare const PUBLIC_ROUTE_GROUP = "(public)";
|
|
20
|
+
export declare function expirePublicEntity(config: Pick<EntityConfig, 'slug' | 'access' | 'builder'> | undefined): void;
|
|
21
|
+
//# sourceMappingURL=public-entity-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-entity-cache.d.ts","sourceRoot":"","sources":["../../../src/lib/cache/public-entity-cache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIrD;;;;;;;;;;;;;;;GAeG;AACH,0HAA0H;AAC1H,eAAO,MAAM,kBAAkB,aAAa,CAAA;AAE5C,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC,GAAG,SAAS,GAAG,IAAI,CAe9G"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { revalidatePath } from "next/cache";
|
|
2
|
+
import { unstable_rethrow } from "next/navigation";
|
|
3
|
+
import { getEntityBasePath } from "../entities/schema-generator.js";
|
|
4
|
+
import { revalidateTag } from "./revalidate-tag.js";
|
|
5
|
+
const PUBLIC_ROUTE_GROUP = "(public)";
|
|
6
|
+
function expirePublicEntity(config) {
|
|
7
|
+
var _a;
|
|
8
|
+
if (!config) return;
|
|
9
|
+
try {
|
|
10
|
+
if (config.slug === "patterns") revalidateTag("patterns");
|
|
11
|
+
const basePath = getEntityBasePath(config);
|
|
12
|
+
if (!basePath) return;
|
|
13
|
+
revalidateTag(`entity:${config.slug}`);
|
|
14
|
+
const group = `/${PUBLIC_ROUTE_GROUP}`;
|
|
15
|
+
if (basePath === "/") revalidatePath(`${group}${((_a = config.access) == null ? void 0 : _a.allowNestedSlugs) ? "/[...slug]" : "/[slug]"}`, "page");
|
|
16
|
+
else revalidatePath(`${group}${basePath}`, "layout");
|
|
17
|
+
} catch (error) {
|
|
18
|
+
unstable_rethrow(error);
|
|
19
|
+
console.warn(`[public-entity-cache] could not expire the public pages of ${config.slug}: ${error instanceof Error ? error.message : error}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
PUBLIC_ROUTE_GROUP,
|
|
24
|
+
expirePublicEntity
|
|
25
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.config.d.ts","sourceRoot":"","sources":["../../../src/lib/config/app.config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,SAAS,CAAA;AAOzD,eAAO,MAAM,kBAAkB,EAAE,
|
|
1
|
+
{"version":3,"file":"app.config.d.ts","sourceRoot":"","sources":["../../../src/lib/config/app.config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,SAAS,CAAA;AAOzD,eAAO,MAAM,kBAAkB,EAAE,SA6gBhC,CAAA"}
|
|
@@ -337,14 +337,13 @@ const DEFAULT_APP_CONFIG = {
|
|
|
337
337
|
*/
|
|
338
338
|
methods: ["email-otp", "google"],
|
|
339
339
|
/**
|
|
340
|
-
* Better Auth's email + password endpoints
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
340
|
+
* Better Auth's email + password endpoints. Left undeclared, they follow
|
|
341
|
+
* `methods`: on when it lists 'email-password', and always outside
|
|
342
|
+
* production (seeded test users, the dev keyring). Set `enabled: true` to
|
|
343
|
+
* keep password logins (API clients, test users) in a passwordless
|
|
344
|
+
* production app, or `false` to turn them off everywhere.
|
|
344
345
|
*/
|
|
345
|
-
emailAndPassword: {
|
|
346
|
-
enabled: true
|
|
347
|
-
},
|
|
346
|
+
emailAndPassword: {},
|
|
348
347
|
/**
|
|
349
348
|
* Whether Better Auth automatically sends the verification email on signup.
|
|
350
349
|
*
|
|
@@ -532,18 +532,20 @@ export interface AuthConfig {
|
|
|
532
532
|
* auth: { methods: ['email-otp', 'email-password', 'google'] } // both, OTP first
|
|
533
533
|
* ```
|
|
534
534
|
*
|
|
535
|
-
* This
|
|
536
|
-
* endpoints
|
|
535
|
+
* This shapes the UI and the signup page, and decides whether Better Auth's
|
|
536
|
+
* password endpoints run in production (see `emailAndPassword`).
|
|
537
537
|
*/
|
|
538
538
|
methods?: AuthLoginMethod[];
|
|
539
539
|
/**
|
|
540
540
|
* Server-side switch for Better Auth's email + password endpoints
|
|
541
541
|
* (`sign-in/email`, `sign-up/email`, forget/reset/change-password).
|
|
542
542
|
*
|
|
543
|
-
* Default: `
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
*
|
|
543
|
+
* Default (undeclared): on when `methods` lists `'email-password'`, and
|
|
544
|
+
* outside production (`NODE_ENV !== 'production'`) so the dev keyring,
|
|
545
|
+
* `db:seed` users and Cypress can sign in with a password. A production app
|
|
546
|
+
* on the passwordless preset serves no password endpoint. Set
|
|
547
|
+
* `{ enabled: true }` to keep password logins (API clients, test users) in
|
|
548
|
+
* production, or `{ enabled: false }` to turn them off everywhere.
|
|
547
549
|
*/
|
|
548
550
|
emailAndPassword?: {
|
|
549
551
|
enabled?: boolean;
|