knowless 0.1.8 → 0.1.10
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/CHANGELOG.md +68 -0
- package/GUIDE.md +86 -16
- package/OPS.md +48 -0
- package/README.md +1 -1
- package/knowless.context.md +9 -7
- package/package.json +1 -1
- package/src/index.js +7 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,75 @@ Versioning is [SemVer](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
- **Turnkey Docker image** (`knowless/knowless-server:0.2.x`)
|
|
11
|
+
bundling Postfix + null-route + the binary so a self-hoster
|
|
12
|
+
runs `docker compose up` and has a working auth gateway in
|
|
13
|
+
one step. Material UX win for the PRD §4.2 self-hoster
|
|
14
|
+
audience. Targeted for v0.2.0.
|
|
10
15
|
- Caddy forward-auth Docker integration test (TASKS.md 6.8).
|
|
16
|
+
- `knowless-server --check-null-route`: CLI probe that submits a
|
|
17
|
+
test message to `shamRecipient` and confirms the local MTA
|
|
18
|
+
discarded it. Targeted for v0.2.0.
|
|
19
|
+
|
|
20
|
+
## [0.1.10] — 2026-04-28
|
|
21
|
+
|
|
22
|
+
addypin manual smoke continued. Two DX docs improvements; no code
|
|
23
|
+
changes.
|
|
24
|
+
|
|
25
|
+
### Documentation
|
|
26
|
+
|
|
27
|
+
- **GUIDE: "Local development setup" section (AF-16).** Covers the
|
|
28
|
+
five flags that turn knowless from "production-tuned, defensive"
|
|
29
|
+
to "developer-friendly, get-out-of-my-way" — `cookieSecure: false`,
|
|
30
|
+
`devLogMagicLinks: true`, `maxLoginRequestsPerIpPerHour: 0`,
|
|
31
|
+
`maxNewHandlesPerIpPerHour: 0`, `openRegistration: true`. Each
|
|
32
|
+
flag explained with what it solves and a sharp warning about
|
|
33
|
+
shipping it. Considered auto-disabling rate limits whenever
|
|
34
|
+
`devLogMagicLinks: true` to save typing, but rejected the
|
|
35
|
+
coupling — operators turning on `devLogMagicLinks` briefly to
|
|
36
|
+
debug a single email in prod should NOT have rate limits silently
|
|
37
|
+
dropped at the same time.
|
|
38
|
+
- **GUIDE: silent-miss debug line is now promoted as a feature
|
|
39
|
+
(AF-17).** The `[knowless dev:<from>] silent-miss: handle for
|
|
40
|
+
"X" does not exist (openRegistration=false)` stderr hint
|
|
41
|
+
introduced in AF-7.2 was buried in the CHANGELOG; it now leads
|
|
42
|
+
the dev-setup section. First-time closed-reg friction was costing
|
|
43
|
+
every adopter the same ~30 min; the hint cuts that to seconds
|
|
44
|
+
but only if you know it exists.
|
|
45
|
+
|
|
46
|
+
## [0.1.9] — 2026-04-28
|
|
47
|
+
|
|
48
|
+
addypin manual smoke turned up one real bug, one defaults footgun,
|
|
49
|
+
and one DX gap.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- **`auth.deriveHandle(email)` now normalizes the email before
|
|
54
|
+
HMAC (AF-13).** Prior versions skipped `normalize()` while
|
|
55
|
+
`auth.startLogin` and `POST /login` ran it — adopters using
|
|
56
|
+
`deriveHandle` to precompute owner-keyed lookups got silent
|
|
57
|
+
handle mismatches whenever email casing varied between
|
|
58
|
+
create-time and click-time. Symptom was "user's records
|
|
59
|
+
disappear after login," which is awful to debug. The bare
|
|
60
|
+
`deriveHandle(emailNormalized, secret)` re-export still
|
|
61
|
+
expects pre-normalized input — that contract is unchanged.
|
|
62
|
+
|
|
63
|
+
### Documentation
|
|
64
|
+
|
|
65
|
+
- **GUIDE flags the `failureRedirect` Mode-A footgun (AF-14).**
|
|
66
|
+
Adopters running programmatic-only (`startLogin` without
|
|
67
|
+
mounting `loginForm`) hit a default `failureRedirect = /login`
|
|
68
|
+
pointing at a route they don't serve — expired/replayed
|
|
69
|
+
magic-link clicks 302 to a 404. The GUIDE now leads with this
|
|
70
|
+
in the Mode-A walkthrough and adds a callout in the config
|
|
71
|
+
table. Default unchanged to avoid breaking Mode-B users with
|
|
72
|
+
custom paths.
|
|
73
|
+
- **OPS.md §11b — MailHog dev workflow (AF-15).** `docker run
|
|
74
|
+
mailhog/mailhog`, point knowless at port 1025, inspect every
|
|
75
|
+
outgoing mail (including sham submissions) in a UI at port
|
|
76
|
+
8025. Verifies `bodyFooter`, `subjectOverride`, and the
|
|
77
|
+
URL-line-isn't-QP-soft-broken invariant without spinning up
|
|
78
|
+
real Postfix.
|
|
11
79
|
|
|
12
80
|
## [0.1.8] — 2026-04-28
|
|
13
81
|
|
package/GUIDE.md
CHANGED
|
@@ -62,8 +62,8 @@ built-in auth is either missing or weak. The existing alternatives
|
|
|
62
62
|
(Authelia, Authentik, Keycloak, oauth2-proxy) are heavyweight for
|
|
63
63
|
the job: "redirect to login if no cookie, otherwise let through."
|
|
64
64
|
|
|
65
|
-
knowless's standalone server (
|
|
66
|
-
Caddy / nginx / Traefik via forward-auth. One auth subdomain, one
|
|
65
|
+
knowless's standalone server (`bin/knowless-server`, shipped in
|
|
66
|
+
v0.1.3) sits behind Caddy / nginx / Traefik via forward-auth. One auth subdomain, one
|
|
67
67
|
session cookie scoped to the parent eTLD+1, SSO across all your
|
|
68
68
|
services for free.
|
|
69
69
|
|
|
@@ -106,7 +106,7 @@ nothing else*.
|
|
|
106
106
|
- **Walks away at v1.0.0.** Maintenance mode (security patches +
|
|
107
107
|
bug fixes) after that, by design.
|
|
108
108
|
|
|
109
|
-
## Walkthrough: library mode
|
|
109
|
+
## Walkthrough: library mode
|
|
110
110
|
|
|
111
111
|
The shape: import `knowless`, configure it, mount the handlers on
|
|
112
112
|
your HTTP framework.
|
|
@@ -175,7 +175,9 @@ sending domain):
|
|
|
175
175
|
Without all three, Gmail / Outlook will silently drop your auth
|
|
176
176
|
mail. This is the operator commitment knowless asks of you.
|
|
177
177
|
|
|
178
|
-
> Full Postfix walkthrough lives in `OPS.md`
|
|
178
|
+
> Full Postfix walkthrough lives in [`OPS.md`](OPS.md) — Postfix
|
|
179
|
+
> install, null-route, SPF/DKIM/PTR, systemd, reverse-proxy
|
|
180
|
+
> forward-auth examples, multi-process deployments.
|
|
179
181
|
|
|
180
182
|
### Step 4: Mount the handlers
|
|
181
183
|
|
|
@@ -295,7 +297,19 @@ callers. See SPEC §7.3a for the full contract.
|
|
|
295
297
|
|
|
296
298
|
`auth.deriveHandle(email)` returns the same opaque HMAC handle
|
|
297
299
|
that the form path uses, without you having to import the helper
|
|
298
|
-
or pass the secret around.
|
|
300
|
+
or pass the secret around. The instance method **normalizes the
|
|
301
|
+
email** (lowercase, trim) before HMAC (AF-13), so `Alice@X.com`
|
|
302
|
+
and `alice@x.com` produce the same handle — match what the form
|
|
303
|
+
and `startLogin` would compute. The bare `deriveHandle` re-export
|
|
304
|
+
takes pre-normalized input; use the instance method unless you
|
|
305
|
+
have a specific reason to call the lower-level primitive.
|
|
306
|
+
|
|
307
|
+
> **Mode-A heads-up: set `failureRedirect`.** If you only mount
|
|
308
|
+
> `auth.callback` (not `auth.loginForm`), the default
|
|
309
|
+
> `failureRedirect` cascade points at `/login` — a route you
|
|
310
|
+
> don't serve. An expired or replayed magic-link click will 302
|
|
311
|
+
> to a 404. Set `failureRedirect: '/'` (or any route you do
|
|
312
|
+
> serve) when wiring Mode A.
|
|
299
313
|
|
|
300
314
|
### Step 5: Pre-seed users (closed-registration mode, default)
|
|
301
315
|
|
|
@@ -384,6 +398,59 @@ reverse proxy gates upstreams via `/verify` returning 200/401 +
|
|
|
384
398
|
`handleFromRequest` — same answer, no sub-request round-trip, no
|
|
385
399
|
header parsing.
|
|
386
400
|
|
|
401
|
+
### Local development setup
|
|
402
|
+
|
|
403
|
+
Production defaults are tuned to bite bots, not to be friendly to a
|
|
404
|
+
developer hammering the same address from `127.0.0.1` for the
|
|
405
|
+
hundredth time. Use a dedicated dev config:
|
|
406
|
+
|
|
407
|
+
```js
|
|
408
|
+
const auth = knowless({
|
|
409
|
+
// ...required fields
|
|
410
|
+
cookieSecure: false, // localhost-only HTTP origins (AF-4.4)
|
|
411
|
+
devLogMagicLinks: true, // print magic links to stderr when SMTP fails (AF-6.2)
|
|
412
|
+
maxLoginRequestsPerIpPerHour: 0, // disable per-IP login cap
|
|
413
|
+
maxNewHandlesPerIpPerHour: 0, // disable per-IP create cap
|
|
414
|
+
openRegistration: true, // skip the pre-seeding step in dev
|
|
415
|
+
});
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Why each flag matters in dev:
|
|
419
|
+
|
|
420
|
+
- **`cookieSecure: false`** — without it, `http://localhost` browsers
|
|
421
|
+
reject the session cookie silently. The library logs a stderr
|
|
422
|
+
warning at startup so you can't accidentally ship this to prod.
|
|
423
|
+
- **`devLogMagicLinks: true`** — when SMTP is unreachable (no local
|
|
424
|
+
Postfix yet), magic-link URLs print to stderr tagged
|
|
425
|
+
`[knowless dev:<from>] magic link: ...`. Click straight from the
|
|
426
|
+
terminal. **Bonus diagnostic** (AF-7.2): on a sham/silent-miss
|
|
427
|
+
path, you get `[knowless dev:<from>] silent-miss: handle for
|
|
428
|
+
"X" does not exist (openRegistration=false)` instead — surfaces
|
|
429
|
+
the closed-reg gotcha that costs everyone the same 30 minutes
|
|
430
|
+
the first time.
|
|
431
|
+
- **`maxLoginRequestsPerIpPerHour: 0` and `maxNewHandlesPerIpPerHour:
|
|
432
|
+
0`** — disable per-IP rate caps. The defaults (30 / 3 per hour)
|
|
433
|
+
are sane for prod but shoot you in the foot during repeated test
|
|
434
|
+
runs. The counters **persist in the SQLite file** across process
|
|
435
|
+
restarts, so even rebooting the dev server doesn't clear them —
|
|
436
|
+
you'd have to delete the DB or wait an hour. Setting both to 0
|
|
437
|
+
in dev avoids the surprise.
|
|
438
|
+
- **`openRegistration: true`** — saves you from manually pre-seeding
|
|
439
|
+
every test email via `auth.deriveHandle` + your own store insert.
|
|
440
|
+
|
|
441
|
+
> **Don't ship this config.** Each of these flags weakens a specific
|
|
442
|
+
> defense. They are coupled to your environment, not to each other —
|
|
443
|
+
> intentionally. (We considered auto-disabling rate limits whenever
|
|
444
|
+
> `devLogMagicLinks` is true, but rejected: an operator turning on
|
|
445
|
+
> `devLogMagicLinks` to debug a single email in production should
|
|
446
|
+
> NOT have rate limits silently dropped at the same time.)
|
|
447
|
+
|
|
448
|
+
For end-to-end mail rendering checks (verify the `bodyFooter`,
|
|
449
|
+
inspect the magic-link line for QP soft-breaks, confirm the
|
|
450
|
+
right `subjectOverride` shipped), point dev knowless at MailHog
|
|
451
|
+
on `localhost:1025`. Setup walkthrough lives in
|
|
452
|
+
[`OPS.md` §11b](OPS.md).
|
|
453
|
+
|
|
387
454
|
### Step 7: GDPR right-to-erasure
|
|
388
455
|
|
|
389
456
|
The store interface exposes `deleteHandle(handle)` — atomic delete
|
|
@@ -402,11 +469,10 @@ Library doesn't ship a built-in HTTP endpoint for this — operator
|
|
|
402
469
|
chooses the UX (admin CLI, in-app self-service, ticket-driven
|
|
403
470
|
support).
|
|
404
471
|
|
|
405
|
-
## Walkthrough: standalone server mode
|
|
472
|
+
## Walkthrough: standalone server mode
|
|
406
473
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
magic-link login.
|
|
474
|
+
Run `npx knowless-server`, point Caddy / nginx / Traefik at it for
|
|
475
|
+
forward-auth, protect any HTTP service behind magic-link login.
|
|
410
476
|
|
|
411
477
|
The deployment-shape pattern:
|
|
412
478
|
```
|
|
@@ -418,7 +484,9 @@ The deployment-shape pattern:
|
|
|
418
484
|
[Caddy redirects to auth.example.com/login?next=...]
|
|
419
485
|
```
|
|
420
486
|
|
|
421
|
-
Sample Caddyfile (
|
|
487
|
+
Sample Caddyfile (full setup including TLS/ACME + multiple gated
|
|
488
|
+
services lives in [`OPS.md`](OPS.md) §7):
|
|
489
|
+
|
|
422
490
|
```caddy
|
|
423
491
|
auth.example.com {
|
|
424
492
|
reverse_proxy localhost:8080
|
|
@@ -427,7 +495,7 @@ auth.example.com {
|
|
|
427
495
|
kuma.example.com {
|
|
428
496
|
forward_auth localhost:8080 {
|
|
429
497
|
uri /verify
|
|
430
|
-
copy_headers X-
|
|
498
|
+
copy_headers X-Knowless-Handle
|
|
431
499
|
}
|
|
432
500
|
reverse_proxy localhost:3001 # Uptime Kuma
|
|
433
501
|
}
|
|
@@ -435,7 +503,7 @@ kuma.example.com {
|
|
|
435
503
|
adguard.example.com {
|
|
436
504
|
forward_auth localhost:8080 {
|
|
437
505
|
uri /verify
|
|
438
|
-
copy_headers X-
|
|
506
|
+
copy_headers X-Knowless-Handle
|
|
439
507
|
}
|
|
440
508
|
reverse_proxy localhost:3000 # AdGuard Home
|
|
441
509
|
}
|
|
@@ -444,9 +512,11 @@ adguard.example.com {
|
|
|
444
512
|
One auth subdomain, one cookie, SSO across all gated services
|
|
445
513
|
because the cookie is scoped to the parent eTLD+1.
|
|
446
514
|
|
|
447
|
-
|
|
448
|
-
`
|
|
449
|
-
`knowless
|
|
515
|
+
Configuration is via `KNOWLESS_*` env vars — see
|
|
516
|
+
[`config.example.env`](config.example.env) and run
|
|
517
|
+
`knowless-server --help` for the full list. `knowless-server
|
|
518
|
+
--config-check` validates your env, SMTP reachability, and DB
|
|
519
|
+
write access; suitable for systemd `ExecStartPre`.
|
|
450
520
|
|
|
451
521
|
## Configuration reference
|
|
452
522
|
|
|
@@ -479,7 +549,7 @@ Full options table:
|
|
|
479
549
|
| `trustedProxies` | no | `['127.0.0.1', '::1']` | IPs allowed to set `X-Forwarded-For`. |
|
|
480
550
|
| `shamRecipient` | no | `null@knowless.invalid` | Where sham mail goes (your MTA must discard it). |
|
|
481
551
|
| `sweepIntervalMs` | no | `300000` | Sweeper tick (5 min default). |
|
|
482
|
-
| `failureRedirect` | no | (= `loginPath`) | Where /auth/callback failures redirect. |
|
|
552
|
+
| `failureRedirect` | no | (= `loginPath`) | Where /auth/callback failures redirect. **Mode-A adopters:** if you don't mount `loginForm`, set this to a route you actually serve (e.g. `/`) — otherwise expired/replayed magic-link clicks 302 to a 404. |
|
|
483
553
|
| `store` | no | (built-in better-sqlite3) | Inject your own store implementation. |
|
|
484
554
|
| `mailer` | no | (built-in nodemailer) | Inject your own mailer. |
|
|
485
555
|
|
package/OPS.md
CHANGED
|
@@ -589,6 +589,54 @@ want for forward-auth-style deployments anyway.
|
|
|
589
589
|
|
|
590
590
|
---
|
|
591
591
|
|
|
592
|
+
## 11b. Local development without a real Postfix
|
|
593
|
+
|
|
594
|
+
Spinning up Postfix on a developer laptop just to inspect what
|
|
595
|
+
knowless sends is heavy. Two leaner options:
|
|
596
|
+
|
|
597
|
+
### Option A — `devLogMagicLinks: true` (no MTA needed)
|
|
598
|
+
|
|
599
|
+
knowless already supports this for the URL. When SMTP submission
|
|
600
|
+
fails AND `devLogMagicLinks: true` is set, the magic link is
|
|
601
|
+
printed to stderr tagged `[knowless dev:<from>]`. Sufficient for
|
|
602
|
+
smoke-testing the click flow, not the email content. AF-6.2.
|
|
603
|
+
|
|
604
|
+
### Option B — MailHog (visual UI for the rendered mail)
|
|
605
|
+
|
|
606
|
+
For verifying subject/body/footer rendering or the timing of
|
|
607
|
+
sham-vs-real submissions, run [MailHog](https://github.com/mailhog/MailHog)
|
|
608
|
+
or any compatible test SMTP catcher (e.g. mailpit, smtp4dev) on
|
|
609
|
+
the dev machine and point knowless at it.
|
|
610
|
+
|
|
611
|
+
```sh
|
|
612
|
+
# Docker one-liner for MailHog
|
|
613
|
+
docker run --rm -p 1025:1025 -p 8025:8025 mailhog/mailhog
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
Then in your dev knowless config:
|
|
617
|
+
|
|
618
|
+
```js
|
|
619
|
+
const auth = knowless({
|
|
620
|
+
secret: process.env.KNOWLESS_SECRET,
|
|
621
|
+
baseUrl: 'http://localhost:3000',
|
|
622
|
+
from: 'auth@dev.local',
|
|
623
|
+
smtpHost: 'localhost',
|
|
624
|
+
smtpPort: 1025, // MailHog's SMTP port
|
|
625
|
+
cookieSecure: false, // localhost-only dev
|
|
626
|
+
});
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
Open `http://localhost:8025` in your browser; every magic-link mail
|
|
630
|
+
(including sham submissions to `null@knowless.invalid`) shows up in
|
|
631
|
+
the inbox UI with full subject/body/headers visible. Perfect for
|
|
632
|
+
verifying `bodyFooter`, `subjectOverride`, the URL line is intact
|
|
633
|
+
without QP soft-breaks, etc.
|
|
634
|
+
|
|
635
|
+
> Don't ship MailHog into production. It accepts mail from anywhere
|
|
636
|
+
> and stores it forever — defeats the entire knowless threat model.
|
|
637
|
+
|
|
638
|
+
---
|
|
639
|
+
|
|
592
640
|
## 12. Backup and recovery
|
|
593
641
|
|
|
594
642
|
The only stateful file is the SQLite database (`KNOWLESS_DB_PATH`,
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ that don't need to email their users for anything but the sign-in link.
|
|
|
7
7
|
npm install knowless
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
> v0.1.
|
|
10
|
+
> v0.1.10 | Node.js >= 20 | 2 deps (nodemailer, better-sqlite3) | Apache-2.0
|
|
11
11
|
|
|
12
12
|
## What this is
|
|
13
13
|
|
package/knowless.context.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# knowless -- Integration Guide
|
|
2
2
|
|
|
3
3
|
> For AI assistants and developers wiring knowless into a project.
|
|
4
|
-
> v0.1.
|
|
4
|
+
> v0.1.10 | Node.js >= 20 | 2 deps (nodemailer, better-sqlite3) | Apache-2.0
|
|
5
5
|
|
|
6
6
|
## What this is
|
|
7
7
|
|
|
@@ -18,11 +18,13 @@ npm install knowless
|
|
|
18
18
|
|
|
19
19
|
Two integration paths:
|
|
20
20
|
|
|
21
|
-
1. **Library mode
|
|
22
|
-
mount five handlers on Express / Fastify / Hono / `node:http
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
1. **Library mode:** `import { knowless } from 'knowless'` --
|
|
22
|
+
mount five handlers on Express / Fastify / Hono / `node:http`,
|
|
23
|
+
gate your endpoints with `auth.handleFromRequest(req)`.
|
|
24
|
+
2. **Standalone server:** `npx knowless-server` -- forward-auth
|
|
25
|
+
gateway for Caddy / nginx / Traefik in front of no-auth services
|
|
26
|
+
like Uptime Kuma, AdGuard, Pi-hole. Configured via `KNOWLESS_*`
|
|
27
|
+
env vars; see [`OPS.md`](OPS.md) for the full deployment guide.
|
|
26
28
|
|
|
27
29
|
This document is the dense reference. For the why, see
|
|
28
30
|
`docs/01-product/PRD.md`. For the wire formats, see
|
|
@@ -148,7 +150,7 @@ const auth = knowless({
|
|
|
148
150
|
| `deleteHandle` | (handle: string) | void | Atomic delete of handle + tokens + sessions (FR-37a, GDPR) |
|
|
149
151
|
| `revokeSessions` | (handle: string) | number | Drops every session for `handle` without deleting the account ("log out everywhere"). Returns rows removed. AF-6.1. |
|
|
150
152
|
| `startLogin` | ({email, nextUrl?, sourceIp?, subjectOverride?, bypassRateLimit?}) | Promise\<{handle, submitted: true}\> | Programmatic magic-link send for "use first, claim later" flows. Same 12-step sham-work as form. `subjectOverride` (AF-9) replaces `cfg.subject` per call. `bypassRateLimit: true` (AF-10) opts trusted server-side callers (CLI, cron, worker) out of IP-rate-limit accounting. SPEC §7.3a. AF-7.3. |
|
|
151
|
-
| `deriveHandle` | (email: string) | string | HMAC-SHA256(secret, normalize(email)) using the configured secret.
|
|
153
|
+
| `deriveHandle` | (email: string) | string | `HMAC-SHA256(secret, normalize(email))` using the configured secret. Normalizes input (lowercase + trim) so `Alice@X.com` and `alice@x.com` produce the same handle. Match what `startLogin` and `POST /login` compute. AF-7.4 / AF-13. |
|
|
152
154
|
| `_sweep` | -- | void | Trigger one sweep tick on demand (tests, operator scripts). AF-5.3. |
|
|
153
155
|
| `config` | -- | object | Merged effective config; safe to read (do not mutate) |
|
|
154
156
|
| `close` | -- | void | Stops sweeper, closes mailer + store. Call on shutdown. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knowless",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Small, opinionated, full-stack passwordless auth for Node.js services that don't need to email their users for anything but the sign-in link.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createStore } from './store.js';
|
|
2
2
|
import { createMailer, validateBodyFooter } from './mailer.js';
|
|
3
3
|
import { createHandlers } from './handlers.js';
|
|
4
|
-
import { deriveHandle as deriveHandleRaw } from './handle.js';
|
|
4
|
+
import { deriveHandle as deriveHandleRaw, normalize } from './handle.js';
|
|
5
5
|
|
|
6
6
|
/** Default sweeper tick: 5 minutes. Per FR-13. */
|
|
7
7
|
const DEFAULT_SWEEP_INTERVAL_MS = 5 * 60 * 1000;
|
|
@@ -153,9 +153,12 @@ export function knowless(options = {}) {
|
|
|
153
153
|
* See SPEC §7.3a. AF-7.3. */
|
|
154
154
|
startLogin: handlers.startLogin,
|
|
155
155
|
/** Derive the opaque handle for an email using the configured
|
|
156
|
-
* secret.
|
|
157
|
-
*
|
|
158
|
-
|
|
156
|
+
* secret. Normalizes the email first (AF-13) so handles match
|
|
157
|
+
* what `auth.startLogin` and `POST /login` would compute for the
|
|
158
|
+
* same address typed with different casing or surrounding
|
|
159
|
+
* whitespace. Adopters should treat this as the canonical handle
|
|
160
|
+
* derivation. AF-7.4 / AF-13. */
|
|
161
|
+
deriveHandle: (email) => deriveHandleRaw(normalize(email), options.secret),
|
|
159
162
|
/** Effective config (with defaults applied), useful for routing. */
|
|
160
163
|
config: handlers._config,
|
|
161
164
|
/** Run a sweep tick on demand. Useful for tests and operator scripts. */
|