@lenne.tech/nest-server 11.27.1 → 11.27.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/.claude/rules/testing.md +5 -4
- package/CLAUDE.md +2 -2
- package/FRAMEWORK-API.md +1 -1
- package/dist/core/modules/better-auth/better-auth.config.d.ts +1 -0
- package/dist/core/modules/better-auth/better-auth.config.js +14 -2
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/migration-guides/11.27.1-to-11.27.2.md +191 -0
- package/migration-guides/11.27.2-to-11.27.3.md +177 -0
- package/package.json +7 -3
- package/src/core/modules/better-auth/better-auth.config.ts +52 -4
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Migration Guide: 11.27.1 → 11.27.2
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
| Category | Details |
|
|
6
|
+
|----------|---------|
|
|
7
|
+
| **Breaking Changes** | None |
|
|
8
|
+
| **New Features** | None |
|
|
9
|
+
| **Bugfixes** | `scripts/check.mjs` no longer hides devDependency vulnerabilities for library packages. The audit step now runs the chain's own audit command verbatim (same scope / `--prod` flag / `--audit-level`) instead of a hardcoded `pnpm audit --prod --json`. Repo-internal tooling — no consumer impact. |
|
|
10
|
+
| **Security** | 3 new / refreshed `pnpm.overrides` entries — `undici` range widened to `<7.28.0`, plus new entries for `piscina <4.9.3` and `@babel/core <7.29.6`. All transitive via the build/test toolchain. |
|
|
11
|
+
| **Migration Effort** | 0 minutes (automatic) — `pnpm update` is enough. ~2 minutes optional to mirror the security overrides into your own project's `package.json`. |
|
|
12
|
+
|
|
13
|
+
This is a **maintenance release**. No source-code or config changes are required.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Quick Migration
|
|
18
|
+
|
|
19
|
+
No code changes required.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Update package
|
|
23
|
+
pnpm add @lenne.tech/nest-server@11.27.2
|
|
24
|
+
|
|
25
|
+
# Verify build
|
|
26
|
+
pnpm run build
|
|
27
|
+
|
|
28
|
+
# Run tests
|
|
29
|
+
pnpm test
|
|
30
|
+
|
|
31
|
+
# Re-run audit to confirm advisories cleared
|
|
32
|
+
pnpm audit
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## What's New in 11.27.2
|
|
38
|
+
|
|
39
|
+
### 1. Security overrides refreshed
|
|
40
|
+
|
|
41
|
+
`pnpm.overrides` in this repo's `package.json` gained two new entries and
|
|
42
|
+
widened one existing range. Every override targets a **fixed version** (per
|
|
43
|
+
`.claude/rules/package-management.md` override rules — no `>=` / `^` / `~`):
|
|
44
|
+
|
|
45
|
+
| Package | Override | Advisory | Pulled in via |
|
|
46
|
+
|---------|----------|----------|---------------|
|
|
47
|
+
| `undici` | `7.28.0` (was `7.25.0`) | Various CVEs `<7.28.0` (incl. high) | `@compodoc/compodoc > cheerio`, `better-auth` |
|
|
48
|
+
| `piscina` | `4.9.3` *(new)* | High CVE in `piscina <4.9.3` | build/test toolchain |
|
|
49
|
+
| `@babel/core` | `7.29.6` *(new)* | `@babel/core <7.29.6` advisory | `@compodoc/compodoc > @babel/preset-env` |
|
|
50
|
+
|
|
51
|
+
#### Why your project may still see CVEs after upgrading
|
|
52
|
+
|
|
53
|
+
`pnpm.overrides` is **scoped to the package that declares it**. The framework's
|
|
54
|
+
overrides do **not** propagate into consumer projects' lockfiles. If your own
|
|
55
|
+
`pnpm audit` flags `undici / piscina / @babel/core`, mirror the entries into
|
|
56
|
+
your project's `package.json`:
|
|
57
|
+
|
|
58
|
+
```jsonc
|
|
59
|
+
// projects/api/package.json (or your project's package.json)
|
|
60
|
+
"pnpm": {
|
|
61
|
+
"//overrides": {
|
|
62
|
+
"undici@>=7.0.0 <7.28.0": "Security: various CVEs <7.28.0 (incl. high) - transitive via @compodoc/compodoc>cheerio + better-auth",
|
|
63
|
+
"piscina@<4.9.3": "Security: high CVE in piscina <4.9.3 - transitive via the build/test toolchain",
|
|
64
|
+
"@babel/core@<7.29.6": "Security: @babel/core <7.29.6 advisory - transitive via @compodoc/compodoc>@babel/preset-env"
|
|
65
|
+
},
|
|
66
|
+
"overrides": {
|
|
67
|
+
"undici@>=7.0.0 <7.28.0": "7.28.0",
|
|
68
|
+
"piscina@<4.9.3": "4.9.3",
|
|
69
|
+
"@babel/core@<7.29.6": "7.29.6"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
After adding, run `pnpm install && pnpm audit && pnpm test`.
|
|
75
|
+
|
|
76
|
+
### 2. `scripts/check.mjs` audit step is now chain-faithful
|
|
77
|
+
|
|
78
|
+
The audit step in the `check.mjs` wrapper previously hardcoded
|
|
79
|
+
`pnpm audit --prod --json` with a separate gate level extracted from the
|
|
80
|
+
chain's `--audit-level` flag. For library packages where dev-only dependencies
|
|
81
|
+
ship vulnerabilities, this **masked the vulnerabilities** that a bare
|
|
82
|
+
`pnpm audit` would have surfaced — because `--prod` excludes devDependencies.
|
|
83
|
+
|
|
84
|
+
The audit step now runs the **chain's own audit command verbatim** (same scope,
|
|
85
|
+
same `--prod` flag if any, same `--audit-level` if any), only appending `--json`
|
|
86
|
+
for the structured counts. The blocking gate becomes the command's own exit
|
|
87
|
+
code, so `check` blocks **precisely when a bare `<auditCmd>` would** —
|
|
88
|
+
never with a narrower scope than the chain.
|
|
89
|
+
|
|
90
|
+
What this means in practice:
|
|
91
|
+
|
|
92
|
+
- **Library packages** (this repo, plus any consumer that runs `pnpm audit` —
|
|
93
|
+
not `pnpm audit --prod` — in their `check` script) now see devDep vulns
|
|
94
|
+
surface in the wrapper run, matching what their bare audit command reports.
|
|
95
|
+
- **Application packages** that use `pnpm audit --prod` keep the exact same
|
|
96
|
+
behaviour — their scope is unchanged.
|
|
97
|
+
- The status header now shows the **full audit command** (e.g.
|
|
98
|
+
`audit: pnpm audit --audit-level=high`) instead of just `audit gate: high`,
|
|
99
|
+
so the gate is auditable at a glance.
|
|
100
|
+
|
|
101
|
+
If your `check` chain has no audit step at all, the wrapper now correctly
|
|
102
|
+
**skips** the audit phase (previously it ran a hardcoded one anyway).
|
|
103
|
+
|
|
104
|
+
> **Consumer impact:** None unless you copied `scripts/check.mjs` into your
|
|
105
|
+
> own project from 11.27.1. If you did, the upgrade fixes a silent gap in your
|
|
106
|
+
> security gate — review the next `pnpm run check` for previously hidden
|
|
107
|
+
> devDep findings.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Breaking Changes
|
|
112
|
+
|
|
113
|
+
None.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Compatibility Notes
|
|
118
|
+
|
|
119
|
+
- **`IServerOptions` / `CoreModule.forRoot()`:** unchanged.
|
|
120
|
+
- **`FRAMEWORK-API.md`:** regenerated for the new version stamp only — no API
|
|
121
|
+
surface differences vs. 11.27.1.
|
|
122
|
+
- **`config.env.ts`:** no new fields, no deprecations.
|
|
123
|
+
- **Vendor-mode consumers:** nothing to sync. The override changes live in
|
|
124
|
+
`package.json` at the **repo root**, which is excluded from
|
|
125
|
+
`convertCloneToVendored`. The `scripts/check.mjs` wrapper also lives outside
|
|
126
|
+
`src/core/`, so vendor projects keep their existing `check` setup.
|
|
127
|
+
- **Custom controllers / resolvers extending Core* classes:** no impact —
|
|
128
|
+
no Core method signatures changed.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Verifying
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# 1. Update + install
|
|
136
|
+
pnpm add @lenne.tech/nest-server@11.27.2
|
|
137
|
+
pnpm install
|
|
138
|
+
|
|
139
|
+
# 2. Confirm advisories cleared (or only show unaffected residuals)
|
|
140
|
+
pnpm audit
|
|
141
|
+
|
|
142
|
+
# 3. Smoke-test the new check wrapper (only relevant in this repo)
|
|
143
|
+
pnpm run check # quiet, report-driven — audit now mirrors the chain
|
|
144
|
+
pnpm run check:raw # original chain, full noise
|
|
145
|
+
|
|
146
|
+
# 4. Tests + build
|
|
147
|
+
pnpm test
|
|
148
|
+
pnpm run build
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Troubleshooting
|
|
154
|
+
|
|
155
|
+
### `pnpm audit` in my consumer project still reports `undici` / `piscina` / `@babel/core`
|
|
156
|
+
|
|
157
|
+
Expected — `pnpm.overrides` is scoped to the declaring package. Mirror the
|
|
158
|
+
entries listed under [Why your project may still see CVEs after upgrading](#why-your-project-may-still-see-cves-after-upgrading).
|
|
159
|
+
|
|
160
|
+
### `pnpm run check` suddenly reports more vulnerabilities than 11.27.1
|
|
161
|
+
|
|
162
|
+
Working as intended — the previous wrapper silently narrowed the audit to
|
|
163
|
+
`--prod` scope. The new wrapper mirrors your chain's own audit command, so
|
|
164
|
+
devDep vulnerabilities a bare `pnpm audit` would have flagged now appear in
|
|
165
|
+
the wrapper run. Either:
|
|
166
|
+
|
|
167
|
+
- Mirror the relevant overrides from this guide (recommended), or
|
|
168
|
+
- Narrow your chain's audit command to `pnpm audit --prod --audit-level=high`
|
|
169
|
+
if devDep vulns are out of scope for your gate.
|
|
170
|
+
|
|
171
|
+
### Audit step is skipped — "audit: none" in the header
|
|
172
|
+
|
|
173
|
+
Your `check` chain has no audit step. Add `pnpm audit` (or
|
|
174
|
+
`pnpm audit --prod --audit-level=high`) at the start of the chain in
|
|
175
|
+
`package.json` to re-enable the gate.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Module Documentation
|
|
180
|
+
|
|
181
|
+
No module documentation changes in this release. Reference documentation:
|
|
182
|
+
|
|
183
|
+
- **Package management rules:** [`.claude/rules/package-management.md`](../.claude/rules/package-management.md) — fixed-version overrides only, never ranges
|
|
184
|
+
- **Framework compatibility:** [`.claude/rules/framework-compatibility.md`](../.claude/rules/framework-compatibility.md) — what ships in the npm package
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## References
|
|
189
|
+
|
|
190
|
+
- [Migration Guide 11.27.0 → 11.27.1](./11.27.0-to-11.27.1.md) — Previous release (initial `scripts/check.mjs` introduction + 7 security overrides)
|
|
191
|
+
- [nest-server-starter](https://github.com/lenneTech/nest-server-starter) — reference implementation
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Migration Guide: 11.27.2 → 11.27.3
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
| Category | Details |
|
|
6
|
+
|----------|---------|
|
|
7
|
+
| **Breaking Changes** | None |
|
|
8
|
+
| **New Features** | None (one new internal helper export: `sendAuthEmailSafely`) |
|
|
9
|
+
| **Bugfixes** | A failed verification-email send (e.g. SMTP not configured, delivery failure) no longer crashes the Node process via an unhandled promise rejection. The send stays fire-and-forget (timing-attack mitigation) but every failure — sync throw or async rejection — is now caught and logged instead. |
|
|
10
|
+
| **Migration Effort** | 0 minutes (automatic) — `pnpm update` is enough. |
|
|
11
|
+
|
|
12
|
+
This is a **stability bugfix release**. No source-code or config changes are
|
|
13
|
+
required in consuming projects.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Quick Migration
|
|
18
|
+
|
|
19
|
+
No code changes required.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Update package
|
|
23
|
+
pnpm add @lenne.tech/nest-server@11.27.3
|
|
24
|
+
|
|
25
|
+
# Verify build
|
|
26
|
+
pnpm run build
|
|
27
|
+
|
|
28
|
+
# Run tests
|
|
29
|
+
pnpm test
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## What's Fixed in 11.27.3
|
|
35
|
+
|
|
36
|
+
### Process crash on failed verification-email send
|
|
37
|
+
|
|
38
|
+
**The bug:** BetterAuth's `emailVerification.sendVerificationEmail` callback
|
|
39
|
+
invoked the email send fire-and-forget (intentionally not awaited, per
|
|
40
|
+
Better-Auth's recommendation, so response timing cannot leak whether an
|
|
41
|
+
account exists). But the detached promise had **no rejection handler**. When
|
|
42
|
+
the send failed — SMTP not configured, provider outage, delivery rejection —
|
|
43
|
+
the rejection became an *unhandled promise rejection*, which terminates the
|
|
44
|
+
Node process under Node's default `--unhandled-rejections=throw` (Node ≥ 15).
|
|
45
|
+
|
|
46
|
+
**Observed trigger:** signing up an unverified user and then signing in
|
|
47
|
+
(`sendOnSignUp` / `sendOnSignIn` are enabled by default) while no working
|
|
48
|
+
SMTP transport was configured took the whole API down.
|
|
49
|
+
|
|
50
|
+
**The fix:** sends are now routed through a small hardened wrapper:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// src/core/modules/better-auth/better-auth.config.ts
|
|
54
|
+
export function sendAuthEmailSafely(send: () => unknown, onError: (error: unknown) => void): void {
|
|
55
|
+
void Promise.resolve()
|
|
56
|
+
.then(send)
|
|
57
|
+
.catch((error) => {
|
|
58
|
+
try {
|
|
59
|
+
onError(error);
|
|
60
|
+
} catch {
|
|
61
|
+
// A throwing error handler must not crash the process either.
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Behavior after the fix:
|
|
68
|
+
|
|
69
|
+
- The send remains **non-blocking** — response timing is unchanged
|
|
70
|
+
(timing-attack mitigation preserved).
|
|
71
|
+
- Sync throws and async rejections are both caught and logged via the NestJS
|
|
72
|
+
logger (context `BetterAuthConfig`), including the **stack trace**:
|
|
73
|
+
`Failed to send verification email: <message>`.
|
|
74
|
+
- Even a throwing error handler (e.g. a custom logger transport failing)
|
|
75
|
+
cannot re-introduce the crash.
|
|
76
|
+
- The API response to the client is unchanged (it never reflected send
|
|
77
|
+
failures — this matches Better-Auth's own default behavior).
|
|
78
|
+
|
|
79
|
+
**Covered by regression tests:** `tests/unit/better-auth-email-safe.spec.ts`
|
|
80
|
+
(async rejection, sync throw, success path, non-blocking guarantee, no
|
|
81
|
+
unhandled rejection, throwing error handler).
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Breaking Changes
|
|
86
|
+
|
|
87
|
+
None.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Compatibility Notes
|
|
92
|
+
|
|
93
|
+
- **`IServerOptions` / `CoreModule.forRoot()`:** unchanged.
|
|
94
|
+
- **`SendVerificationEmailCallback` contract:** unchanged (`{ token, url, user }`
|
|
95
|
+
→ `Promise<void>`). Projects supplying their own callback need no changes —
|
|
96
|
+
their callback is now additionally protected by the wrapper.
|
|
97
|
+
- **Monitoring / operations:** if you relied on the process crash (supervisor
|
|
98
|
+
restart loops) as an implicit signal for a broken mail setup, switch your
|
|
99
|
+
alerting to the error log line `Failed to send verification email: …`
|
|
100
|
+
(logger context `BetterAuthConfig`). Note that the email-verification
|
|
101
|
+
service additionally logs delivery failures with a masked recipient address
|
|
102
|
+
before rethrowing — a real outage produces both lines.
|
|
103
|
+
- **Workarounds can be removed:** a custom `process.on('unhandledRejection')`
|
|
104
|
+
handler added to a project's `main.ts` solely to survive this crash is no
|
|
105
|
+
longer needed for this path.
|
|
106
|
+
- **Better-Auth standard mechanism:** the framework deliberately detaches only
|
|
107
|
+
the sends it owns instead of enabling Better-Auth's global
|
|
108
|
+
`advanced.backgroundTasks` option (which would detach ALL background tasks —
|
|
109
|
+
OTP, invites, password reset — and route failures to Better-Auth's internal
|
|
110
|
+
logger instead of the NestJS logger). Projects that want the native
|
|
111
|
+
mechanism can still enable it via the `betterAuth.options.advanced`
|
|
112
|
+
passthrough.
|
|
113
|
+
- **Vendor-mode consumers:** the fix lives in
|
|
114
|
+
`src/core/modules/better-auth/better-auth.config.ts` and is picked up by the
|
|
115
|
+
next core sync (`/lt-dev:backend:update-nest-server-core`). The regression
|
|
116
|
+
test file (`tests/unit/`) is framework-repo-only and is not part of the
|
|
117
|
+
vendored file set.
|
|
118
|
+
- **Custom controllers / resolvers extending Core\* classes:** no impact — no
|
|
119
|
+
Core method signatures changed.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Troubleshooting
|
|
124
|
+
|
|
125
|
+
### Verification emails silently don't arrive after updating
|
|
126
|
+
|
|
127
|
+
Nothing regressed — before this fix the same situation crashed the API
|
|
128
|
+
instead. Check the logs for `Failed to send verification email: …`
|
|
129
|
+
(context `BetterAuthConfig`) and fix the underlying SMTP/Brevo configuration
|
|
130
|
+
(`email.smtp` / provider settings in `config.env.ts`).
|
|
131
|
+
|
|
132
|
+
### I see two error log lines for one failed send
|
|
133
|
+
|
|
134
|
+
Expected. The email-verification service logs the delivery failure with a
|
|
135
|
+
masked recipient address and rethrows; the wrapper logs the same failure once
|
|
136
|
+
more (with stack trace) as the final safety net. The wrapper line additionally
|
|
137
|
+
covers failures thrown outside the service's own try/catch.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Repo-Internal Tooling Changes (no consumer action)
|
|
142
|
+
|
|
143
|
+
These affect only this repository's development workflow — listed here because
|
|
144
|
+
some projects copied `scripts/check.mjs` from 11.27.1+:
|
|
145
|
+
|
|
146
|
+
- **`scripts/check.mjs` idle watchdog:** a step whose child produces no output
|
|
147
|
+
for 300s (configurable via `--idle-timeout=<seconds>` / `CHECK_IDLE_TIMEOUT`,
|
|
148
|
+
`0` disables) is now killed — including its whole process tree — and the
|
|
149
|
+
check fails with a clear `[watchdog]` reason. Previously a deadlocked test
|
|
150
|
+
run (workers idle at 0% CPU) spun the live view forever. If you copied
|
|
151
|
+
`check.mjs` into your project, update your copy to get the watchdog.
|
|
152
|
+
- **Per-run test databases:** `tests/global-setup.ts` now gives every vitest
|
|
153
|
+
run a unique database (`<base>-run-<timestamp>-p<pid>`) instead of dropping
|
|
154
|
+
a shared fixed-name DB — concurrent runs (second terminal, IDE runner) can
|
|
155
|
+
no longer wipe each other's users/sessions mid-flight. A new reporter
|
|
156
|
+
(`tests/db-lifecycle.reporter.ts`) drops the DB after a successful run and
|
|
157
|
+
collects stale leftovers; after a failed run the DB is kept for debugging
|
|
158
|
+
until the next successful run. An externally set `MONGODB_URI` (CI) keeps
|
|
159
|
+
the previous behavior.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Module Documentation
|
|
164
|
+
|
|
165
|
+
### BetterAuth
|
|
166
|
+
|
|
167
|
+
- **README:** [src/core/modules/better-auth/README.md](../src/core/modules/better-auth/README.md)
|
|
168
|
+
- **Integration Checklist:** [src/core/modules/better-auth/INTEGRATION-CHECKLIST.md](../src/core/modules/better-auth/INTEGRATION-CHECKLIST.md)
|
|
169
|
+
- **Key File:** `src/core/modules/better-auth/better-auth.config.ts` —
|
|
170
|
+
`sendAuthEmailSafely()` helper + `buildEmailVerificationConfig()` wiring
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## References
|
|
175
|
+
|
|
176
|
+
- [Migration Guide 11.27.1 → 11.27.2](./11.27.1-to-11.27.2.md) — Previous release (chain-faithful audit step + security overrides)
|
|
177
|
+
- [nest-server-starter](https://github.com/lenneTech/nest-server-starter) — reference implementation
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "11.27.
|
|
3
|
+
"version": "11.27.3",
|
|
4
4
|
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node",
|
|
@@ -192,7 +192,9 @@
|
|
|
192
192
|
"minimatch@>=10.0.0 <10.2.5": "Security: RegExp DoS - transitive via @nestjs/apollo>ts-morph>@ts-morph/common and nodemon",
|
|
193
193
|
"ajv@<6.14.0": "Security: prototype pollution - transitive via @getbrevo/brevo>rewire>eslint",
|
|
194
194
|
"ajv@>=7.0.0-alpha.0 <8.18.0": "Security: prototype pollution - transitive via @nestjs/cli>@angular-devkit",
|
|
195
|
-
"undici@>=7.0.0 <7.
|
|
195
|
+
"undici@>=7.0.0 <7.28.0": "Security: various CVEs <7.28.0 (incl. high) - transitive via @compodoc/compodoc>cheerio + better-auth",
|
|
196
|
+
"piscina@<4.9.3": "Security: high CVE in piscina <4.9.3 - transitive via the build/test toolchain",
|
|
197
|
+
"@babel/core@<7.29.6": "Security: @babel/core <7.29.6 advisory - transitive via @compodoc/compodoc>@babel/preset-env",
|
|
196
198
|
"handlebars@>=4.0.0 <4.7.9": "Security: prototype pollution (GHSA-q42p-pg8m-cqh6) - transitive via @compodoc/compodoc",
|
|
197
199
|
"brace-expansion@<1.1.13": "Security: RegExp DoS - transitive via eslint>minimatch",
|
|
198
200
|
"brace-expansion@>=4.0.0 <5.0.6": "Security: RegExp DoS - Large numeric range defeats brace expansion (GHSA-jxxr-4gwj-5jf2) - transitive via nodemon>minimatch and @ts-morph/common>minimatch and @compodoc/compodoc>glob>minimatch",
|
|
@@ -225,7 +227,9 @@
|
|
|
225
227
|
"minimatch@>=10.0.0 <10.2.5": "10.2.5",
|
|
226
228
|
"ajv@<6.14.0": "6.14.0",
|
|
227
229
|
"ajv@>=7.0.0-alpha.0 <8.18.0": "8.18.0",
|
|
228
|
-
"undici@>=7.0.0 <7.
|
|
230
|
+
"undici@>=7.0.0 <7.28.0": "7.28.0",
|
|
231
|
+
"piscina@<4.9.3": "4.9.3",
|
|
232
|
+
"@babel/core@<7.29.6": "7.29.6",
|
|
229
233
|
"handlebars@>=4.0.0 <4.7.9": "4.7.9",
|
|
230
234
|
"brace-expansion@<1.1.13": "1.1.13",
|
|
231
235
|
"brace-expansion@>=4.0.0 <5.0.6": "5.0.6",
|
|
@@ -15,6 +15,11 @@ import { detectCookiePrefixDrift, resolveBetterAuthCookiePrefix } from './better
|
|
|
15
15
|
*/
|
|
16
16
|
export type BetterAuthInstance = ReturnType<typeof betterAuth>;
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Shared logger for all functions in this config module
|
|
20
|
+
*/
|
|
21
|
+
const logger = new Logger('BetterAuthConfig');
|
|
22
|
+
|
|
18
23
|
// ---------------------------------------------------------------------------
|
|
19
24
|
// Performance-optimized password hashing using Node.js native crypto.scrypt
|
|
20
25
|
//
|
|
@@ -174,6 +179,40 @@ export type SendVerificationEmailCallback = (options: {
|
|
|
174
179
|
user: { email: string; id: string; name?: null | string };
|
|
175
180
|
}) => Promise<void>;
|
|
176
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Invoke an auth-email send (verification / password-reset) fire-and-forget.
|
|
184
|
+
*
|
|
185
|
+
* Better-Auth recommends NOT awaiting these sends so the response time does not
|
|
186
|
+
* leak whether an account exists (timing attack). The catch here is essential:
|
|
187
|
+
* a rejected send (e.g. SMTP not configured / delivery failure) must never
|
|
188
|
+
* become an unhandled promise rejection — that crashes the Node process (an
|
|
189
|
+
* unverified sign-up + sign-in took the whole API down in dev). This wrapper
|
|
190
|
+
* keeps the send non-blocking while routing every failure (sync throw or async
|
|
191
|
+
* rejection) to `onError` instead. `onError` itself is guarded too: if the
|
|
192
|
+
* handler throws, the error is swallowed rather than crashing the process.
|
|
193
|
+
*
|
|
194
|
+
* Deliberate divergence from Better-Auth's own mechanism: Better-Auth awaits
|
|
195
|
+
* these callbacks via `runInBackgroundOrAwait` and offers
|
|
196
|
+
* `advanced.backgroundTasks` as its native non-blocking path. That option is
|
|
197
|
+
* global (it detaches ALL background tasks — OTP, invites, password reset, …)
|
|
198
|
+
* and routes failures to Better-Auth's internal logger instead of the NestJS
|
|
199
|
+
* logger, so this wrapper detaches only the sends the framework owns. Projects
|
|
200
|
+
* can still opt into `advanced.backgroundTasks` via the `options` passthrough.
|
|
201
|
+
*/
|
|
202
|
+
export function sendAuthEmailSafely(send: () => unknown, onError: (error: unknown) => void): void {
|
|
203
|
+
void Promise.resolve()
|
|
204
|
+
.then(send)
|
|
205
|
+
.catch((error) => {
|
|
206
|
+
try {
|
|
207
|
+
onError(error);
|
|
208
|
+
} catch {
|
|
209
|
+
// Last resort: the error handler itself failed. Swallowing here keeps
|
|
210
|
+
// the no-unhandled-rejection guarantee — a throwing handler must not
|
|
211
|
+
// crash the process this helper exists to protect.
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
177
216
|
/**
|
|
178
217
|
* Better-Auth field type definition
|
|
179
218
|
* Matches the DBFieldType from better-auth
|
|
@@ -264,7 +303,6 @@ export interface CreateBetterAuthResult {
|
|
|
264
303
|
}
|
|
265
304
|
|
|
266
305
|
export function createBetterAuthInstance(options: CreateBetterAuthOptions): CreateBetterAuthResult | null {
|
|
267
|
-
const logger = new Logger('BetterAuthConfig');
|
|
268
306
|
const { config, db, fallbackSecrets, onEmailVerified, sendVerificationEmail, serverEnv } = options;
|
|
269
307
|
|
|
270
308
|
// Return null only if better-auth is explicitly disabled
|
|
@@ -495,9 +533,19 @@ function buildEmailVerificationConfig(
|
|
|
495
533
|
data: { token: string; url: string; user: { email: string; id: string; name?: null | string } },
|
|
496
534
|
_request?: Request,
|
|
497
535
|
) => {
|
|
498
|
-
//
|
|
499
|
-
|
|
500
|
-
|
|
536
|
+
// Fire-and-forget (timing-attack mitigation, per Better-Auth docs) — but a
|
|
537
|
+
// failed send must be logged, never crash the process (see sendAuthEmailSafely).
|
|
538
|
+
// Note: delivery failures are also logged (with masked recipient) by the
|
|
539
|
+
// email-verification service before rethrowing; this line additionally
|
|
540
|
+
// covers failures thrown outside the service's own try/catch.
|
|
541
|
+
sendAuthEmailSafely(
|
|
542
|
+
() => sendVerificationEmail(data),
|
|
543
|
+
(error) =>
|
|
544
|
+
logger.error(
|
|
545
|
+
`Failed to send verification email: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
546
|
+
error instanceof Error ? error.stack : undefined,
|
|
547
|
+
),
|
|
548
|
+
);
|
|
501
549
|
};
|
|
502
550
|
}
|
|
503
551
|
|