@molecule/app-auth 1.3.1 → 1.3.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/README.md +49 -33
- package/dist/browser-storage.d.ts +7 -0
- package/dist/browser-storage.d.ts.map +1 -1
- package/dist/browser-storage.js +7 -0
- package/dist/browser-storage.js.map +1 -1
- package/dist/index.d.ts +37 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
|
3
3
|
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
4
|
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
5
|
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
-
Generated: 2026-10-
|
|
6
|
+
Generated: 2026-10-17T20:34:11.708Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/app-auth
|
|
@@ -17,6 +17,43 @@ client; `setClient`/`getClient` bond it; `login`/`logout`/`register`/
|
|
|
17
17
|
bindings (e.g. `AuthProvider`/`useAuth` in `@molecule/app-react`) wrap the
|
|
18
18
|
same client.
|
|
19
19
|
|
|
20
|
+
**In a scaffolded app, wire it by hand — the sandbox cannot run `mlcl add`.**
|
|
21
|
+
The package is already installed; create the client once at startup with
|
|
22
|
+
`setClient(createJWTAuthClient({ baseURL, authScheme: 'Token', storage:
|
|
23
|
+
createBrowserStorageAdapter() }))`, or `createDefaultAuthClient(authConfig)`
|
|
24
|
+
from `@molecule/app-bonds-default-react` (which also registers
|
|
25
|
+
`whenRestored()` as the HTTP gate), call `initialize()`, and read the session
|
|
26
|
+
through `MoleculeProvider`/`useAuth()` in a React app.
|
|
27
|
+
|
|
28
|
+
**`initialize()` restores the session BEFORE the first render, and no
|
|
29
|
+
hand-rolled token store may replace it.** With the token AND user in the
|
|
30
|
+
store the restore is synchronous and costs no request; with an httpOnly
|
|
31
|
+
session cookie the API sets at login it restores through
|
|
32
|
+
`currentUserEndpoint` (default `/users/me`). `AuthClientConfig.storage` is
|
|
33
|
+
the config field that decides which route a reload takes. A store written
|
|
34
|
+
beside the client answers requests before its own restore settles: the first
|
|
35
|
+
authenticated page renders signed-out, and its 401 signs the user out for
|
|
36
|
+
good.
|
|
37
|
+
|
|
38
|
+
**Never fetch viewer-dependent data before the restore settles.** A
|
|
39
|
+
component effect that runs on mount fires while `initialize()` is still
|
|
40
|
+
restoring, so its request leaves without the `Authorization` header and the
|
|
41
|
+
server answers for an anonymous viewer — a followed user renders "Follow", a
|
|
42
|
+
saved item renders unsaved. On localhost the restore usually wins this race;
|
|
43
|
+
over the network it does not, so the bug appears only on the deployed URL.
|
|
44
|
+
Gate the fetch on the auth state being settled (`state.initialized`), or let
|
|
45
|
+
the HTTP client do it: `@molecule/app-bonds-default-react` registers
|
|
46
|
+
`whenRestored()` as the gate so every request waits for the restore.
|
|
47
|
+
|
|
48
|
+
**A store that registers its own gate must exempt the restore's own request.**
|
|
49
|
+
This client restores with `fetch`, so the gate registered over it never holds
|
|
50
|
+
the restore. A store that restores THROUGH the bonded HTTP client and
|
|
51
|
+
registers that restore as the gate (`setAuthGate(() => store.whenSettled())`)
|
|
52
|
+
waits on itself unless the restore's request passes `skipAuthGate: true` —
|
|
53
|
+
otherwise the gate holds the very request it is waiting for, and every
|
|
54
|
+
request it holds stalls until the gate times out. See
|
|
55
|
+
`RequestConfig.skipAuthGate` in `@molecule/app-http`.
|
|
56
|
+
|
|
20
57
|
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
21
58
|
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
22
59
|
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
@@ -648,6 +685,13 @@ type AuthEventListener = (event: AuthEvent) => void
|
|
|
648
685
|
|
|
649
686
|
The auth client's `StorageAdapter` over the browser's Web Storage.
|
|
650
687
|
|
|
688
|
+
**The restore it enables costs NO request — never block the first paint on one.** With the
|
|
689
|
+
token AND the user in the store, `initialize()` restores the authenticated state synchronously
|
|
690
|
+
(`whenRestored()` is already resolved on the first render); render from it, and validate the
|
|
691
|
+
token as a background request — never in front of the first render. A page that waits on its
|
|
692
|
+
own `/me` call pays a round trip on EVERY navigation (xr-molecule-flash-m122: a 1–5 s restore
|
|
693
|
+
window, eight `waitForURL` timeouts).
|
|
694
|
+
|
|
651
695
|
Pass it as `AuthClientConfig.storage` to keep the session across a page reload:
|
|
652
696
|
the client's default (`'memory'`) is lost on reload, and re-establishing the
|
|
653
697
|
session then needs a credential that survived — this adapter, or an httpOnly
|
|
@@ -665,15 +709,15 @@ const authClient = createJWTAuthClient({
|
|
|
665
709
|
The scaffold's `authConfig` already passes it, so a scaffolded app survives a
|
|
666
710
|
reload out of the box; each of these replaces that default:
|
|
667
711
|
|
|
668
|
-
|
|
712
|
+
````ts
|
|
669
713
|
storage: 'memory' // memory only: pair it with an httpOnly cookie session
|
|
670
714
|
storage: createBrowserStorageAdapter({ kind: 'session' }) // survives a reload, dies with the tab
|
|
671
|
-
|
|
672
|
-
|
|
715
|
+
|
|
716
|
+
*(description truncated — read the full text in `node_modules/@molecule/app-auth/dist/browser-storage.d.ts`.)*
|
|
673
717
|
|
|
674
718
|
```typescript
|
|
675
719
|
function createBrowserStorageAdapter(options?: BrowserStorageAdapterOptions): StorageAdapter
|
|
676
|
-
|
|
720
|
+
````
|
|
677
721
|
|
|
678
722
|
- `options` — Which Web Storage area to use, or a store to use directly.
|
|
679
723
|
|
|
@@ -855,38 +899,10 @@ Peer dependencies:
|
|
|
855
899
|
(`setClient(createJWTAuthClient({ authScheme: 'Token', … }))` in the auth bond), not
|
|
856
900
|
properties of a client that already exists. Do not hand-write an auth client for a spec
|
|
857
901
|
like RealWorld.
|
|
858
|
-
- **Whether a reload keeps the user signed in is `AuthClientConfig.storage`,
|
|
859
|
-
and one of the two routes must be wired.** The client's own default is
|
|
860
|
-
`'memory'`, which is lost on reload, so a session survives one only by
|
|
861
|
-
having a credential that survived: (1) `storage: createBrowserStorageAdapter()`
|
|
862
|
-
— the browser's `localStorage`, what a scaffolded app's `authConfig` passes
|
|
863
|
-
(see `@molecule/app-auth`'s `createBrowserStorageAdapter` for the trade: Web
|
|
864
|
-
Storage is script-readable, so an XSS can steal the token), or (2) an
|
|
865
|
-
httpOnly session cookie the API sets at login, which `initialize()` restores
|
|
866
|
-
through `currentUserEndpoint` (default `/users/me`) — the stronger option
|
|
867
|
-
wherever the API supports it, and what `@molecule/api-resource-user` issues.
|
|
868
|
-
Wire `initialize()` once at startup either way. Do NOT hand-roll a token
|
|
869
|
-
store beside the client for this: it is a config field.
|
|
870
902
|
- **Endpoint defaults must match your API's real routes** (`/auth/login`,
|
|
871
903
|
`/auth/register`, `/auth/refresh`, `/users/logout`, `/users/me`, …).
|
|
872
904
|
Align them via `createJWTAuthClient({ baseURL, ...endpoints })` — never by
|
|
873
905
|
hand-editing fetch calls in components.
|
|
874
|
-
- **Do not fetch viewer-dependent data before the restore settles.** A component
|
|
875
|
-
effect that runs on mount fires while `initialize()` is still restoring, so its
|
|
876
|
-
request leaves without the `Authorization` header and the server answers for an
|
|
877
|
-
anonymous viewer — a followed user renders "Follow", a saved item renders unsaved.
|
|
878
|
-
On localhost the restore usually wins this race; over the network it does not, so
|
|
879
|
-
the bug appears only on the deployed URL. Two ways out: gate the fetch on the auth
|
|
880
|
-
state being settled (`state.initialized`), or let the HTTP client do it — bond the
|
|
881
|
-
auth client through `@molecule/app-bonds-default-react`, which registers
|
|
882
|
-
`whenRestored()` as the http gate so every request waits for the restore.
|
|
883
|
-
- **Your own session store's gate must exempt its restore's own request.** This client
|
|
884
|
-
restores with `fetch`, so the gate registered over it never holds the restore. A store
|
|
885
|
-
that restores THROUGH the bonded HTTP client and registers that restore as the gate
|
|
886
|
-
(`setAuthGate(() => store.whenSettled())`) waits on itself unless the restore's request
|
|
887
|
-
passes `skipAuthGate: true` — otherwise the gate holds the very request it is waiting
|
|
888
|
-
for, and every request it holds stalls until the gate times out. See
|
|
889
|
-
`RequestConfig.skipAuthGate` in `@molecule/app-http`.
|
|
890
906
|
- Client-side auth state is UX only — the server enforces authorization on
|
|
891
907
|
every request; hiding a screen is not protection.
|
|
892
908
|
|
|
@@ -31,6 +31,13 @@ export interface BrowserStorageAdapterOptions {
|
|
|
31
31
|
/**
|
|
32
32
|
* The auth client's `StorageAdapter` over the browser's Web Storage.
|
|
33
33
|
*
|
|
34
|
+
* **The restore it enables costs NO request — never block the first paint on one.** With the
|
|
35
|
+
* token AND the user in the store, `initialize()` restores the authenticated state synchronously
|
|
36
|
+
* (`whenRestored()` is already resolved on the first render); render from it, and validate the
|
|
37
|
+
* token as a background request — never in front of the first render. A page that waits on its
|
|
38
|
+
* own `/me` call pays a round trip on EVERY navigation (xr-molecule-flash-m122: a 1–5 s restore
|
|
39
|
+
* window, eight `waitForURL` timeouts).
|
|
40
|
+
*
|
|
34
41
|
* Pass it as `AuthClientConfig.storage` to keep the session across a page reload:
|
|
35
42
|
* the client's default (`'memory'`) is lost on reload, and re-establishing the
|
|
36
43
|
* session then needs a credential that survived — this adapter, or an httpOnly
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-storage.d.ts","sourceRoot":"","sources":["../src/browser-storage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,SAAS,CAAA;AAEpD,4EAA4E;AAC5E,MAAM,WAAW,4BAA4B;IAC3C;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,kBAAkB,CAAA;IACzB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,cAAc,CAAA;CACzB;AAeD
|
|
1
|
+
{"version":3,"file":"browser-storage.d.ts","sourceRoot":"","sources":["../src/browser-storage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,SAAS,CAAA;AAEpD,4EAA4E;AAC5E,MAAM,WAAW,4BAA4B;IAC3C;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,kBAAkB,CAAA;IACzB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,cAAc,CAAA;CACzB;AAeD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,eAAO,MAAM,2BAA2B,GACtC,UAAS,4BAAiC,KACzC,cA6EF,CAAA"}
|
package/dist/browser-storage.js
CHANGED
|
@@ -22,6 +22,13 @@ const warn = (message, error) => {
|
|
|
22
22
|
/**
|
|
23
23
|
* The auth client's `StorageAdapter` over the browser's Web Storage.
|
|
24
24
|
*
|
|
25
|
+
* **The restore it enables costs NO request — never block the first paint on one.** With the
|
|
26
|
+
* token AND the user in the store, `initialize()` restores the authenticated state synchronously
|
|
27
|
+
* (`whenRestored()` is already resolved on the first render); render from it, and validate the
|
|
28
|
+
* token as a background request — never in front of the first render. A page that waits on its
|
|
29
|
+
* own `/me` call pays a round trip on EVERY navigation (xr-molecule-flash-m122: a 1–5 s restore
|
|
30
|
+
* window, eight `waitForURL` timeouts).
|
|
31
|
+
*
|
|
25
32
|
* Pass it as `AuthClientConfig.storage` to keep the session across a page reload:
|
|
26
33
|
* the client's default (`'memory'`) is lost on reload, and re-establishing the
|
|
27
34
|
* session then needs a credential that survived — this adapter, or an httpOnly
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-storage.js","sourceRoot":"","sources":["../src/browser-storage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AA8BhD,gFAAgF;AAChF,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,KAAe,EAAQ,EAAE;IACtD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;QAChC,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;;YACxC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IACtC,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,2EAA2E;QAC3E,2EAA2E;QAC3E,wBAAwB;IAC1B,CAAC;AACH,CAAC,CAAA;AAED
|
|
1
|
+
{"version":3,"file":"browser-storage.js","sourceRoot":"","sources":["../src/browser-storage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AA8BhD,gFAAgF;AAChF,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,KAAe,EAAQ,EAAE;IACtD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;QAChC,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;;YACxC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IACtC,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,2EAA2E;QAC3E,2EAA2E;QAC3E,wBAAwB;IAC1B,CAAC;AACH,CAAC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,UAAwC,EAAE,EAC1B,EAAE;IAClB,MAAM,EAAE,IAAI,GAAG,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IAC3C,2EAA2E;IAC3E,2DAA2D;IAC3D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAA;IACxC,yFAAyF;IACzF,IAAI,QAA2C,CAAA;IAC/C,gFAAgF;IAChF,IAAI,MAAM,GAAG,KAAK,CAAA;IAElB,MAAM,OAAO,GAAG,GAA0B,EAAE;QAC1C,IAAI,OAAO;YAAE,OAAO,OAAO,CAAA;QAC3B,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO,QAAQ,CAAA;QAChE,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,UAAmE,CAAA;YACjF,MAAM,GAAG,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAA;YAC1E,IAAI,GAAG,EAAE,CAAC;gBACR,mEAAmE;gBACnE,6EAA6E;gBAC7E,MAAM,KAAK,GAAG,yBAAyB,CAAA;gBACvC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;gBACzB,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;gBACrB,QAAQ,GAAG,GAAG,CAAA;gBACd,OAAO,GAAG,CAAA;YACZ,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,GAAG,IAAI,CAAA;gBACb,IAAI,CAAC,OAAO,IAAI,gEAAgE,EAAE,KAAK,CAAC,CAAA;YAC1F,CAAC;QACH,CAAC;QACD,4EAA4E;QAC5E,8CAA8C;QAC9C,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;IAED,OAAO;QACL,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACf,MAAM,KAAK,GAAG,OAAO,EAAE,CAAA;YACvB,IAAI,CAAC,KAAK;gBAAE,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAA;YAC1C,IAAI,CAAC;gBACH,sEAAsE;gBACtE,4DAA4D;gBAC5D,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAA;YACtD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,4DAA4D,EAAE,KAAK,CAAC,CAAA;gBACzE,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAA;YAChC,CAAC;QACH,CAAC;QACD,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;YACtB,MAAM,KAAK,GAAG,OAAO,EAAE,CAAA;YACvB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;gBACtB,OAAM;YACR,CAAC;YACD,IAAI,CAAC;gBACH,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;gBACzB,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACpB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,0EAA0E;gBAC1E,2EAA2E;gBAC3E,wDAAwD;gBACxD,IAAI,CAAC,kEAAkE,EAAE,KAAK,CAAC,CAAA;gBAC/E,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;YACxB,CAAC;QACH,CAAC;QACD,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE;YAClB,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YAClB,MAAM,KAAK,GAAG,OAAO,EAAE,CAAA;YACvB,IAAI,CAAC,KAAK;gBAAE,OAAM;YAClB,IAAI,CAAC;gBACH,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;YACvB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAA;YACpD,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,43 @@
|
|
|
8
8
|
* bindings (e.g. `AuthProvider`/`useAuth` in `@molecule/app-react`) wrap the
|
|
9
9
|
* same client.
|
|
10
10
|
*
|
|
11
|
+
* **In a scaffolded app, wire it by hand — the sandbox cannot run `mlcl add`.**
|
|
12
|
+
* The package is already installed; create the client once at startup with
|
|
13
|
+
* `setClient(createJWTAuthClient({ baseURL, authScheme: 'Token', storage:
|
|
14
|
+
* createBrowserStorageAdapter() }))`, or `createDefaultAuthClient(authConfig)`
|
|
15
|
+
* from `@molecule/app-bonds-default-react` (which also registers
|
|
16
|
+
* `whenRestored()` as the HTTP gate), call `initialize()`, and read the session
|
|
17
|
+
* through `MoleculeProvider`/`useAuth()` in a React app.
|
|
18
|
+
*
|
|
19
|
+
* **`initialize()` restores the session BEFORE the first render, and no
|
|
20
|
+
* hand-rolled token store may replace it.** With the token AND user in the
|
|
21
|
+
* store the restore is synchronous and costs no request; with an httpOnly
|
|
22
|
+
* session cookie the API sets at login it restores through
|
|
23
|
+
* `currentUserEndpoint` (default `/users/me`). `AuthClientConfig.storage` is
|
|
24
|
+
* the config field that decides which route a reload takes. A store written
|
|
25
|
+
* beside the client answers requests before its own restore settles: the first
|
|
26
|
+
* authenticated page renders signed-out, and its 401 signs the user out for
|
|
27
|
+
* good.
|
|
28
|
+
*
|
|
29
|
+
* **Never fetch viewer-dependent data before the restore settles.** A
|
|
30
|
+
* component effect that runs on mount fires while `initialize()` is still
|
|
31
|
+
* restoring, so its request leaves without the `Authorization` header and the
|
|
32
|
+
* server answers for an anonymous viewer — a followed user renders "Follow", a
|
|
33
|
+
* saved item renders unsaved. On localhost the restore usually wins this race;
|
|
34
|
+
* over the network it does not, so the bug appears only on the deployed URL.
|
|
35
|
+
* Gate the fetch on the auth state being settled (`state.initialized`), or let
|
|
36
|
+
* the HTTP client do it: `@molecule/app-bonds-default-react` registers
|
|
37
|
+
* `whenRestored()` as the gate so every request waits for the restore.
|
|
38
|
+
*
|
|
39
|
+
* **A store that registers its own gate must exempt the restore's own request.**
|
|
40
|
+
* This client restores with `fetch`, so the gate registered over it never holds
|
|
41
|
+
* the restore. A store that restores THROUGH the bonded HTTP client and
|
|
42
|
+
* registers that restore as the gate (`setAuthGate(() => store.whenSettled())`)
|
|
43
|
+
* waits on itself unless the restore's request passes `skipAuthGate: true` —
|
|
44
|
+
* otherwise the gate holds the very request it is waiting for, and every
|
|
45
|
+
* request it holds stalls until the gate times out. See
|
|
46
|
+
* `RequestConfig.skipAuthGate` in `@molecule/app-http`.
|
|
47
|
+
*
|
|
11
48
|
* @remarks
|
|
12
49
|
* - **An API with its own auth envelope keeps this client.** `requestBody` wraps the
|
|
13
50
|
* login/register payload (`(kind, p) => ({ user: p })`), `mapAuthResponse` reads the
|
|
@@ -17,38 +54,10 @@
|
|
|
17
54
|
* (`setClient(createJWTAuthClient({ authScheme: 'Token', … }))` in the auth bond), not
|
|
18
55
|
* properties of a client that already exists. Do not hand-write an auth client for a spec
|
|
19
56
|
* like RealWorld.
|
|
20
|
-
* - **Whether a reload keeps the user signed in is `AuthClientConfig.storage`,
|
|
21
|
-
* and one of the two routes must be wired.** The client's own default is
|
|
22
|
-
* `'memory'`, which is lost on reload, so a session survives one only by
|
|
23
|
-
* having a credential that survived: (1) `storage: createBrowserStorageAdapter()`
|
|
24
|
-
* — the browser's `localStorage`, what a scaffolded app's `authConfig` passes
|
|
25
|
-
* (see `@molecule/app-auth`'s `createBrowserStorageAdapter` for the trade: Web
|
|
26
|
-
* Storage is script-readable, so an XSS can steal the token), or (2) an
|
|
27
|
-
* httpOnly session cookie the API sets at login, which `initialize()` restores
|
|
28
|
-
* through `currentUserEndpoint` (default `/users/me`) — the stronger option
|
|
29
|
-
* wherever the API supports it, and what `@molecule/api-resource-user` issues.
|
|
30
|
-
* Wire `initialize()` once at startup either way. Do NOT hand-roll a token
|
|
31
|
-
* store beside the client for this: it is a config field.
|
|
32
57
|
* - **Endpoint defaults must match your API's real routes** (`/auth/login`,
|
|
33
58
|
* `/auth/register`, `/auth/refresh`, `/users/logout`, `/users/me`, …).
|
|
34
59
|
* Align them via `createJWTAuthClient({ baseURL, ...endpoints })` — never by
|
|
35
60
|
* hand-editing fetch calls in components.
|
|
36
|
-
* - **Do not fetch viewer-dependent data before the restore settles.** A component
|
|
37
|
-
* effect that runs on mount fires while `initialize()` is still restoring, so its
|
|
38
|
-
* request leaves without the `Authorization` header and the server answers for an
|
|
39
|
-
* anonymous viewer — a followed user renders "Follow", a saved item renders unsaved.
|
|
40
|
-
* On localhost the restore usually wins this race; over the network it does not, so
|
|
41
|
-
* the bug appears only on the deployed URL. Two ways out: gate the fetch on the auth
|
|
42
|
-
* state being settled (`state.initialized`), or let the HTTP client do it — bond the
|
|
43
|
-
* auth client through `@molecule/app-bonds-default-react`, which registers
|
|
44
|
-
* `whenRestored()` as the http gate so every request waits for the restore.
|
|
45
|
-
* - **Your own session store's gate must exempt its restore's own request.** This client
|
|
46
|
-
* restores with `fetch`, so the gate registered over it never holds the restore. A store
|
|
47
|
-
* that restores THROUGH the bonded HTTP client and registers that restore as the gate
|
|
48
|
-
* (`setAuthGate(() => store.whenSettled())`) waits on itself unless the restore's request
|
|
49
|
-
* passes `skipAuthGate: true` — otherwise the gate holds the very request it is waiting
|
|
50
|
-
* for, and every request it holds stalls until the gate times out. See
|
|
51
|
-
* `RequestConfig.skipAuthGate` in `@molecule/app-http`.
|
|
52
61
|
* - Client-side auth state is UX only — the server enforces authorization on
|
|
53
62
|
* every request; hiding a screen is not protection.
|
|
54
63
|
*
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4HG;AAEH,cAAc,sBAAsB,CAAA;AACpC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,43 @@
|
|
|
8
8
|
* bindings (e.g. `AuthProvider`/`useAuth` in `@molecule/app-react`) wrap the
|
|
9
9
|
* same client.
|
|
10
10
|
*
|
|
11
|
+
* **In a scaffolded app, wire it by hand — the sandbox cannot run `mlcl add`.**
|
|
12
|
+
* The package is already installed; create the client once at startup with
|
|
13
|
+
* `setClient(createJWTAuthClient({ baseURL, authScheme: 'Token', storage:
|
|
14
|
+
* createBrowserStorageAdapter() }))`, or `createDefaultAuthClient(authConfig)`
|
|
15
|
+
* from `@molecule/app-bonds-default-react` (which also registers
|
|
16
|
+
* `whenRestored()` as the HTTP gate), call `initialize()`, and read the session
|
|
17
|
+
* through `MoleculeProvider`/`useAuth()` in a React app.
|
|
18
|
+
*
|
|
19
|
+
* **`initialize()` restores the session BEFORE the first render, and no
|
|
20
|
+
* hand-rolled token store may replace it.** With the token AND user in the
|
|
21
|
+
* store the restore is synchronous and costs no request; with an httpOnly
|
|
22
|
+
* session cookie the API sets at login it restores through
|
|
23
|
+
* `currentUserEndpoint` (default `/users/me`). `AuthClientConfig.storage` is
|
|
24
|
+
* the config field that decides which route a reload takes. A store written
|
|
25
|
+
* beside the client answers requests before its own restore settles: the first
|
|
26
|
+
* authenticated page renders signed-out, and its 401 signs the user out for
|
|
27
|
+
* good.
|
|
28
|
+
*
|
|
29
|
+
* **Never fetch viewer-dependent data before the restore settles.** A
|
|
30
|
+
* component effect that runs on mount fires while `initialize()` is still
|
|
31
|
+
* restoring, so its request leaves without the `Authorization` header and the
|
|
32
|
+
* server answers for an anonymous viewer — a followed user renders "Follow", a
|
|
33
|
+
* saved item renders unsaved. On localhost the restore usually wins this race;
|
|
34
|
+
* over the network it does not, so the bug appears only on the deployed URL.
|
|
35
|
+
* Gate the fetch on the auth state being settled (`state.initialized`), or let
|
|
36
|
+
* the HTTP client do it: `@molecule/app-bonds-default-react` registers
|
|
37
|
+
* `whenRestored()` as the gate so every request waits for the restore.
|
|
38
|
+
*
|
|
39
|
+
* **A store that registers its own gate must exempt the restore's own request.**
|
|
40
|
+
* This client restores with `fetch`, so the gate registered over it never holds
|
|
41
|
+
* the restore. A store that restores THROUGH the bonded HTTP client and
|
|
42
|
+
* registers that restore as the gate (`setAuthGate(() => store.whenSettled())`)
|
|
43
|
+
* waits on itself unless the restore's request passes `skipAuthGate: true` —
|
|
44
|
+
* otherwise the gate holds the very request it is waiting for, and every
|
|
45
|
+
* request it holds stalls until the gate times out. See
|
|
46
|
+
* `RequestConfig.skipAuthGate` in `@molecule/app-http`.
|
|
47
|
+
*
|
|
11
48
|
* @remarks
|
|
12
49
|
* - **An API with its own auth envelope keeps this client.** `requestBody` wraps the
|
|
13
50
|
* login/register payload (`(kind, p) => ({ user: p })`), `mapAuthResponse` reads the
|
|
@@ -17,38 +54,10 @@
|
|
|
17
54
|
* (`setClient(createJWTAuthClient({ authScheme: 'Token', … }))` in the auth bond), not
|
|
18
55
|
* properties of a client that already exists. Do not hand-write an auth client for a spec
|
|
19
56
|
* like RealWorld.
|
|
20
|
-
* - **Whether a reload keeps the user signed in is `AuthClientConfig.storage`,
|
|
21
|
-
* and one of the two routes must be wired.** The client's own default is
|
|
22
|
-
* `'memory'`, which is lost on reload, so a session survives one only by
|
|
23
|
-
* having a credential that survived: (1) `storage: createBrowserStorageAdapter()`
|
|
24
|
-
* — the browser's `localStorage`, what a scaffolded app's `authConfig` passes
|
|
25
|
-
* (see `@molecule/app-auth`'s `createBrowserStorageAdapter` for the trade: Web
|
|
26
|
-
* Storage is script-readable, so an XSS can steal the token), or (2) an
|
|
27
|
-
* httpOnly session cookie the API sets at login, which `initialize()` restores
|
|
28
|
-
* through `currentUserEndpoint` (default `/users/me`) — the stronger option
|
|
29
|
-
* wherever the API supports it, and what `@molecule/api-resource-user` issues.
|
|
30
|
-
* Wire `initialize()` once at startup either way. Do NOT hand-roll a token
|
|
31
|
-
* store beside the client for this: it is a config field.
|
|
32
57
|
* - **Endpoint defaults must match your API's real routes** (`/auth/login`,
|
|
33
58
|
* `/auth/register`, `/auth/refresh`, `/users/logout`, `/users/me`, …).
|
|
34
59
|
* Align them via `createJWTAuthClient({ baseURL, ...endpoints })` — never by
|
|
35
60
|
* hand-editing fetch calls in components.
|
|
36
|
-
* - **Do not fetch viewer-dependent data before the restore settles.** A component
|
|
37
|
-
* effect that runs on mount fires while `initialize()` is still restoring, so its
|
|
38
|
-
* request leaves without the `Authorization` header and the server answers for an
|
|
39
|
-
* anonymous viewer — a followed user renders "Follow", a saved item renders unsaved.
|
|
40
|
-
* On localhost the restore usually wins this race; over the network it does not, so
|
|
41
|
-
* the bug appears only on the deployed URL. Two ways out: gate the fetch on the auth
|
|
42
|
-
* state being settled (`state.initialized`), or let the HTTP client do it — bond the
|
|
43
|
-
* auth client through `@molecule/app-bonds-default-react`, which registers
|
|
44
|
-
* `whenRestored()` as the http gate so every request waits for the restore.
|
|
45
|
-
* - **Your own session store's gate must exempt its restore's own request.** This client
|
|
46
|
-
* restores with `fetch`, so the gate registered over it never holds the restore. A store
|
|
47
|
-
* that restores THROUGH the bonded HTTP client and registers that restore as the gate
|
|
48
|
-
* (`setAuthGate(() => store.whenSettled())`) waits on itself unless the restore's request
|
|
49
|
-
* passes `skipAuthGate: true` — otherwise the gate holds the very request it is waiting
|
|
50
|
-
* for, and every request it holds stalls until the gate times out. See
|
|
51
|
-
* `RequestConfig.skipAuthGate` in `@molecule/app-http`.
|
|
52
61
|
* - Client-side auth state is UX only — the server enforces authorization on
|
|
53
62
|
* every request; hiding a screen is not protection.
|
|
54
63
|
*
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4HG;AAEH,cAAc,sBAAsB,CAAA;AACpC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA"}
|