@jimhoyd/urlcode-auth 0.1.0-alpha.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.
@@ -0,0 +1,37 @@
1
+ # Auth implementation status
2
+
3
+ Status: `@jimhoyd/urlcode-auth` 0.1.0-alpha.1 is the first npm release (core issue #78); it requires the published `@jimhoyd/urlcode` 0.4.x and `@jimhoyd/urlcode-ui` 0.1.x alphas. The implemented auth and shared-presentation work is merged to main. The source plan is URLCode PR #54; cross-repository release acceptance is tracked in https://github.com/jimhoyd-com/urlcode/issues/58. The generic core extension contract from PR #59 is merged. Implementation and synthetic acceptance do not establish production readiness.
4
+
5
+ Implemented and covered by automated tests: durable SQLite accounts; bounded scrypt and hash migration; email/password and numeric email codes; OIDC with explicit linking; Google/Apple adapters; WebAuthn registration, login and step-up; TOTP/recovery; opaque sessions and revocation; role ceilings; registration modes; terms and scoped metadata; email change cooldown/cancellation; deletion grace; exports; key rotation; backup/restore; operator CLI/scaffolding; SES/development senders; safe themes and locale catalogue; admin service operations including dual-approval cases and bounded impersonation. Device recognition supports notices; separate opt-in, revocable remembered-device authority can exempt ordinary MFA without granting fresh step-up. Explicit passkey second-factor enrollment requires an independent credential. Optional breach checking is an operator-selected external service.
6
+
7
+ Resumable verification-first password/passkey signup (including waitlist approval) and opt-in email-mediated factor recovery with a 24-hour cancellation window and recovery-session-only reenrollment are implemented.
8
+
9
+ Mandatory verification/TOTP enrollment, operator standard/hardened presets and explicit pinned configuration migration are implemented; hardened requires email and breach-screening adapters.
10
+
11
+ Kit adoption (urlcode-auth issue #9, core plan §7.2) is implemented: every account screen is an `auth/*` kit template with a declared view model and sample view (`authTemplates`, `authUiTemplates`, `authCatalogue`); `authExtension({ ui })` renders through `ui.kit.page` when the host supplies the `ui` extension and through the shared primitives otherwise. The HTTP suites run under both render paths; a doctor-style suite renders every template with its sample and with the view a real request computes, checks escaping of user-controlled values on kit pages and the nonce-bound CSP. A themed browser walkthrough of the account pages remains a manual acceptance step.
12
+
13
+ ## Additional implemented acceptance
14
+
15
+ - Bounded localized email copy, durable progressive password backoff, trusted-client and signup-domain velocity budgets, optional fixed-origin Turnstile verification/widget, and pinned disposable-domain data.
16
+ - Integrated maker/checker manual recovery, staged administrative account actions and audited identifier reveal/notes. Manual recovery is an operator process; public lost-everything intake and recovery contacts remain later scope.
17
+ - Offline `auth-baseline` runs 17 synthetic checks on a passing run (extra failure-only markers are recorded when a probe, deadline or cleanup fails); anonymous `verify-deployment` inspects headers/cookies without claiming provider readiness.
18
+ - Local browser walkthrough exercised identifier-first password login, account page, admin dashboard, filtered directory and masked detail. It found and corrected the no-referrer/Origin form failure. This is not a complete WCAG 2.2 AA assessment.
19
+
20
+ - The source-only [synthetic recovery drill](RECOVERY-DRILL.md) exercises online backup, isolated reopen, configuration/key refusal and explicit session revocation after snapshot restore. Its 18 checks do not establish production disaster recovery or RTO/RPO.
21
+
22
+ ## Remaining first-release acceptance
23
+
24
+ - Complete accessibility assessment, browser/device WebAuthn coverage, deployment/soak/backup-recovery exercises and independent security review.
25
+ - Operator wiring of sender monitoring and lifecycle delivery policy. Hooks and security notices are best-effort after commit, without a durable retry queue (signed webhooks/retries are later scope).
26
+ - Refresh the recorded package and CI evidence whenever code or dependency pins change; the merged implementation baseline is recorded in ACCEPTANCE.md.
27
+
28
+ Live Google/Apple/SES testing is explicitly deferred by the project owner and is not a blocker for local implementation. It remains unverified. Synthetic signed protocol tests do not establish vendor configuration or delivery readiness.
29
+
30
+ ## Agreed architecture corrections
31
+
32
+ Auth/admin live in independent repositories. The core owns generic revision-pinned extension contracts and never depends on auth. SQLite and privileged transactions belong to the trusted operator service. Project YAML cannot select host modules or credentials. Safe package renderers replace arbitrary project templates. The initial auth target is Node with operator-owned durable storage; runtime adapter availability does not make this SQLite service portable to every deployment target.
33
+
34
+ ## Recorded acceptance
35
+
36
+ See [ACCEPTANCE.md](https://github.com/jimhoyd-com/urlcode-auth/blob/main/ACCEPTANCE.md) for exact merged revisions, automated coverage,
37
+ clean-install evidence and the remaining operational validation boundary.
package/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2026 Jimmy Ho
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,261 @@
1
+ # URLCode auth
2
+
3
+ An optional, operator-installed authentication extension for URLCode. This repository contains the Node/SQLite implementation: password and passkey authentication, OpenID Connect, email codes, TOTP, recovery codes, versioned registration profiles, account lifecycle operations, administrative service operations and trusted HTML pages.
4
+
5
+ [![Verify](https://github.com/jimhoyd-com/urlcode-auth/actions/workflows/verify.yml/badge.svg)](https://github.com/jimhoyd-com/urlcode-auth/actions/workflows/verify.yml)
6
+
7
+ The implementation is under active review. Local tests and builds are evidence of those checks, not an independent security assessment, production deployment, provider certification or recovery/soak result. See [SECURITY.md](SECURITY.md) for the trust boundary and [the first-release coverage review](docs/SPIKE-AUTH.md) for the proposal; the proposal is not a list of completed features.
8
+
9
+ ## Install
10
+
11
+ `@jimhoyd/urlcode-auth` is published to npm as an alpha alongside its peers. Install the three packages together; the peer ranges in `package.json` require `@jimhoyd/urlcode` 0.4.0-alpha.1 or a later 0.4.x and `@jimhoyd/urlcode-ui` 0.1.0-alpha.1 or a later 0.1.x, and the release workflow builds and tests against exactly those registry versions.
12
+
13
+ ```sh
14
+ npm install @jimhoyd/urlcode @jimhoyd/urlcode-ui @jimhoyd/urlcode-auth
15
+ npx urlcode init my-site --with auth
16
+ ```
17
+
18
+ `urlcode init --with auth` is core's layered scaffold; `npx urlcode-auth init --directory /absolute/new-account-site` scaffolds an auth-only project. Either writes `app/urlcode.yaml`, external `host.mjs` and `operator-service.mjs`, a private `data/` directory and independent encryption/CSRF keys, and refuses an existing destination. Its README gives the exact next steps.
19
+
20
+ Alpha caveat: the source is complete for the first release and its automated checks pass, but independent security review, accessibility assessment, browser/device WebAuthn coverage and deployment/soak/recovery exercises are still pending (see [IMPLEMENTATION-STATUS.md](IMPLEMENTATION-STATUS.md)). Alpha versions may change public exports, configuration keys and the SQLite schema between releases without a migration path. Do not run an alpha on production accounts.
21
+
22
+ Use a current supported Node release with a patched SQLite build. The actual runtime requirement is a Node build whose bundled SQLite (`process.versions.sqlite`) is 3.51.3 or newer, or a patched 3.50.7+ / 3.44.6+ branch release; `engines.node` alone does not encode this, and the service (`src/auth-store.ts`) refuses other builds with `patched_sqlite_required` even when the package's minimum Node version is satisfied.
23
+
24
+ Every release tarball is attested from the tagged commit: `gh attestation verify jimhoyd-urlcode-auth-<version>.tgz --repo jimhoyd-com/urlcode-auth`. `npm view @jimhoyd/urlcode-auth` shows the published provenance.
25
+
26
+ ## Install from reviewed source
27
+
28
+ Operators who pin exact reviewed commits rather than registry versions can build the same packages locally. A registry version alone does not establish that a revision was reviewed: this implementation requires the core extension contract introduced by [core PR #59](https://github.com/jimhoyd-com/urlcode/pull/59). Use its reviewed implementation or a reviewed successor containing it, pinned to an exact commit. Do not infer approval from the current branch name.
29
+
30
+ This package also depends on the shared `@jimhoyd/urlcode-ui` peer, which owns document layout, semantic fields, escaping, themes and the locale engine; authentication/administration behavior remains here. Core can use UI without auth/admin. Source CI (`verify.yml`) checks the peers out at the commits in [`peers.json`](peers.json) and needs the narrow `URLCODE_UI_READ_TOKEN`; the release workflow resolves them from the registry instead.
31
+
32
+ Each repository has a lockfile. The source packaging helper installs dependencies with lifecycle scripts disabled, builds the reviewed packages (core, then UI, then their consumers), installs local peer tarballs in dependency order and writes package integrity/revision metadata. It does not publish. All source trees must be committed and clean. `--core`, `--auth`, `--ui`, `--core-revision` and `--out` are required. Replace these illustrative paths with your reviewed locations. `--core-revision` defaults to the `urlcode` entry in [`peers.json`](peers.json), the single source of verified peer revisions; pass it explicitly only to override:
33
+
34
+ ```sh
35
+ node scripts/pack-sources.mjs \
36
+ --core /absolute/source/urlcode \
37
+ --ui /absolute/source/urlcode-ui \
38
+ --auth /absolute/source/urlcode-auth \
39
+ --admin /absolute/source/urlcode-admin \
40
+ --core-revision REVIEWED_40_CHARACTER_COMMIT_SHA \
41
+ --out /absolute/new-private-package-directory
42
+ ```
43
+
44
+ Omit `--admin` for auth only. `--offline` forbids network package resolution and requires a populated dependency cache. `--skip-install` reuses installed third-party dependencies; local peer tarballs are still installed. The script does not alter dependency manifests or lockfiles. Run `npm run verify` in each repository separately; source packaging runs typecheck/build, not the HTTP suite.
45
+
46
+ Install all required local tarballs together (core, UI and auth; admin if built) in an operator-owned directory with a private `package.json`. For example, after checking the manifest:
47
+
48
+ ```sh
49
+ npm install /absolute/packages/jimhoyd-urlcode-0.4.0-alpha.1.tgz /absolute/packages/jimhoyd-urlcode-ui-0.1.0-alpha.1.tgz /absolute/packages/jimhoyd-urlcode-auth-0.1.0-alpha.1.tgz
50
+ npx urlcode-auth init --directory /absolute/new-account-site
51
+ ```
52
+
53
+ Tarball names and versions must match the generated manifest. Install the same reviewed local tarballs inside the generated directory so its host modules resolve them.
54
+
55
+ ## Programmatic scaffold
56
+
57
+ `scaffold({directory, project, hostFile, names})` returns the auth pieces of a layered project (YAML fragments, host imports and entries, private files with in-memory key material, a README section and next steps) without writing anything; `initAuthentication` is assembled from it. Core's `urlcode init --with auth` calls this export and merges it with other extensions.
58
+ Exported types: `ScaffoldRequest`, `ScaffoldResult`, `ScaffoldFile`.
59
+
60
+ ## Operator activation
61
+
62
+ Route YAML declares a versioned logical extension, not executable code:
63
+
64
+ ```yaml
65
+ version: '1'
66
+ extensions:
67
+ auth:
68
+ version: '1'
69
+ config:
70
+ registration: 'off'
71
+ routes:
72
+ /account/*:
73
+ extension: auth
74
+ methods: [GET, HEAD, POST]
75
+ /private:
76
+ respond: {text: Signed in}
77
+ policies:
78
+ extensions:
79
+ auth: {}
80
+ ```
81
+
82
+ The external host creates an AuthService and supplies `authExtension({service, csrfKey, projectSha256})`. The generated host requires a canonical HTTPS `AUTH_ORIGIN` and a static `PROJECT_SHA256` copied after review. Inspecting a revision with `inspectExtensionRevision(project)` grants nothing; never compute and automatically approve the current project during activation. The runtime receives the same origin through `--origin` and loads the absolute external host with `--host-file`. Guest/application code never chooses the module, database path, keys, sender credentials or grants.
83
+
84
+ Registration starts off. Bootstrap the first administrator through `urlcode-auth bootstrap --operator-file /absolute/operator-service.mjs`, supplying `{email,password}` as bounded JSON on stdin. Never place passwords in command arguments or source files. The command returns account metadata, not the session token. A role/default-role configuration change is a reviewed operator change, not an administration-page edit.
85
+
86
+ ## Authentication and presentation
87
+
88
+ `createAuthService` owns a private SQLite database outside the application directory. Its operations enforce authority, fresh authentication, delegation ceilings, replay protection and transaction boundaries. Callers must preserve the distinction between unrestricted operator APIs and actor-token administrative APIs. `authExtension` adds HTTP cookies, same-origin CSRF checks, bounded bodies and trusted pages.
89
+
90
+ Optional factories supply Google/Apple/generic OIDC and passkey providers. Unconfigured providers are not offered. Synthetic cryptographic fixtures do not prove real Google, Apple, authenticator or SES deployment behavior. The auth extension currently declares **Node only**; generic core extension support for AWS/Vercel does not make this SQLite service portable to their deployment environments.
91
+
92
+ `createPresentation` supplies configured locale catalogues, plural rules, RTL, and validated theme variables/local logo paths. Messages are plain text and escaped by renderers. It does not load executable project templates or arbitrary HTML/CSS. Translation coverage and accessibility require review; the helper does not establish WCAG conformance. Registration metadata is descriptive data and never authorization authority. Private metadata is excluded from public projections; public and unsafe fields remain untrusted.
93
+
94
+ ## Optional breached-password screening
95
+
96
+ An operator can configure `checkPassword: createPasswordBreachChecker()` on `createAuthService`. This optional Have I Been Pwned range check sends only the SHA-1 prefix, requests padded responses, bounds concurrency/deadline/response bytes, and fails closed when the check cannot complete. It does not send the password or full hash to the service. Configuring the callback introduces an external service dependency; do not enable it silently or describe it as a complete hardened preset. Offline fixtures are not evidence of live service availability.
97
+
98
+ ## Email and local development
99
+
100
+ `createSesSender({region, from, origin, authMount, credentials?})` returns a callable token sender with `sendEmailCode`, `notify` and `close`. Wire callbacks explicitly into the auth/admin factories. Production credentials come from operator configuration or the SDK credential chain; never put them in YAML. Delivery is bounded and cancellable; services cannot guarantee that an email reaches an inbox.
101
+
102
+ `createDevelopmentSender` requires `allowDevelopment: true` and either a private output directory outside the project plus its `projectRoot`, or `allowConsoleTokens: true`. File notices use exclusive `0600` files and a bounded count. Console mode deliberately exposes development tokens and must never feed shared production logs. Sender helpers do not infer safety from `NODE_ENV`.
103
+
104
+ ## Operations and recovery
105
+
106
+ Run `urlcode-auth --help` for the current CLI. Operator commands have full database authority; stdin avoids putting secrets in process arguments. User/audit listings currently return a bounded page; use service pagination for complete exports. Doctor's successful local database check is not live-provider verification.
107
+
108
+ | Command | Arguments | Purpose |
109
+ | --- | --- | --- |
110
+ | `init` | `--directory NEW_DIRECTORY` | Scaffold a new account site; refuses an existing destination |
111
+ | `bootstrap` | `--operator-file`, JSON `{email,password}` on stdin | Create the first administrator; returns account metadata, not a session token |
112
+ | `users` | `--operator-file` | List accounts (bounded page of 100) |
113
+ | `sessions` | `--operator-file`, JSON `{accountId}` on stdin | List an account's sessions |
114
+ | `revoke` | `--operator-file`, JSON `{accountId}` on stdin | Revoke all sessions of an account |
115
+ | `audit` | `--operator-file` | List audit events (bounded page of 100) |
116
+ | `import` | `--operator-file`, JSON `{users:[{email,passwordHash,emailVerified?}]}` on stdin | Import generic password hashes; only those fields are accepted |
117
+ | `rotate-key` | `--operator-file` | Re-encrypt records with the active encryption key; reports changed/remaining |
118
+ | `purge` | `--operator-file` | Permanently remove accounts whose deletion grace has elapsed |
119
+ | `cleanup` | `--operator-file` | Sweep expired sessions/tokens (bounded batch) |
120
+ | `configuration` | `--operator-file` | Print configuration revision, registration mode, security policy and roles |
121
+ | `doctor` | `--operator-file` | Local database/configuration readiness check |
122
+ | `validate` | `--operator-file` | Offline validation of the loaded service's configuration |
123
+ | `auth-baseline` | none (refuses `--operator-file`) | Offline synthetic checks against a temporary runtime |
124
+ | `verify-deployment` | JSON `{origin,authMount,allowDevelopment?,allowTurnstile?}` on stdin | Anonymous header/cookie checks of a deployed site |
125
+ | `backup` | JSON `{database,destination,projectRoot}` on stdin | Online SQLite backup to a new private path |
126
+ | `restore` | JSON `{backup,destination,projectRoot}` on stdin | Restore a backup to a new private path |
127
+
128
+ `--operator-file` is an absolute path to a module that default-exports an `AuthService`.
129
+
130
+ Backup/restore accepts JSON paths on stdin. `createBackup({database,destination,projectRoot})` uses SQLite's online backup API, including committed WAL pages, with a bounded worker and integrity checks. `restoreBackup({backup,destination,projectRoot})` restores to a **new** path. Both require private operator paths outside the project and refuse overwrite. Never copy only a live `.sqlite` file and assume its WAL is included.
131
+
132
+ Back up encryption keys, CSRF keys and reviewed static configuration separately. Database snapshots contain sensitive account/audit data and password hashes, but do not export key files. Restoring historical data also restores historical sessions/tokens and revocation state: plan revocation and recovery before reopening traffic. Rotate keys by adding a new active key, retaining decryption keys while bounded migration reports remaining records, then remove old keys only after completion and backup verification. Old writers fail closed after activation switches. Keep a tested isolated restore procedure.
133
+
134
+ The host owns the shared service and sender lifecycle. Close them once after all extension runtimes stop. Scheduled purge/sweep operation and backups are operator responsibilities; opportunistic cleanup is not a retention policy.
135
+
136
+ Apache-2.0. `release.yml` publishes a tagged commit that is on `main` to npm through trusted publishing when the repository variable `PUBLISH_NPM` is `true`; `verify.yml` publishes nothing.
137
+
138
+ ## Operator presets and enrollment
139
+
140
+ `createAuthPreset({preset: 'standard', origin, rpName, sender?})` supplies passkeys, standard session limits and seven-day deletion grace. Without a sender it returns an explicit notice that email flows are unavailable. TOTP and recovery are service capabilities; remembered devices can optionally exempt ordinary MFA, but never grant fresh step-up authority.
141
+
142
+ `createAuthPreset({preset: 'hardened', origin, rpName, sender, checkPassword: createPasswordBreachChecker()})` requires both adapters and supplies mandatory email verification followed by TOTP enrollment, shorter sessions and 30-day deletion grace. Spread `preset.service` into `createAuthService` and `preset.extension` into `authExtension`, together with your operator paths, keys and static project pin. Choosing the online breach checker makes password creation/reset depend on that external service; inject an approved local checker if needed. Deliberate overrides change the effective policy and should be reviewed.
143
+
144
+ Restricted enrollment sessions can verify their email and enroll TOTP, but cannot authorize protected application routes or administration. Required verification revokes old sessions and requires a fresh sign-in before factor enrollment. Public routes without auth policies remain public. These controls do not establish independent security certification or live provider readiness.
145
+
146
+ Configuration is database-pinned. Before changing modes, roles or security requirements, run `urlcode-auth configuration --operator-file /absolute/operator-service.mjs` and retain its revision. Review the new operator configuration and pass `approveConfigurationChangeFrom: 'the-old-64-character-revision'` on the first `createAuthService` startup. The generated operator file accepts that explicit approval through `AUTH_CONFIG_FROM`. A matching already-applied migration can be repeated safely; an unrelated pin fails.
147
+
148
+ Migration preserves accounts, enrolled credentials and history, while revoking sessions and pending authentication/registration state, closing pending cases and recording an audit entry. Existing roles must remain valid and active administration cannot be removed accidentally. Valid pending-deletion cancellation links retain only their original expiry. Old workers reject reads and writes after migration; restart every instance with the reviewed configuration, remove the approval variable, and separately review/pin the changed route project. Schedule the transition as a maintenance operation; do not edit database metadata manually.
149
+
150
+ `englishCatalogue` (also exported as `authCatalogue`) exports the semantic UI keys for catalogue authors. Translations are plain text and escaped at rendering; runtime templates never execute project markup. No complete non-English language pack is bundled. Dates, provider identifiers and user data retain their own values.
151
+
152
+ ## Presentation
153
+
154
+ Every account screen is an `auth/*` template in the urlcode-ui kit language with a declared view model (`authTemplates`, each with a sample view; `authUiTemplates` is the block the `ui` extension takes). The extension computes the view and the template only places it: a template cannot change which steps a flow has, what a form validates, what is escaped, or the CSRF field and headers a page sends. Forms, fields and buttons arrive in the view as renderer-produced markup built by the shared primitives.
155
+
156
+ `authExtension` takes an optional `ui`, the object `createUiExtension` returns. Declare `ui` first in the host file so the runtime activates it before auth; auth reads `ui.kit` per request and never captures it at activation.
157
+
158
+ ```js
159
+ import { createUiExtension } from '@jimhoyd/urlcode-ui/host';
160
+ import { authExtension, authCatalogue, authUiTemplates } from '@jimhoyd/urlcode-auth';
161
+ const ui = createUiExtension({ projectSha256, projectRoot: '/absolute/site', sources: [authCatalogue], extensions: [authUiTemplates] });
162
+ export default { extensions: [ui.registration, authExtension({ service, csrfKey, projectSha256, ui })] };
163
+ ```
164
+
165
+ ```yaml
166
+ extensions:
167
+ ui: { version: "1", config: { theme: { name: Acme }, templates: ui/templates } }
168
+ routes:
169
+ /assets/ui/*: { extension: ui, methods: [GET, HEAD] }
170
+ ```
171
+
172
+ With `ui`, screens render through `ui.kit.page`: the project's theme, layout, hashed stylesheet and copy apply, a project file `ui/templates/auth/<screen>.html` shadows the shipped template, and `urlcode-ui doctor` reports every `auth/*` template behind its view model. Copy then resolves through the kit's presentation, which carries the kit catalogue, the auth catalogue and the project's `extensions.ui` copy; omit `presentation` in that case. If both are given, `presentation` wins and must register the kit catalogue for the layout's own keys.
173
+
174
+ Without `ui`, nothing changes: screens render the same templates through the shared primitives with `presentation` (or the bundled English catalogue). The `presentation` option remains the fallback; core plans to retire it one minor version after the kit path ships. The auth passkey script and the optional challenge widget are nonce-bound on both paths and the page CSP admits only that nonce (plus the challenge origin when configured).
175
+
176
+ Changing `configurationTag` deliberately advances the approved configuration revision for provider/callback/profile-policy deployments that cannot be fingerprinted as simple data. The service does not automatically fingerprint executable callbacks. Session idle and absolute limits do participate in the declared configuration fingerprint.
177
+
178
+ ### Verification-first signup
179
+
180
+ The browser registration entry point resumes a short-lived, browser-bound signup
181
+ wizard. With `requireEmailVerification`, it verifies an emailed numeric code before
182
+ accepting a password or passkey; `sendSignupCode` must be configured. Credentials,
183
+ profile and required consent are finalized together. Open/invited signup creates
184
+ one account/session transaction; waitlist signup creates only a pending application
185
+ until an administrator approves it. Passkey applications retain their credential
186
+ and account binding through approval. Existing accounts are never overwritten:
187
+ the identifier step gives the same next page and sends a registration-attempt
188
+ notice privately. The low-level operator registration/bootstrap methods remain
189
+ explicit privileged provisioning APIs, not public HTTP signup shortcuts.
190
+
191
+ ### Lost second-factor recovery
192
+
193
+ `allowEmailFactorRecovery: true` explicitly enables an email fallback for verified
194
+ accounts that lost their second factor. It is disabled by default because control
195
+ of the mailbox becomes a recovery authority. Configure `sendFactorRecovery`; the
196
+ bundled SES/development senders and presets provide it. The flow confirms a private
197
+ email link in the originating browser, starts a 24-hour waiting period and provides
198
+ a separate cancellation link. GET requests never consume either capability.
199
+
200
+ Completion checks the account version, revokes sessions and pending authority,
201
+ removes the old TOTP/recovery codes, and issues an enrollment-only session. Only that
202
+ recovery session can enroll the replacement factor; ordinary password/provider
203
+ logins cannot race it, even when the site's global MFA requirement is off. No
204
+ application authority returns until the replacement factor is confirmed. Recovery
205
+ state expires, is rate-limited, survives restart and is revoked by configuration
206
+ migration. This is email-based factor recovery, not proof of a person's legal
207
+ identity or the later public lost-everything workflow.
208
+
209
+ ### Anonymous deployment checks
210
+
211
+ Run `urlcode-auth verify-deployment` with bounded JSON on stdin containing the
212
+ canonical HTTPS `origin` and `authMount`. It performs two anonymous GET requests,
213
+ checks the expected login/unauthenticated account status, restrictive CSP,
214
+ no-store, path-private referrer and nosniff headers, and secure host-only cookies. It does not send
215
+ credentials, follow redirects, read response bodies, send email or create accounts.
216
+ A failed check exits nonzero and prints only named booleans, never response bodies
217
+ or network errors. `allowDevelopment: true` permits HTTP only for loopback hosts.
218
+ These checks cover the observed public responses; they do not establish live
219
+ provider readiness, security assessment, recovery or load-test results.
220
+
221
+ ### Passkey second factors and remembered devices
222
+
223
+ Set `allowPasskeySecondFactor: true` to let a user explicitly enroll an owned,
224
+ user-verified passkey as a second factor at `/account/second-factors`. A passkey
225
+ used for primary sign-in cannot also satisfy the second factor in that sign-in.
226
+ WebAuthn challenges bind to the browser; opaque factor proofs are consumed with
227
+ the primary credential, account version and counter in the final transaction.
228
+ TOTP/recovery-code alternatives remain available. Restricted enrollment may add a
229
+ factor through a narrowly scoped path, including the recovery-session grant.
230
+
231
+ Set `trustedDeviceTtlMs` (at most 30 days; default disabled) to offer
232
+ `/account/trusted-devices`. Remembering a device requires recent actual MFA and an
233
+ explicit user action. It creates a separate Secure, HttpOnly, host-only cookie;
234
+ ordinary device recognition is never an MFA exemption. Remembered sign-in has no
235
+ fresh authentication timestamp and cannot satisfy admin/credential step-up or mint
236
+ another exemption. Users can revoke individual remembered devices; account
237
+ security/version changes invalidate them. These options are part of the pinned
238
+ operator configuration and require the explicit migration workflow when changed.
239
+
240
+ `blockDisposableEmails: true` optionally refuses new registrations using the
241
+ bundled disposable-domain snapshot, including subdomains. The dataset revision is
242
+ part of the configuration fingerprint. Existing-account login/recovery is not
243
+ blocked by this policy. Exact operator allow/block lists still apply. The snapshot
244
+ is fallible and may reject legitimate addresses; see
245
+ [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for provenance and CC0 data terms.
246
+
247
+ ### Offline operational checks
248
+
249
+ `urlcode-auth validate --operator-file /absolute/operator/auth.mjs` checks the loaded service's configuration revision, registration mode, bounded role definitions and public security policy. Output contains policy values and aggregate counts, not accounts, credentials, database paths or callback configuration. It requests no migration or account mutation. Loading an operator module executes trusted initialization: use an existing configuration without migration approval, and review that module's own startup behavior. This command does not sandbox operator code or verify providers.
250
+
251
+ `urlcode-auth auth-baseline` requires no operator module and refuses one. It creates private temporary fixtures and an auth database outside the fixture project, runs a bounded child process, then removes them. Seventeen named checks (listed in `test/auth-baseline.test.ts`) exercise the real local runtime without opening a listener: anonymous authorization denial, CSRF and origin enforcement, Secure/HttpOnly/Strict host cookies, no-store auth responses, credential withholding from guest Request and derived header context, revocation, and restricted enrollment authority. Extra failure-only markers are recorded when a probe, deadline or cleanup fails. A failed check or deadline produces a nonzero exit status and redacted results. The command uses no customer state, network, mail or live providers. These synthetic checks are limited regression evidence, not an independent security assessment, deployment certification, browser test, load test or recovery drill.
252
+
253
+ `verify-deployment` remains a separate network check. Its stdin option `allowTurnstile: true` permits only the reviewed `challenges.cloudflare.com` challenge origin in script/frame/connect CSP checks; the default remains strict about external origins. Neither command proves a deployment's provider credentials, delivery, breach callback or complete abuse policy.
254
+
255
+ ### Localized email and abuse controls
256
+
257
+ Pass `emailCopy: createEmailCopy({catalogues: {...}})` to a sender helper to customize bounded plain-text subjects and bodies. Catalogue entries must preserve every link/code placeholder. Account notices use the saved locale; anonymous flows use request language without revealing whether an account exists. Delivery failures for post-commit security notices do not roll back account changes; operators must monitor their sender.
258
+
259
+ `AuthOptions.abuse` enables durable progressive password backoff and trusted-client/signup-domain velocity budgets. Configure the runtime trusted-proxy boundary before enabling client limits. Optional `createTurnstileChallenge` supplies a fixed-origin widget and bounded server verification; challenge success never overrides a hard budget. Provider callbacks and existing token redemption keep their own bound proofs.
260
+
261
+ Auth pages use `Referrer-Policy: strict-origin`: path/query credentials are never sent as referrers, while browsers retain the Origin header needed for no-JavaScript POST forms. Null or foreign Origin headers remain rejected. Live pagination cursors use a process-local HMAC key; restart the search after a worker restart or changed boundary.
package/SECURITY.md ADDED
@@ -0,0 +1,33 @@
1
+ # Security boundary
2
+
3
+ This repository is an actively reviewed implementation, not an independent security assessment or a hostile multi-tenant authentication platform. Report a suspected vulnerability privately using the security reporting channel on the repository hosting this reviewed source; do not include passwords, session cookies, live recovery tokens or customer databases in public issues. If no private reporting channel is configured, ask the maintainer for one before sending sensitive details.
4
+
5
+ ## Trusted and untrusted components
6
+
7
+ Operator modules, their dependencies, configuration, database directory, encryption/CSRF keys, identity providers and mail transport are trusted. Project routes and sandboxed guest code do not gain authority to load host modules. Core extension activation requires an explicitly supplied registry and a reviewed exact project revision pin. Never turn revision inspection into automatic approval.
8
+
9
+ Auth is Node/SQLite only. It refuses unpatched SQLite versions and requires private database files. Keep the database, WAL/SHM, backups, operator modules and key files outside the application project and inaccessible to guest filesystem access. Do not run the host as a shared hostile operating-system user. Filesystem permission and symlink checks do not defend against an attacker who already controls the operator account or its parent directories.
10
+
11
+ **Same-origin browser JavaScript is trusted.** HttpOnly cookies and stripped guest server headers do not isolate frontend JavaScript served on the account origin. Such JavaScript can fetch a CSRF value and send requests with ambient cookies. Serve only reviewed frontend content on that origin, or isolate the authentication surface on a different origin with explicit narrowly scoped integration. A WASM sandbox for server code is not a browser-content sandbox.
12
+
13
+ ## Authentication and authorization
14
+
15
+ Use HTTPS with a canonical operator-specified origin, never an untrusted Host header. Cookies are Secure, HttpOnly and host-scoped. Unsafe HTTP operations require unambiguous same-origin and browser/session-bound CSRF proof. An XSS or compromised trusted dependency can cross this boundary; CSRF tokens do not prevent same-origin XSS.
16
+
17
+ Session tokens are opaque and stored as hashes. TOTP/recovery/code consumption and administrative invariants use transactions. Role changes and account state changes invalidate relevant sessions. Fresh authentication, ceilings and last-administrator checks are service responsibilities, not UI-only safeguards. Use actor-token administrative methods for delegated users; unrestricted operator methods are not HTTP authorization APIs.
18
+
19
+ Impersonation is opt-in, excludes privileged targets, expires, and denies account security/administrative mutations. The built-in account page shows a warning. Arbitrary guest pages do not automatically receive a universal impersonation banner; do not assume otherwise. Never expose an unrestricted issueSession or operator service method to a client.
20
+
21
+ Metadata is never a permission source. Private fields must stay out of public projections; public/unsafe fields are untrusted. Render translated/user text as text, not markup. Operator theme inputs are constrained; arbitrary project templates are deliberately excluded.
22
+
23
+ ## Operational limitations
24
+
25
+ Rate limits, worker bounds and request limits reduce specific abuse paths; they do not replace perimeter admission controls or deployment capacity testing. Review proxy/client identity configuration. Do not weaken MFA because an email/reset flow is inconvenient. Provider account linking must remain explicit and issuer/subject-scoped rather than inferred from matching email.
26
+
27
+ Emails can fail or be delayed. Preserve an operator recovery procedure for deletion cancellations, email changes and provider outages. Do not log token-bearing callback URLs, passwords, codes, session headers or mail bodies. Development console/file senders intentionally expose development credentials and require explicit opt-in.
28
+
29
+ Backups contain sensitive account records and password hashes. Preserve keys separately, retain the exact reviewed configuration, test isolated restores, and plan session/token revocation when restoring old data. An integrity check proves database consistency, not freshness, provenance or absence of malicious operator modifications. Key rotation is not a substitute for revoking compromised sessions or rotating other credentials.
30
+
31
+ Synthetic tests do not prove real provider delivery, browser/device compatibility, accessibility conformance, production resilience, recovery time or independent security review. Keep those claims separate from local verification results.
32
+
33
+ This repository follows the [core URLCode security policy](https://github.com/jimhoyd-com/urlcode/blob/main/SECURITY.md) for reporting and support baseline.
@@ -0,0 +1,29 @@
1
+ # Bundled data attribution
2
+
3
+ The disposable-email domain data comes from
4
+ https://github.com/disposable-email-domains/disposable-email-domains at commit
5
+ `be0dd1affed11ea9b1ffcd14da81319f9a080609`.
6
+
7
+ Original file: `disposable_email_blocklist.conf` (8,883 entries).
8
+ Original SHA-256: `87bf71873ced728237615c546eff8fa36962dfa261d366e13ef9e0bad3224aad`.
9
+ The data is converted mechanically into a frozen TypeScript array; no upstream code
10
+ is executed. The dataset is a fallible classification snapshot, not identity or
11
+ abuse proof. Updates require a reviewed code change and configuration migration
12
+ when the policy is enabled.
13
+
14
+ Upstream license follows. URLCode auth code remains Apache-2.0 licensed.
15
+
16
+ CC0 1.0 Universal (CC0 1.0)
17
+ Public Domain Dedication
18
+
19
+ No Copyright
20
+
21
+ The person who associated a work with this deed has dedicated
22
+ the work to the public domain by waiving all of his or her rights
23
+ to the work worldwide under copyright law, including all related
24
+ and neighboring rights, to the extent allowed by law.
25
+
26
+ You can copy, modify, distribute and perform the work, even
27
+ for commercial purposes, all without asking permission.
28
+
29
+ Details: https://creativecommons.org/publicdomain/zero/1.0/legalcode
@@ -0,0 +1,62 @@
1
+ # Auth threat model
2
+
3
+ ## Scope and assets
4
+
5
+ This package is a trusted Node host service. Application server code remains
6
+ untrusted WASM under the core runtime's capability model. The auth service protects
7
+ account ownership, verified identifiers, credential material, sessions, factor and
8
+ recovery proofs, operator role grants, audit history and private profile data.
9
+ SQLite files and backups contain sensitive account data: file permissions are a
10
+ boundary, not whole-database encryption. TOTP secrets and protocol state are sealed;
11
+ passwords are hashed, and reusable browser capabilities are stored as hashes.
12
+
13
+ ## Entry points and trust boundaries
14
+
15
+ | Entry point | Untrusted input | Authority boundary |
16
+ | --- | --- | --- |
17
+ | Mounted auth HTTP routes | Query, form/JSON, cookies, origin, provider responses | Bounded parsing, browser binding, CSRF, maintained protocol verification, final SQLite transaction |
18
+ | Public signup | Identifier, credential, profile and consent | Verification-first state machine; unique identifiers and reviewed default role; no metadata grants |
19
+ | OIDC/WebAuthn completion | State, claims, signed assertions | Issuer/subject binding or RP/origin/UV checks; current account and credential versions rechecked at issuance |
20
+ | Self-service security changes | Password/factor proof and session | Fresh non-impersonated authority; current version; atomic proof use and session revocation |
21
+ | Email/manual recovery | Mailbox capabilities or reviewed case evidence | Explicit operator opt-in; cancellation/cooldown or two current administrators; restricted reenrollment |
22
+ | Operator modules and CLI | Configuration, keys, import data, callbacks | Trusted operator installation outside the project; bounded imports; explicit configuration migration |
23
+ | Auth service API called by admin | Actor token, target and reason | Worker rechecks current permissions, delegation ceiling, freshness and target state |
24
+
25
+ A same-origin frontend can exercise browser authority even without reading an
26
+ HttpOnly cookie. Do not host adversarial frontend scripts on this origin. A hostile
27
+ host process, operator module, dependency or OS user able to read the key material
28
+ is outside the WASM boundary. This release does not claim hostile multi-tenant
29
+ readiness or independent assessment.
30
+
31
+ ## Required invariants
32
+
33
+ - Guests cannot select host modules or receive session, provider or recovery secrets.
34
+ - Required signup verification precedes stored credentials. Existing accounts are
35
+ never overwritten by a duplicate signup, linked by email alone, or upgraded from
36
+ untrusted metadata.
37
+ - Proof verification and authority issuance must share account/credential version
38
+ checks; retries, parallel workers and A→B→A configuration changes cannot revive
39
+ consumed or stale authority.
40
+ - A passkey cannot serve as both primary and second factor in the same sign-in.
41
+ Remembered-device authority never grants fresh admin/credential authority.
42
+ - Recovery replacement-factor enrollment belongs to the restoration session.
43
+ Another party possessing an old password cannot win an enrollment race.
44
+ - Administrative restoration is a human evidence procedure. Software enforces
45
+ two-person approval and delivery gates; it does not establish legal identity.
46
+ - Limits fail closed, cleanup is bounded, and expiry is checked during use rather
47
+ than relying on a sweep. Network callbacks cannot authorize incomplete state.
48
+ - Diagnostic output, public responses, browser JavaScript, tests and repository
49
+ artifacts must not contain real passwords, reusable credentials or customer data.
50
+
51
+ ## Review and evidence
52
+
53
+ For an auth change, trace the final transaction, not just the form or preflight.
54
+ Check replay, concurrency, expiry, browser/account binding, privilege changes,
55
+ configuration migration, fallback methods, last usable access and secret handling.
56
+ Run `npm run verify`; for exports/build/CLI changes also exercise the installed
57
+ local tarballs across core/auth/admin. Protocol fixtures must use synthetic keys.
58
+
59
+ Tests cover these mechanisms but are not a penetration test, real-provider setup,
60
+ load/soak result or disaster-recovery exercise. Live Google/Apple/SES verification
61
+ is separately deferred. Operator callbacks and policy data require their own
62
+ review, monitoring, incident handling and periodically tested backups.
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@jimhoyd/urlcode-auth",
3
+ "version": "0.1.0-alpha.1",
4
+ "type": "module",
5
+ "description": "Operator-installed authentication extension for URLCode: accounts, sessions, passkeys, OIDC, TOTP and trusted account pages",
6
+ "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/jimhoyd-com/urlcode-auth.git"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "engines": {
15
+ "node": ">=22.18.0"
16
+ },
17
+ "scripts": {
18
+ "build": "tsc -p tsconfig.build.json",
19
+ "typecheck": "tsc --noEmit",
20
+ "pretest": "node scripts/check-sqlite.mjs",
21
+ "test": "node --conditions=development --test test/*.test.ts",
22
+ "verify": "npm run typecheck && npm run build && npm test"
23
+ },
24
+ "exports": {
25
+ ".": {
26
+ "development": "./src/index.ts",
27
+ "types": "./dist/index.d.ts",
28
+ "default": "./dist/index.js"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "README.md",
34
+ "LICENSE",
35
+ "SECURITY.md",
36
+ "IMPLEMENTATION-STATUS.md",
37
+ "THIRD_PARTY_NOTICES.md",
38
+ "THREAT-MODEL.md"
39
+ ],
40
+ "devDependencies": {
41
+ "@types/node": "26.5.1",
42
+ "typescript": "6.0.3"
43
+ },
44
+ "dependencies": {
45
+ "@aws-sdk/client-sesv2": "3.1135.0",
46
+ "@simplewebauthn/server": "14.0.2",
47
+ "bcryptjs": "3.0.3",
48
+ "jose": "6.2.12",
49
+ "openid-client": "6.8.8",
50
+ "otpauth": "9.5.2"
51
+ },
52
+ "peerDependencies": {
53
+ "@jimhoyd/urlcode": ">=0.4.0-alpha.1 <0.5.0",
54
+ "@jimhoyd/urlcode-ui": ">=0.1.0-alpha.1 <0.2.0"
55
+ },
56
+ "bin": {
57
+ "urlcode-auth": "./dist/cli.js"
58
+ },
59
+ "homepage": "https://github.com/jimhoyd-com/urlcode-auth#readme",
60
+ "bugs": {
61
+ "url": "https://github.com/jimhoyd-com/urlcode-auth/issues"
62
+ }
63
+ }