@gaballahtech/seba 0.0.0-stage → 0.2.1

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.
Files changed (86) hide show
  1. package/ARCHITECTURE.md +242 -0
  2. package/BUILD_REPORT.md +195 -0
  3. package/CHANGELOG.md +86 -0
  4. package/LICENSE +21 -0
  5. package/README.md +282 -2
  6. package/RESEARCH.md +180 -0
  7. package/SECURITY.md +224 -0
  8. package/VALIDATION_REPORT.md +187 -0
  9. package/dist/browser/index.d.ts +9 -0
  10. package/dist/browser/index.d.ts.map +1 -0
  11. package/dist/browser/index.js +9 -0
  12. package/dist/browser/index.js.map +1 -0
  13. package/dist/browser/platform.d.ts +14 -0
  14. package/dist/browser/platform.d.ts.map +1 -0
  15. package/dist/browser/platform.js +60 -0
  16. package/dist/browser/platform.js.map +1 -0
  17. package/dist/browser/provider-modal.d.ts +15 -0
  18. package/dist/browser/provider-modal.d.ts.map +1 -0
  19. package/dist/browser/provider-modal.js +126 -0
  20. package/dist/browser/provider-modal.js.map +1 -0
  21. package/dist/browser/storage.d.ts +32 -0
  22. package/dist/browser/storage.d.ts.map +1 -0
  23. package/dist/browser/storage.js +121 -0
  24. package/dist/browser/storage.js.map +1 -0
  25. package/dist/core/client.d.ts +49 -0
  26. package/dist/core/client.d.ts.map +1 -0
  27. package/dist/core/client.js +371 -0
  28. package/dist/core/client.js.map +1 -0
  29. package/dist/core/discovery.d.ts +82 -0
  30. package/dist/core/discovery.d.ts.map +1 -0
  31. package/dist/core/discovery.js +243 -0
  32. package/dist/core/discovery.js.map +1 -0
  33. package/dist/core/dpop.d.ts +67 -0
  34. package/dist/core/dpop.d.ts.map +1 -0
  35. package/dist/core/dpop.js +176 -0
  36. package/dist/core/dpop.js.map +1 -0
  37. package/dist/core/errors.d.ts +86 -0
  38. package/dist/core/errors.d.ts.map +1 -0
  39. package/dist/core/errors.js +104 -0
  40. package/dist/core/errors.js.map +1 -0
  41. package/dist/core/http.d.ts +68 -0
  42. package/dist/core/http.d.ts.map +1 -0
  43. package/dist/core/http.js +128 -0
  44. package/dist/core/http.js.map +1 -0
  45. package/dist/core/identity.d.ts +145 -0
  46. package/dist/core/identity.d.ts.map +1 -0
  47. package/dist/core/identity.js +735 -0
  48. package/dist/core/identity.js.map +1 -0
  49. package/dist/core/oidc.d.ts +175 -0
  50. package/dist/core/oidc.d.ts.map +1 -0
  51. package/dist/core/oidc.js +413 -0
  52. package/dist/core/oidc.js.map +1 -0
  53. package/dist/core/session.d.ts +116 -0
  54. package/dist/core/session.d.ts.map +1 -0
  55. package/dist/core/session.js +350 -0
  56. package/dist/core/session.js.map +1 -0
  57. package/dist/core/storage.d.ts +22 -0
  58. package/dist/core/storage.d.ts.map +1 -0
  59. package/dist/core/storage.js +48 -0
  60. package/dist/core/storage.js.map +1 -0
  61. package/dist/index.d.ts +18 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +3 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/node/index.d.ts +9 -0
  66. package/dist/node/index.d.ts.map +1 -0
  67. package/dist/node/index.js +9 -0
  68. package/dist/node/index.js.map +1 -0
  69. package/dist/node/platform.d.ts +25 -0
  70. package/dist/node/platform.d.ts.map +1 -0
  71. package/dist/node/platform.js +161 -0
  72. package/dist/node/platform.js.map +1 -0
  73. package/dist/seba.d.ts +39 -0
  74. package/dist/seba.d.ts.map +1 -0
  75. package/dist/seba.js +58 -0
  76. package/dist/seba.js.map +1 -0
  77. package/dist/types.d.ts +279 -0
  78. package/dist/types.d.ts.map +1 -0
  79. package/dist/types.js +8 -0
  80. package/dist/types.js.map +1 -0
  81. package/examples/node/index.ts +61 -0
  82. package/examples/react/App.tsx +127 -0
  83. package/examples/react/index.html +12 -0
  84. package/examples/react/main.tsx +14 -0
  85. package/examples/react/vite.config.ts +27 -0
  86. package/package.json +83 -4
@@ -0,0 +1,242 @@
1
+ # ARCHITECTURE
2
+
3
+ ## 1. System overview
4
+
5
+ SEBA is a single npm package with three entry points over one implementation:
6
+
7
+ ```
8
+ ┌──────────────────────────────┐
9
+ seba │ src/index.ts (public API) │
10
+ └──────────────┬───────────────┘
11
+ ┌───────────┴────────────┐
12
+ │ src/seba.ts (Seba) │ 4 public methods, lazy runtime binding
13
+ └───────────┬────────────┘
14
+ │
15
+ ┌──────────────▼───────────────┐
16
+ │ src/core/client.ts │ protocol orchestration, platform-agnostic
17
+ └──┬────────┬────────┬─────────┘
18
+ ┌───────────┘ │ └─────────────┐
19
+ ┌───────▼──────┐ ┌──────────▼───────┐ ┌──────────▼─────────┐
20
+ │ identity.ts │ │ discovery.ts │ │ oidc.ts │
21
+ │ Client ID │ │ OP metadata │ │ PKCE, state, nonce │
22
+ │ WebID │ │ issuer checks │ │ callback, tokens │
23
+ └──────────────┘ └──────────────────┘ └──────────┬─────────┘
24
+ │
25
+ ┌───────────────▼──────────┐
26
+ │ dpop.ts session.ts │
27
+ │ proofs storage/refresh│
28
+ └───────────────┬──────────┘
29
+ │ Platform interface
30
+ ┌──────────────────────────┴─────────────────────────┐
31
+ ┌─────────▼──────────┐ ┌────────────▼─────────┐
32
+ │ browser/platform │ │ node/platform │
33
+ │ sessionStorage, │ │ memory pending auth, │
34
+ │ IndexedDB, DOM │ │ loopback HTTP server │
35
+ └────────────────────┘ └──────────────────────┘
36
+ ```
37
+
38
+ `src/core/client.ts` contains **all** protocol sequencing exactly once. Browser and Node
39
+ differences are confined to the `Platform` interface (`src/types.ts`): how a callback URL
40
+ is obtained, where pending authorization state lives, and which storage backend is the
41
+ default. There is no second code path for "Node login"; the same validation runs for both.
42
+
43
+ ## 2. Core module responsibilities
44
+
45
+ | Module | Responsibility | Deliberately not responsible for |
46
+ |---|---|---|
47
+ | `core/errors.ts` | Error hierarchy with stable `code` values. | Deciding retry policy. |
48
+ | `core/http.ts` | JSON/text fetching with content-type checks, `SebaNetworkError` mapping, non-2xx handling, timeout. | Auth headers (callers add them). |
49
+ | `core/identity.ts` | Client ID Document fetch/validation, exact redirect-URI membership, WebID URL handling, Turtle-subset + JSON-LD + `Link` issuer extraction. | Trusting any value it parsed (issuer is re-validated). |
50
+ | `core/discovery.ts` | Issuer validation, well-known lookup, OP metadata validation. | Assuming optional metadata exists. |
51
+ | `core/oidc.ts` | PKCE, `state`, `nonce`, authorization URL, callback parsing/validation, code exchange, token-response parsing, ID token validation, WebID extraction. | Storing anything. |
52
+ | `core/dpop.ts` | ES256 key generation/import, proof signing, `jti`, `htu`, `ath`, thumbprints, nonce plumbing. | Caching proofs (forbidden). |
53
+ | `core/session.ts` | The active session record, refresh with rotation, revocation, logout cleanup, restore. | Deciding whether to redirect the user, storage engines. |
54
+ | `core/storage.ts`, `browser/storage.ts` | Memory adapters (Node.js, tests) and IndexedDB/`sessionStorage` adapters (browser). | Protocol decisions. |
55
+ | `core/client.ts` | The four operations, fetch retry/redirect policy, in-memory access token, last-seen DPoP nonce, debug logging. | Any DOM or `node:` API. |
56
+ | `seba.ts` | Public `Seba` class with the four documented methods; binds a runtime platform lazily on first use. | Protocol details. |
57
+
58
+ ## 3. Browser architecture
59
+
60
+ `src/browser/platform.ts` supplies:
61
+
62
+ - `currentUrl()` → `new URL(window.location.href)`.
63
+ - `pendingAuth` → one `sessionStorage` record under `seba.pending-authorization`
64
+ (the record itself carries `clientId` and `redirectUri`, which are re-checked on the
65
+ callback). The PKCE verifier and expected nonce never leave the tab and never appear in
66
+ a URL.
67
+ - `beginInteractiveLogin()` → `window.location.assign(authorizationUrl)`; the returned
68
+ promise intentionally never settles, because the document is being replaced.
69
+ - `selectIdentity()` → the vanilla DOM provider selector in `browser/provider-modal.ts`
70
+ (focus trap, `Esc` to close, `Enter` to submit, WebID or custom issuer input, no password
71
+ field, no network calls).
72
+ - `createStorage("indexeddb")` → `idb`-backed adapter; `"memory"` → process memory.
73
+ - `clearCallback()` → `history.replaceState` to remove `code`/`state`/`iss` from the
74
+ address bar after the callback has been consumed.
75
+
76
+ Login lifecycle in a browser (documented in `README.md`):
77
+
78
+ 1. Application calls `login()`. There is no callback in the URL, so SEBA discovers the
79
+ provider, stores pending state in `sessionStorage`, and navigates away.
80
+ 2. The provider authenticates the user and redirects to `redirectUri?code=…&state=…&iss=…`.
81
+ 3. The application calls `login()` again on that page. SEBA recognises the callback,
82
+ validates `state`, `iss`, `nonce`, PKCE and the ID token, stores the refresh token and
83
+ key, keeps the access token in memory, returns the `Session`.
84
+
85
+ Because step 1 unloads the page, its promise never resolves; this is documented rather
86
+ than papered over with polling or `BroadcastChannel` tricks.
87
+
88
+ `src/browser/index.ts` is the `@gaballahtech/seba/browser` entry point: it re-exports the public API and
89
+ `createBrowserPlatform`.
90
+
91
+ ## 4. Node.js architecture
92
+
93
+ `src/node/platform.ts`:
94
+
95
+ - `currentUrl()` → `null` (no document).
96
+ - `pendingAuth` → an in-process record; no browser storage API is referenced anywhere in
97
+ `src/node/`.
98
+ - `beginInteractiveLogin()` →
99
+ 1. parse the configured `redirectUri`; require `http:` and a loopback host
100
+ (`127.0.0.1`, `::1`, `localhost`) — never bind `0.0.0.0`;
101
+ 2. `node:http` server on that host and port, only answering the registered path;
102
+ 3. call `openAuthorizationUrl(authorizationUrl)` (required; otherwise
103
+ `SebaNoUIError` — thrown *before* the server is started);
104
+ 4. respond with a small HTML page that contains no secrets and close the listener
105
+ completely before resolving the login promise, so the port is free for the next login
106
+ and no late request can reach a closed transaction;
107
+ 5. reject after a five-minute timeout and close the listener the same way.
108
+ - `createStorage()` → memory only; `"indexeddb"` throws `SebaConfigError` with an
109
+ actionable message instead of pretending a browser store exists.
110
+
111
+ Provider and resource calls use the runtime's global `fetch` through `core/http.ts`, which
112
+ adds an abort-based 10-second timeout and maps transport failures to `SebaNetworkError`.
113
+ `src/node/index.ts` is the `@gaballahtech/seba/node` entry point: it re-exports the public API and
114
+ `createNodePlatform`.
115
+
116
+ The configured `redirectUri` must still be published by the Client ID Document, so a
117
+ loopback URI must appear in `redirect_uris`; otherwise `SebaRedirectMismatchError` is
118
+ thrown before any listener is opened.
119
+
120
+ ## 5. Authentication flow
121
+
122
+ ```
123
+ login(options)
124
+ │
125
+ ├─ callback present & pending state found? ──yes──► completeLogin()
126
+ │ ├ state exact match
127
+ │ ├ RFC 9207 `iss` == pending issuer
128
+ │ ├ error param? → SebaAuthError
129
+ │ ├ POST token endpoint (fresh DPoP proof, no ath)
130
+ │ ├ token_type == DPoP
131
+ │ ├ validate ID token (JWKS, iss, aud, azp, exp, iat, nonce)
132
+ │ ├ cnf.jkt == our thumbprint (and required for `webid`)
133
+ │ ├ webId == requested webId (when given)
134
+ │ ├ persist {refresh_token, key, webId, issuer, clientId}
135
+ │ └ keep access token in memory → Session
136
+ │
137
+ └─ no callback
138
+ ├ issuer = options.issuer ?? discoverIssuerForWebId(options.webId) ?? platform.selectIdentity()
139
+ ├ loadClientIdDocument(clientId) → redirect URI must be listed exactly
140
+ ├ discoverProvider(issuer)
141
+ ├ create PKCE verifier/challenge, state, nonce → PendingAuthorization
142
+ ├ build authorization URL (response_type=code, scope=openid webid offline_access,
143
+ │ code_challenge_method=S256, nonce, state, client_id)
144
+ ├ platform.beginInteractiveLogin(...) → browser: navigate away
145
+ │ → node: loopback callback URL
146
+ └ completeLogin(callbackUrl)
147
+ ```
148
+
149
+ ## 6. Session lifecycle
150
+
151
+ ```
152
+ (no session) ──login──► { accessToken (memory), refreshToken (storage), key (storage) }
153
+ │
154
+ access token expires / 401 invalid_token
155
+ ▼
156
+ refresh (grant_type=refresh_token, fresh proof)
157
+ │ rotation: persist new refresh token atomically
158
+ ▼
159
+ new access token in memory
160
+ │
161
+ restoreSession() on reload: load record → refresh → Session
162
+ │ record missing → null
163
+ │ invalid_grant → clear storage + SebaSessionExpiredError
164
+ │ 5xx / offline → SebaNetworkError, record kept
165
+ ▼
166
+ logout(): clear memory → revoke (best effort) → delete record
167
+ ```
168
+
169
+ The persisted record (`PersistedSession`) is the only durable state:
170
+ `version`, `webId`, `provider`, `clientId`, `refreshToken`, `key`
171
+ (`CryptoKey` + public JWK, or exported JWK), optional `scope`, `createdAt`.
172
+ **No access token field exists in the type**, so it cannot be persisted by accident.
173
+
174
+ Refresh is serialized through a single in-flight promise in `core/session.ts`, so ten
175
+ concurrent `fetch()` calls that all see an expired token perform one refresh, not ten
176
+ (rotating refresh tokens would otherwise invalidate each other).
177
+
178
+ ## 7. DPoP lifecycle
179
+
180
+ 1. **Generation** at login: ES256/P-256 via `jose.generateKeyPair`. Non-extractable private
181
+ key when the runtime can structured-clone `CryptoKey` (probed with `structuredClone`);
182
+ otherwise an extractable key exported to JWK and stored as such (reported in
183
+ `SECURITY.md`).
184
+ 2. **Signing** per attempt: `{typ: "dpop+jwt", alg: "ES256", jwk: publicJwk}` with
185
+ `{jti, htm, htu, iat}` plus `ath` only when an access token is presented, plus `nonce`
186
+ when the server supplied one.
187
+ 3. **No persistence of proofs.** `createDPoPProof()` is pure: inputs in, one JWT out. It is
188
+ called once per HTTP attempt, including retries (`nonce` challenge, token refresh,
189
+ redirect hop).
190
+ 4. **Nonce handling**: `core/client.ts` keeps the most recent `DPoP-Nonce` returned by a
191
+ protected resource and sends it with subsequent proofs. A nonce is server state, not a
192
+ proof; caching it is explicitly allowed by RFC 9449. Token requests handle their own
193
+ `use_dpop_nonce` challenge in `core/oidc.ts`.
194
+ 5. **Binding**: the key pair stays with the session record; the ID token's `cnf.jkt` and
195
+ the access token's binding are checked against its thumbprint.
196
+
197
+ ## 8. Storage architecture
198
+
199
+ | Runtime | Default | Record location | Access token |
200
+ |---|---|---|---|
201
+ | Browser | `indexeddb` | DB `seba`, object store `session`, single record key `current` | memory only |
202
+ | Browser (`storage: "memory"`) | opt-in | module-level map | memory only |
203
+ | Node.js | `memory` | process memory | memory only |
204
+
205
+ `sessionStorage` holds **only** the pending authorization transaction
206
+ (`state`, `code_verifier`, `nonce`, `issuer`, `clientId`, `redirectUri`, `createdAt`,
207
+ optional `webId`) and is deleted as soon as the callback is processed. `localStorage` is
208
+ never used. IndexedDB writes are whole-record `put`s, which makes refresh-token rotation a
209
+ single atomic overwrite.
210
+
211
+ ## 9. Public API design
212
+
213
+ ```ts
214
+ class Seba {
215
+ constructor(config: SebaConfig)
216
+ login(options?: LoginOptions): Promise<Session>
217
+ restoreSession(): Promise<Session | null>
218
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>
219
+ logout(): Promise<void>
220
+ }
221
+ ```
222
+
223
+ Exactly four public methods. `Session` carries `webId`, `isLoggedIn: true`, `provider` and
224
+ no tokens. Tokens, keys and proofs are never exported from the package root. Configuration
225
+ is validated when the first operation runs (`SebaConfigError`); there are no
226
+ proof-caching or TTL knobs.
227
+
228
+ `Seba` binds its platform on first use (dynamic import of `browser/platform.js` when
229
+ `window`/`document` exist, otherwise `node/platform.js`), which keeps `node:http` out of
230
+ browser bundles and DOM code out of Node.
231
+
232
+ ## 10. Architectural trade-offs
233
+
234
+ | Trade-off | Choice | Consequence |
235
+ |---|---|---|
236
+ | Redirect lifecycle vs. promise ergonomics | `login()`'s promise does not settle when navigation happens | Applications must call `login()` again on the callback page; documented with code. |
237
+ | No proof cache | Re-sign every attempt | Slight CPU cost per request (one ECDSA signature); replay protection stays intact. |
238
+ | `redirect: "manual"` everywhere | Never re-attach credentials on redirects | Browsers surface redirects to the app instead of following them. |
239
+ | Custom Turtle subset | ~200 lines instead of an RDF dependency | Documented, tested subset + JSON-LD + `Link` fallback; unsupported syntax fails loudly. |
240
+ | Static client identity | No DCR | One published JSON-LD file must list every redirect URI. |
241
+ | Loopback-only Node login | Real HTTP callback | Requires `openAuthorizationUrl`; impossible flows fail with `SebaNoUIError`. |
242
+ | `cnf.jkt` requirement gated on advertised `webid` scope | Interoperable and strict | Providers that do not advertise `webid` get standard OIDC validation only. |
@@ -0,0 +1,195 @@
1
+ # BUILD_REPORT — seba 0.2.0
2
+
3
+ Date: 2026-11-05
4
+ Environment: Linux container, Node.js v26.12.1, npm 12.2.1, TypeScript 5.9.3, Vitest 5.1.0,
5
+ Vite 8.3.6. Chrome binary present; no Firefox/Safari.
6
+
7
+ ## 1. Gate results (actual, in the required order)
8
+
9
+ | Gate | Command | Exit | Result |
10
+ |---|---|---|---|
11
+ | Install | `npm install` | 0 | 0 vulnerabilities |
12
+ | Typecheck | `npm run typecheck` = `tsc --noEmit && tsc -p tsconfig.examples.json` | 0 | No errors in `src/`, `tests/` or `examples/` |
13
+ | Tests | `npm test` | 0 | 9 files, **123/123** tests passed |
14
+ | Build | `npm run build` | 0 | `dist/` emitted |
15
+ | Pack | `npm pack --dry-run` | 0 | `seba-0.2.0.tgz`, 86 files, 97.8 kB packed, 373.7 kB unpacked |
16
+
17
+ Test summary as printed:
18
+
19
+ ```
20
+ Test Files 9 passed (9)
21
+ Tests 123 passed (123)
22
+ ```
23
+
24
+ Coverage changed since 0.1.0: discovery gained three issuer-canonicalization tests and the
25
+ session suite's "different WebID on refresh" test now expects the stricter
26
+ `SebaSessionExpiredError` + cleared storage behaviour.
27
+
28
+ ## 2. `npm pack --dry-run` output
29
+
30
+ Captured from the final gate run (the listing was taken immediately before this report's
31
+ last wording change, so this file's own byte count differs by less than 1 kB; the pack was
32
+ re-run after writing it and again included `BUILD_REPORT.md` and exited 0):
33
+
34
+ ```
35
+ npm notice 📦 seba@0.2.0
36
+ npm notice Tarball Contents
37
+ npm notice 15.6kB ARCHITECTURE.md
38
+ npm notice 6.1kB BUILD_REPORT.md
39
+ npm notice 3.1kB CHANGELOG.md
40
+ npm notice 1.1kB LICENSE
41
+ npm notice 10.7kB README.md
42
+ npm notice 11.7kB RESEARCH.md
43
+ npm notice 16.9kB SECURITY.md
44
+ npm notice 10.1kB VALIDATION_REPORT.md
45
+ npm notice 325B dist/browser/index.d.ts
46
+ npm notice 191B dist/browser/index.d.ts.map
47
+ npm notice 323B dist/browser/index.js
48
+ npm notice 189B dist/browser/index.js.map
49
+ npm notice 517B dist/browser/platform.d.ts
50
+ npm notice 282B dist/browser/platform.d.ts.map
51
+ npm notice 2.8kB dist/browser/platform.js
52
+ npm notice 2.1kB dist/browser/platform.js.map
53
+ npm notice 609B dist/browser/provider-modal.d.ts
54
+ npm notice 255B dist/browser/provider-modal.d.ts.map
55
+ npm notice 5.6kB dist/browser/provider-modal.js
56
+ npm notice 4.1kB dist/browser/provider-modal.js.map
57
+ npm notice 1.4kB dist/browser/storage.d.ts
58
+ npm notice 526B dist/browser/storage.d.ts.map
59
+ npm notice 4.3kB dist/browser/storage.js
60
+ npm notice 3.2kB dist/browser/storage.js.map
61
+ npm notice 2.1kB dist/core/client.d.ts
62
+ npm notice 630B dist/core/client.d.ts.map
63
+ npm notice 15.3kB dist/core/client.js
64
+ npm notice 12.3kB dist/core/client.js.map
65
+ npm notice 3.8kB dist/core/discovery.d.ts
66
+ npm notice 900B dist/core/discovery.d.ts.map
67
+ npm notice 11.5kB dist/core/discovery.js
68
+ npm notice 7.0kB dist/core/discovery.js.map
69
+ npm notice 2.4kB dist/core/dpop.d.ts
70
+ npm notice 979B dist/core/dpop.d.ts.map
71
+ npm notice 5.8kB dist/core/dpop.js
72
+ npm notice 4.7kB dist/core/dpop.js.map
73
+ npm notice 3.5kB dist/core/errors.d.ts
74
+ npm notice 1.5kB dist/core/errors.d.ts.map
75
+ npm notice 3.7kB dist/core/errors.js
76
+ npm notice 2.1kB dist/core/errors.js.map
77
+ npm notice 3.0kB dist/core/http.d.ts
78
+ npm notice 1.1kB dist/core/http.d.ts.map
79
+ npm notice 4.6kB dist/core/http.js
80
+ npm notice 3.0kB dist/core/http.js.map
81
+ npm notice 6.1kB dist/core/identity.d.ts
82
+ npm notice 2.0kB dist/core/identity.d.ts.map
83
+ npm notice 28.3kB dist/core/identity.js
84
+ npm notice 23.5kB dist/core/identity.js.map
85
+ npm notice 6.4kB dist/core/oidc.d.ts
86
+ npm notice 3.1kB dist/core/oidc.d.ts.map
87
+ npm notice 18.3kB dist/core/oidc.js
88
+ npm notice 14.1kB dist/core/oidc.js.map
89
+ npm notice 4.8kB dist/core/session.d.ts
90
+ npm notice 1.9kB dist/core/session.d.ts.map
91
+ npm notice 13.7kB dist/core/session.js
92
+ npm notice 9.7kB dist/core/session.js.map
93
+ npm notice 1.0kB dist/core/storage.d.ts
94
+ npm notice 401B dist/core/storage.d.ts.map
95
+ npm notice 1.4kB dist/core/storage.js
96
+ npm notice 891B dist/core/storage.js.map
97
+ npm notice 706B dist/index.d.ts
98
+ npm notice 443B dist/index.d.ts.map
99
+ npm notice 279B dist/index.js
100
+ npm notice 286B dist/index.js.map
101
+ npm notice 317B dist/node/index.d.ts
102
+ npm notice 188B dist/node/index.d.ts.map
103
+ npm notice 315B dist/node/index.js
104
+ npm notice 186B dist/node/index.js.map
105
+ npm notice 1.2kB dist/node/platform.d.ts
106
+ npm notice 331B dist/node/platform.d.ts.map
107
+ npm notice 7.2kB dist/node/platform.js
108
+ npm notice 4.7kB dist/node/platform.js.map
109
+ npm notice 1.6kB dist/seba.d.ts
110
+ npm notice 542B dist/seba.d.ts.map
111
+ npm notice 2.1kB dist/seba.js
112
+ npm notice 1.2kB dist/seba.js.map
113
+ npm notice 10.8kB dist/types.d.ts
114
+ npm notice 4.3kB dist/types.d.ts.map
115
+ npm notice 274B dist/types.js
116
+ npm notice 115B dist/types.js.map
117
+ npm notice 2.2kB examples/node/index.ts
118
+ npm notice 3.7kB examples/react/App.tsx
119
+ npm notice 299B examples/react/index.html
120
+ npm notice 319B examples/react/main.tsx
121
+ npm notice 644B examples/react/vite.config.ts
122
+ npm notice 1.9kB package.json
123
+ npm notice Tarball Details
124
+ npm notice name: seba
125
+ npm notice version: 0.2.0
126
+ npm notice filename: seba-0.2.0.tgz
127
+ npm notice package size: 97.8 kB
128
+ npm notice unpacked size: 373.7 kB
129
+ npm notice total files: 86
130
+ ```
131
+
132
+ The tarball contains `README.md`, `CHANGELOG.md`, `BUILD_REPORT.md`, `SECURITY.md`,
133
+ `VALIDATION_REPORT.md`, `dist/` and `examples/`. It contains **no** `node_modules`, no test
134
+ files or fixtures, and no `css-data` directory. The generated `examples/react/dist` bundle
135
+ was removed before this run; the React example ships as sources plus its Vite config.
136
+
137
+ ## 2b. v0.2.0 external validation status
138
+
139
+ > **v0.2.0 external validation status:**
140
+ > - Local CSS 7.3.0: login **fails at ID token validation** due to CSS non-compliance (see CSS_ISSUE.md). Seba's rejection is correct per Solid-OIDC §8.1.
141
+ > - Public providers: discovery tested against `solidcommunity.net`, `solidweb.org`, `solidweb.me`. Full cycle not completed (account creation requires interactive browser). Trailing-slash issuer bug fixed.
142
+ > - Browsers: unit tests for browser storage pass; no real-browser E2E performed.
143
+ > - This release is **not** claimed production-ready. See `VALIDATION_REPORT.md`.
144
+
145
+ Upstream report: https://github.com/CommunitySolidServer/CommunitySolidServer/issues/2295
146
+
147
+ ## 3. External validation summary
148
+
149
+ Full transcripts, error messages and blockers are in `VALIDATION_REPORT.md`.
150
+
151
+ - **Community Solid Server 7.3.0 (real):** discovery, Client ID Document fetch,
152
+ PKCE/state/nonce, account/WebID/consent interaction, callback, and the DPoP token request
153
+ all succeeded. The login is rejected at ID token validation because CSS issues
154
+ `aud` as a string containing only the client ID (no `"solid"`) and no `cnf` claim;
155
+ Solid-OIDC §8.1 requires an array with `"solid"` and a `cnf` binding. The library was not
156
+ weakened to accommodate a non-conformant provider.
157
+ - **Public providers:** discovery through SEBA's real code succeeded for
158
+ `solidcommunity.net` (issuer `https://solidcommunity.net/`), `solidweb.org` (issuer
159
+ `https://solidweb.org`, no revocation endpoint) and `solidweb.me` (issuer
160
+ `https://solidweb.me/`). No account could be created, so no public full cycle is claimed.
161
+ - **Browser:** not executed. Chrome is installed but Firefox/Safari are not, and a real
162
+ provider login is blocked as above. No browser matrix is claimed green.
163
+ - **Examples:** `tsc -p tsconfig.examples.json` and `vite build` succeed; runtime login is
164
+ blocked by the provider issue.
165
+
166
+ ## 4. Changes made because of validation
167
+
168
+ 1. **Issuer canonicalization** (`src/core/discovery.ts`): accept the empty-path
169
+ trailing-slash form of a path-less issuer and adopt the provider's exact spelling for
170
+ all later comparisons. Motivated by real providers (solidcommunity.net, solidweb.me,
171
+ CSS). Paths, ports, schemes and query strings still require exact equality. Three tests
172
+ added.
173
+ 2. **Refresh identity mismatch is terminal** (`src/core/session.ts`): refresh-time ID token
174
+ validation failure now clears storage and throws `SebaSessionExpiredError` instead of
175
+ leaving an unusable grant. Stricter; regression test updated.
176
+ 3. **Examples in the typecheck graph** (`tsconfig.examples.json`, package scripts), plus
177
+ runnable Vite scaffolding for the React example and an env-driven Node example.
178
+ 4. Docs reconciled with reality: provider compatibility table, security audit findings,
179
+ validation report, changelog, version 0.2.0.
180
+
181
+ ## 5. Still unvalidated (explicit)
182
+
183
+ - Full login → fetch → restore → logout against any public provider (no account).
184
+ - Any real-browser run (no Firefox; no provider login that reaches a session).
185
+ - Safari `CryptoKey` persistence and private-browsing IndexedDB behaviour.
186
+ - IndexedDB quota-exceeded and corrupted-record handling in a real browser (unit-covered,
187
+ not browser-executed).
188
+ - Real pod resource access with a provider-issued access token (blocked at ID token).
189
+ - No automated accessibility audit of the browser dialog.
190
+
191
+ ## 6. Release notes
192
+
193
+ Version bumped to `0.2.0`. This is a validation release with one interop fix and one
194
+ security hardening change; it is **not** “production ready” in the sense of having passed
195
+ real-provider and browser gates, and no document here claims that.
package/CHANGELOG.md ADDED
@@ -0,0 +1,86 @@
1
+ # Changelog
2
+
3
+ ## [0.2.1] — 2026-11-05
4
+
5
+ Documentation-only patch. No runtime behaviour changed; the only functional difference is
6
+ the wording of one error message.
7
+
8
+ ### Fixed
9
+
10
+ - **Import paths in the shipped documentation.** The root entry point's JSDoc example still
11
+ read `import { Seba } from "seba"`, a name that npm rejects, and the `SebaConfigError`
12
+ thrown when the browser entry point runs without a DOM still told users to import
13
+ `"seba"` / `"seba/node"`. Both now use `@gaballahtech/seba` and
14
+ `@gaballahtech/seba/node`, and the browser/Node subpath JSDoc headers record their scoped
15
+ names. These strings are what `dist/**/*.d.ts` and the runtime error expose to consumers,
16
+ so 0.2.0 shipped documentation that could not be followed.
17
+
18
+ Persistence identifiers are deliberately unchanged: the IndexedDB database is still named
19
+ `seba` and the pending-authorization key is still `seba.pending-authorization`, so sessions
20
+ stored by 0.2.0 keep working after the upgrade.
21
+
22
+ ## [0.2.0] — 2026-11-05
23
+
24
+ External validation and hardening release. This is not a claim of production readiness; the
25
+ gates that could not run are listed explicitly.
26
+
27
+ ### Fixed because of external validation
28
+
29
+ - **Issuer canonicalization.** Real providers publish path-less issuers with a trailing
30
+ slash (`https://solidcommunity.net/`, `https://solidweb.me/`, Community Solid Server's
31
+ `http://localhost:3000/`). SEBA required byte-identical strings and rejected the document
32
+ when the caller typed the host without the slash. Discovery now accepts that one
33
+ canonical form and adopts the provider's exact spelling for all later comparisons;
34
+ paths, ports, schemes and query strings still require exact equality. Verified against
35
+ solidcommunity.net, solidweb.org, solidweb.me and CSS 7.3.0.
36
+
37
+ ### Fixed from the manual security audit
38
+
39
+ - A refresh whose ID token fails validation (including a WebID that differs from the stored
40
+ session) now clears the stored grant and throws `SebaSessionExpiredError`. It previously
41
+ kept the unusable record and threw `SebaAuthError`, which would repeat the failure on
42
+ every restore. Stricter, covered by a regression test.
43
+
44
+ ### Added
45
+
46
+ - `tsconfig.examples.json`; `npm run typecheck` now typechecks `examples/` as well.
47
+ - `npm run typecheck:examples`, `npm run example:react:build`, `npm run validate:discovery`.
48
+ - Runnable React example scaffolding (`examples/react/index.html`, `main.tsx`,
49
+ `vite.config.ts`) and an env-driven Node example.
50
+ - `scripts/validation/css-setup.mjs` and `css-flow.mjs`: a reproducible real-provider flow
51
+ against a local Community Solid Server, including the account/pod setup and the browser
52
+ interaction driven through CSS's JSON API.
53
+ - `scripts/validation/public-discovery.mjs`: discovery checks against public providers.
54
+ - `VALIDATION_REPORT.md`, this changelog, and a "Provider Compatibility" table in README.
55
+
56
+ ### Changed
57
+
58
+ - **Package name.** `seba` → `@gaballahtech/seba`. npm rejected the unscoped name with
59
+ `E403 Package name too similar to existing packages ses,send,eta,tebs,web3` and suggested
60
+ the scoped alternative. The import path is now `@gaballahtech/seba`, with
61
+ `@gaballahtech/seba/node` and `@gaballahtech/seba/browser` for the subpath entry points.
62
+ All examples and documentation were updated. Version is unchanged at `0.2.0`.
63
+
64
+ ### Validated externally
65
+
66
+ - Community Solid Server 7.3.0: discovery, Client ID Document fetch, PKCE/state/nonce
67
+ authorization, consent, callback, DPoP token request and token response all succeeded.
68
+ Login stops at ID token validation because CSS emits a string `aud` without `"solid"` and
69
+ no `cnf` claim, which Solid-OIDC §8.1 requires.
70
+ - solidcommunity.net, solidweb.org and solidweb.me: discovery and issuer handling verified
71
+ through SEBA's real discovery code; the first two publish trailing-slash issuers, which
72
+ exposed the normalization bug fixed above.
73
+ - One real CSS failure was caused by **provider non-compliance**, not by Seba: CSS 7.3.0
74
+ emits `id_token.aud` as a string without `"solid"` and omits `cnf.jkt`, contrary to
75
+ Solid-OIDC §8.1. The failure is documented in `CSS_ISSUE.md` and reported upstream.
76
+
77
+ ### Not validated (explicitly)
78
+
79
+ - No full login → fetch → restore → logout cycle against any public provider: creating an
80
+ account requires an interactive browser, email and, on some deployments, a CAPTCHA.
81
+ - No browser end-to-end run. Chrome is installed, Firefox/Safari are not. The 5-step browser
82
+ matrix was not executed.
83
+ - Safari `CryptoKey` persistence and IndexedDB private-browsing behaviour were not executed.
84
+ - IndexedDB quota-exceeded and corrupted-record behaviour in a real browser was not
85
+ executed; the unit tests cover the adapter and the failure paths.
86
+ - No real pod resource was fetched with a CSS-issued token (blocked by the ID token issue).
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SEBA contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.