knowless 1.1.1 → 1.1.2

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 CHANGED
@@ -30,6 +30,30 @@ v1.0.0 are:
30
30
  - Documentation corrections
31
31
  - Helper exports that pull existing mechanism back into the library
32
32
 
33
+ ## [1.1.2] — 2026-05-03
34
+
35
+ Documentation-only release. Adopters were repeatedly missing that
36
+ `auth.loginForm` is an unstyled contract-minimal fallback they
37
+ should override, and that `/login` is also where every silent-miss
38
+ failure (used / expired / sham / malformed token) redirects — so
39
+ the page deserves first-class UI in the host app, not the bundled
40
+ default. No code changes.
41
+
42
+ ### Documented
43
+
44
+ - `GUIDE.md` FAQ — added section "Branding the GET /login page
45
+ (you almost certainly want to override it)" with the three
46
+ reasons (brand consistency, silent-miss redirect target, opt-in
47
+ fallback) and the override pattern. Extended the existing
48
+ custom-form contract with `next` validation against
49
+ `baseUrl` + `cookieDomain` and the optional honeypot field name
50
+ from `cfg.honeypotFieldName`.
51
+ - `knowless.context.md` gotcha #10 — expanded the "operators
52
+ wanting branding fork the project" note to surface the more
53
+ common path (skip mounting `auth.loginForm`, serve your own
54
+ `GET /login`) and call out that `/login` is the silent-miss
55
+ redirect target.
56
+
33
57
  ## [1.1.1] — 2026-05-02
34
58
 
35
59
  Documentation-only release. Adds the wrong-shape-integration
package/GUIDE.md CHANGED
@@ -929,7 +929,48 @@ own form, provided it satisfies the handler contract:
929
929
  stream itself. A body-parser middleware mounted before `auth.login`
930
930
  will silently steal the data (see gotcha #15 in
931
931
  [`knowless.context.md`](knowless.context.md)).
932
- - Optional: include a `next` field for the post-callback redirect URL.
932
+ - Optional: include a `next` field for the post-callback redirect URL
933
+ (knowless validates `next` against `baseUrl` + `cookieDomain`).
934
+ - Optional but recommended: include the honeypot field using the name
935
+ from `cfg.honeypotFieldName`.
936
+
937
+ ### Branding the GET /login page (you almost certainly want to override it)
938
+
939
+ Knowless ships `auth.loginForm(req, res)` as a turnkey fallback so
940
+ adopters can wire `GET /login` in one line and have a working magic-link
941
+ form. The page is intentionally unstyled — it's the contract-minimal
942
+ renderer needed to make the flow demonstrable, not a UI you ship to
943
+ end users. Three reasons most adopters override it:
944
+
945
+ 1. **Brand consistency.** The fallback page has no header, footer, nav,
946
+ or styling, so users redirected here from elsewhere in your app land
947
+ on what looks like a different site. That's especially jarring after
948
+ a sham-token failure (a deliberate part of the silent-miss design —
949
+ see "Silent miss" in [`knowless.context.md`](knowless.context.md)),
950
+ where a user clicked a magic link and ended up on a "Sign in" page
951
+ that looks unrelated to where they started.
952
+ 2. **`/login` is load-bearing in the silent-miss contract.** Knowless
953
+ redirects to `loginPath` on every failure mode that must be
954
+ indistinguishable from success — used token, expired token, sham
955
+ token, malformed token. That redirect is correct and required. But
956
+ it means `/login` is the page users actually land on during
957
+ anti-enumeration failures, not just the page they navigate to
958
+ deliberately. It deserves first-class UI in your app.
959
+ 3. **`auth.loginForm` is opt-in, not opt-out.** Adopters who never wire
960
+ the GET route still get a working app — just without a friendly
961
+ `/login` page. Override it whenever you want your app's chrome on
962
+ the page. The POST handler can stay as-is (or also be wrapped — for
963
+ example, plato wraps it for the "we sent a link" confirmation).
964
+
965
+ Override pattern (mount your own handler instead of `auth.loginForm`):
966
+
967
+ ```js
968
+ app.get('/login', (req, res) => renderMyOwnLogin(req, res));
969
+ app.post('/login', auth.login); // unchanged
970
+ ```
971
+
972
+ The form just needs to satisfy the contract listed in the previous
973
+ question.
933
974
 
934
975
  ### How do I add 2FA / WebAuthn / TOTP?
935
976
 
@@ -699,7 +699,13 @@ rate-limits) belongs above the library.
699
699
  10. **No JavaScript in any HTML page.** The login form, the
700
700
  confirmation page, error pages — all static HTML5. Works in
701
701
  text-mode browsers (Lynx, w3m). Operators wanting branding
702
- fork the project.
702
+ fork the project — **or, more commonly, skip mounting
703
+ `auth.loginForm` and serve their own `GET /login`**. The
704
+ fallback is intentionally a contract-minimal renderer, not a
705
+ UI to ship. `/login` is also where every silent-miss failure
706
+ redirects (used / expired / sham / malformed token), so it
707
+ deserves first-class chrome in the host app. See GUIDE.md
708
+ § "Branding the GET /login page".
703
709
 
704
710
  11. **Process cleanup matters.** `auth.close()` stops the
705
711
  sweeper and closes the SQLite handle. Without it, your
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knowless",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
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",