@nxgt/janus 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +554 -0
- package/dist/auth/config.d.ts +201 -0
- package/dist/auth/config.d.ts.map +1 -0
- package/dist/auth/context.d.ts +87 -0
- package/dist/auth/context.d.ts.map +1 -0
- package/dist/auth/hashers.d.ts +35 -0
- package/dist/auth/hashers.d.ts.map +1 -0
- package/dist/auth/index.d.ts +9 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/janus.d.ts +38 -0
- package/dist/auth/janus.d.ts.map +1 -0
- package/dist/auth/outage.d.ts +61 -0
- package/dist/auth/outage.d.ts.map +1 -0
- package/dist/auth/port/assert-stores.d.ts +16 -0
- package/dist/auth/port/assert-stores.d.ts.map +1 -0
- package/dist/auth/port/memory.d.ts +25 -0
- package/dist/auth/port/memory.d.ts.map +1 -0
- package/dist/auth/port/types.d.ts +393 -0
- package/dist/auth/port/types.d.ts.map +1 -0
- package/dist/auth/secrets.d.ts +17 -0
- package/dist/auth/secrets.d.ts.map +1 -0
- package/dist/auth/sessions.d.ts +31 -0
- package/dist/auth/sessions.d.ts.map +1 -0
- package/dist/auth/standard-schema.d.ts +41 -0
- package/dist/auth/standard-schema.d.ts.map +1 -0
- package/dist/auth/types.d.ts +353 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/users.d.ts +9 -0
- package/dist/auth/users.d.ts.map +1 -0
- package/dist/chunks/index-658b6mr2.js +269 -0
- package/dist/chunks/index-658b6mr2.js.map +11 -0
- package/dist/chunks/index-6dytvy3h.js +93 -0
- package/dist/chunks/index-6dytvy3h.js.map +11 -0
- package/dist/chunks/index-6p56fpbe.js +147 -0
- package/dist/chunks/index-6p56fpbe.js.map +12 -0
- package/dist/chunks/index-fgb3t64y.js +77 -0
- package/dist/chunks/index-fgb3t64y.js.map +10 -0
- package/dist/conformance/assert.d.ts +36 -0
- package/dist/conformance/assert.d.ts.map +1 -0
- package/dist/conformance/cases/outage.d.ts +3 -0
- package/dist/conformance/cases/outage.d.ts.map +1 -0
- package/dist/conformance/cases/sessions.d.ts +3 -0
- package/dist/conformance/cases/sessions.d.ts.map +1 -0
- package/dist/conformance/cases/tokens.d.ts +3 -0
- package/dist/conformance/cases/tokens.d.ts.map +1 -0
- package/dist/conformance/cases/users.d.ts +3 -0
- package/dist/conformance/cases/users.d.ts.map +1 -0
- package/dist/conformance/describe.d.ts +74 -0
- package/dist/conformance/describe.d.ts.map +1 -0
- package/dist/conformance/fixtures.d.ts +11 -0
- package/dist/conformance/fixtures.d.ts.map +1 -0
- package/dist/conformance/index.d.ts +33 -0
- package/dist/conformance/index.d.ts.map +1 -0
- package/dist/conformance/index.js +1113 -0
- package/dist/conformance/index.js.map +18 -0
- package/dist/conformance/reference.d.ts +13 -0
- package/dist/conformance/reference.d.ts.map +1 -0
- package/dist/conformance/relations.d.ts +67 -0
- package/dist/conformance/relations.d.ts.map +1 -0
- package/dist/conformance/types.d.ts +71 -0
- package/dist/conformance/types.d.ts.map +1 -0
- package/dist/errors/janus-error.d.ts +259 -0
- package/dist/errors/janus-error.d.ts.map +1 -0
- package/dist/ids/id.d.ts +55 -0
- package/dist/ids/id.d.ts.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +962 -0
- package/dist/index.js.map +19 -0
- package/dist/pagination/cursor-page.d.ts +53 -0
- package/dist/pagination/cursor-page.d.ts.map +1 -0
- package/dist/permissions/engine.d.ts +46 -0
- package/dist/permissions/engine.d.ts.map +1 -0
- package/dist/permissions/index.d.ts +24 -0
- package/dist/permissions/index.d.ts.map +1 -0
- package/dist/permissions/index.js +685 -0
- package/dist/permissions/index.js.map +15 -0
- package/dist/permissions/input.d.ts +29 -0
- package/dist/permissions/input.d.ts.map +1 -0
- package/dist/permissions/model.d.ts +368 -0
- package/dist/permissions/model.d.ts.map +1 -0
- package/dist/permissions/port/memory.d.ts +17 -0
- package/dist/permissions/port/memory.d.ts.map +1 -0
- package/dist/permissions/port/types.d.ts +84 -0
- package/dist/permissions/port/types.d.ts.map +1 -0
- package/dist/permissions/resolve.d.ts +59 -0
- package/dist/permissions/resolve.d.ts.map +1 -0
- package/dist/permissions/reverse.d.ts +53 -0
- package/dist/permissions/reverse.d.ts.map +1 -0
- package/dist/permissions/walk.d.ts +29 -0
- package/dist/permissions/walk.d.ts.map +1 -0
- package/dist/subjects/notation.d.ts +35 -0
- package/dist/subjects/notation.d.ts.map +1 -0
- package/dist/subjects/subject.d.ts +75 -0
- package/dist/subjects/subject.d.ts.map +1 -0
- package/dist/time/clock.d.ts +31 -0
- package/dist/time/clock.d.ts.map +1 -0
- package/dist/time/duration.d.ts +23 -0
- package/dist/time/duration.d.ts.map +1 -0
- package/docs/README.md +17 -0
- package/docs/guide/adapters.md +296 -0
- package/docs/guide/email-flows.md +146 -0
- package/docs/guide/errors.md +146 -0
- package/docs/guide/passwords.md +136 -0
- package/docs/guide/permissions.md +347 -0
- package/docs/guide/sessions.md +211 -0
- package/docs/guide/users.md +277 -0
- package/docs/guide/vocabulary.md +163 -0
- package/docs/roadmap.md +93 -0
- package/docs/troubleshooting.md +648 -0
- package/package.json +70 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The store port: what an adapter implements, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* **This is the contract strangers are asked to implement**, so every line of
|
|
5
|
+
* it is a promise. It is small on purpose — anything the core can derive from
|
|
6
|
+
* what the port already offers (merging fields, deciding that an e-mail is no
|
|
7
|
+
* longer verified, deciding that a session has lapsed) lives in the core, where
|
|
8
|
+
* it is written once, rather than here, where every adapter would write it
|
|
9
|
+
* again, differently.
|
|
10
|
+
*
|
|
11
|
+
* ## The six rules an implementation keeps
|
|
12
|
+
*
|
|
13
|
+
* All of them are checked by `@nxgt/janus/conformance`.
|
|
14
|
+
*
|
|
15
|
+
* 1. **An absence is `null`. A failure throws.** A method that can
|
|
16
|
+
* legitimately find nothing answers `null`, `false`, `0` or an empty page.
|
|
17
|
+
* Everything else — a refused connection, a timeout, a primary stepping
|
|
18
|
+
* down, a bug in the adapter — **throws**, preferably `StoreFailure` with
|
|
19
|
+
* the driver error as `cause`. Never write `try { … } catch { return null }`
|
|
20
|
+
* in an implementation of this port: that one line turns an outage into
|
|
21
|
+
* "no such account", and every caller above it answers 404 to somebody
|
|
22
|
+
* whose account exists.
|
|
23
|
+
* 2. **`null`, not `undefined`.** `undefined` is what a missing property and a
|
|
24
|
+
* function with no `return` both produce, so a store that forgot to answer
|
|
25
|
+
* would report "not found" by accident. `null` has to be written on purpose.
|
|
26
|
+
* 3. **Uniqueness is yours, and it is a constraint.** A unique index, a
|
|
27
|
+
* constraint, an atomic `SET NX` — never a read followed by a write, which
|
|
28
|
+
* two concurrent sign-ups both pass.
|
|
29
|
+
* 4. **Bytes round-trip.** Logins, hashes and fields come back exactly as they
|
|
30
|
+
* were written: no normalisation, no trimming, no `1` turned into `'1'`. The
|
|
31
|
+
* core normalises a login before a store ever sees it, so uniqueness is
|
|
32
|
+
* uniqueness of bytes and no adapter needs a collation.
|
|
33
|
+
* 5. **Every method is atomic on its own.** Nothing composes into a
|
|
34
|
+
* transaction, and the core never opens one. An adapter may open one
|
|
35
|
+
* *inside* a method — a normalised SQL schema writes several rows per
|
|
36
|
+
* user — but the port exposes none.
|
|
37
|
+
* 6. **Schema management is not on this interface.** An adapter exposes its own
|
|
38
|
+
* `sync()`; the core never calls it.
|
|
39
|
+
*
|
|
40
|
+
* ## Why three stores and not one
|
|
41
|
+
*
|
|
42
|
+
* The seam is **where atomicity is not required**. A user and their password
|
|
43
|
+
* are written together — a sign-up that stores the user and loses the hash is
|
|
44
|
+
* an account nobody can enter — so they are one record. A session is derived
|
|
45
|
+
* state: losing them all signs everybody out, which recovers. A one-time token
|
|
46
|
+
* is ephemeral by construction. So `sessions` and `tokens` may live in Redis
|
|
47
|
+
* while `users` lives in MongoDB, with no distributed transaction anywhere.
|
|
48
|
+
*
|
|
49
|
+
* ## Why the port is not generic
|
|
50
|
+
*
|
|
51
|
+
* A store does not know the application's schemas, and must not have to:
|
|
52
|
+
* fields are a {@link JsonObject} here, and the core casts once, at the
|
|
53
|
+
* boundary, after the schema has validated them. That is the shape `nxgt-data`
|
|
54
|
+
* uses — a generic public form, a degenericised mirror inside — and it keeps
|
|
55
|
+
* every adapter free of type parameters it could only pass through.
|
|
56
|
+
*/
|
|
57
|
+
import type { Id } from '../../ids/id';
|
|
58
|
+
import type { CursorPage } from '../../pagination/cursor-page';
|
|
59
|
+
/**
|
|
60
|
+
* A value a store must be able to round-trip byte for byte.
|
|
61
|
+
*
|
|
62
|
+
* JSON and nothing else. A `Date` inside fields round-trips through MongoDB and
|
|
63
|
+
* not through a JSON column or Redis, so an adapter could pass the conformance
|
|
64
|
+
* suite on one database and corrupt fields on the next. The record's own
|
|
65
|
+
* timestamps are `Date`s, because every adapter stores those in a column it
|
|
66
|
+
* chose for them.
|
|
67
|
+
*/
|
|
68
|
+
export type Json = string | number | boolean | null | readonly Json[] | JsonObject;
|
|
69
|
+
/**
|
|
70
|
+
* An object of {@link Json} values.
|
|
71
|
+
*
|
|
72
|
+
* An `interface` declared by the application is not assignable to this — an
|
|
73
|
+
* index signature is only satisfied by a type alias. The core never asks a
|
|
74
|
+
* caller to write one: it casts at the boundary, after validation.
|
|
75
|
+
*/
|
|
76
|
+
export type JsonObject = {
|
|
77
|
+
readonly [key: string]: Json;
|
|
78
|
+
};
|
|
79
|
+
/** A password, as the store holds it: a self-describing hash, never the plain text. */
|
|
80
|
+
export interface PasswordRecord {
|
|
81
|
+
/** Self-describing — `$argon2id$…`, `$scrypt$…` — so any wired verifier can read it. */
|
|
82
|
+
readonly hash: string;
|
|
83
|
+
readonly updatedAt: Date;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A user, as a store holds it.
|
|
87
|
+
*
|
|
88
|
+
* Everything is `readonly`: the core hands records to application code, and a
|
|
89
|
+
* mutation would not reach the store — it could only mislead whoever wrote it.
|
|
90
|
+
*/
|
|
91
|
+
export interface UserRecord {
|
|
92
|
+
/** A UUIDv7 minted by the core. The store never mints an id. */
|
|
93
|
+
readonly id: Id;
|
|
94
|
+
/**
|
|
95
|
+
* Which of the application's user types — `'patient'`, `'staff'`, or
|
|
96
|
+
* `'user'` when it declares one. Never changes after insertion.
|
|
97
|
+
*/
|
|
98
|
+
readonly type: string;
|
|
99
|
+
/**
|
|
100
|
+
* Which version of the type's schema these fields were last validated
|
|
101
|
+
* against.
|
|
102
|
+
*
|
|
103
|
+
* **Nothing reads it yet**, and it is here from v1 on purpose. Tightening a
|
|
104
|
+
* schema changes what an update accepts, and every stored user was validated
|
|
105
|
+
* against the old one: without this field there is no way to find the users
|
|
106
|
+
* that are now unmodifiable. Adding a field to the port later breaks every
|
|
107
|
+
* adapter; adding it now costs one column.
|
|
108
|
+
*/
|
|
109
|
+
readonly schemaVersion: string;
|
|
110
|
+
/** `false` keeps the record and its password, and refuses every sign-in. */
|
|
111
|
+
readonly active: boolean;
|
|
112
|
+
/** The application's own fields, as the type's schema validated them. */
|
|
113
|
+
readonly fields: JsonObject;
|
|
114
|
+
/**
|
|
115
|
+
* What the user signs in with, **already normalised** by the core. Unique
|
|
116
|
+
* per `type`, and that uniqueness is a constraint the store enforces
|
|
117
|
+
* (rule 3): the same e-mail may hold a patient account and a staff account,
|
|
118
|
+
* and never two of either.
|
|
119
|
+
*/
|
|
120
|
+
readonly logins: readonly string[];
|
|
121
|
+
/** `null` when the user has no password. */
|
|
122
|
+
readonly password: PasswordRecord | null;
|
|
123
|
+
/**
|
|
124
|
+
* When the user proved they hold their current e-mail, or `null`. The core
|
|
125
|
+
* sets it back to `null` in the same write that changes the e-mail.
|
|
126
|
+
*/
|
|
127
|
+
readonly emailVerifiedAt: Date | null;
|
|
128
|
+
/**
|
|
129
|
+
* `0` at insertion, and one more on every accepted write. What
|
|
130
|
+
* {@link UserStore.updateUser}'s `ifVersion` is compared against.
|
|
131
|
+
*/
|
|
132
|
+
readonly version: number;
|
|
133
|
+
readonly createdAt: Date;
|
|
134
|
+
readonly updatedAt: Date;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* What one {@link UserStore.updateUser} changes.
|
|
138
|
+
*
|
|
139
|
+
* **A field the patch does not name is left as it is.** There is no full
|
|
140
|
+
* replacement of a record anywhere on this port: in Kratos, an update that
|
|
141
|
+
* omits `state` deactivates the account, and an edit form that omits a trait
|
|
142
|
+
* deletes it. A conformance case carries that trap's name.
|
|
143
|
+
*
|
|
144
|
+
* A field the patch *does* name is replaced whole — `fields` and `logins`
|
|
145
|
+
* included. A deep merge would make every adapter implement a merge on nested
|
|
146
|
+
* values, differently on every database. The core computes the next value from
|
|
147
|
+
* the record it just read, under `ifVersion`, so no write is lost by it.
|
|
148
|
+
*
|
|
149
|
+
* `password: null` removes the password; a patch that does not name `password`
|
|
150
|
+
* keeps it.
|
|
151
|
+
*
|
|
152
|
+
* `id`, `type`, `version` and `createdAt` are not patchable. `updatedAt` is
|
|
153
|
+
* **required**, and comes from the core's clock rather than the database's, so
|
|
154
|
+
* every timestamp on a record comes from one clock.
|
|
155
|
+
*
|
|
156
|
+
* A key present with the value `undefined` is absent. This package is compiled
|
|
157
|
+
* with `exactOptionalPropertyTypes`, so the core cannot write one; an adapter
|
|
158
|
+
* receiving one from JavaScript still treats it as absent, never as an erasure.
|
|
159
|
+
*/
|
|
160
|
+
export interface UserPatch {
|
|
161
|
+
readonly updatedAt: Date;
|
|
162
|
+
readonly schemaVersion?: string;
|
|
163
|
+
readonly active?: boolean;
|
|
164
|
+
readonly fields?: JsonObject;
|
|
165
|
+
readonly logins?: readonly string[];
|
|
166
|
+
readonly password?: PasswordRecord | null;
|
|
167
|
+
readonly emailVerifiedAt?: Date | null;
|
|
168
|
+
}
|
|
169
|
+
/** Which users a page lists, where it starts, and how much of it to read. */
|
|
170
|
+
export interface UserPageRequest {
|
|
171
|
+
/** Only users of this type. */
|
|
172
|
+
readonly type: string;
|
|
173
|
+
/**
|
|
174
|
+
* The last id of the previous page, or `null` for the first.
|
|
175
|
+
*
|
|
176
|
+
* Already checked by the core to be an id this package could have minted,
|
|
177
|
+
* so the store never parses a cursor. It need not name a stored user: the
|
|
178
|
+
* page is every id strictly greater than it.
|
|
179
|
+
*/
|
|
180
|
+
readonly after: Id | null;
|
|
181
|
+
/** Already bounded by the core, between 1 and `MAX_PAGE_SIZE`. */
|
|
182
|
+
readonly limit: number;
|
|
183
|
+
}
|
|
184
|
+
/** Users and their passwords: one unit of atomicity. */
|
|
185
|
+
export interface UserStore {
|
|
186
|
+
/**
|
|
187
|
+
* Stores a new user, verbatim, and answers what is stored.
|
|
188
|
+
*
|
|
189
|
+
* **Idempotent under retry.** When a user with this `id` already exists, it
|
|
190
|
+
* answers the stored record and writes nothing — a retry after a timeout
|
|
191
|
+
* whose first attempt landed is a success, not a conflict. That includes the
|
|
192
|
+
* logins: a login held by the user with this same `id` is not taken.
|
|
193
|
+
*
|
|
194
|
+
* A login held by **another** user of the same type rejects with
|
|
195
|
+
* `StoreConflict('login', …)`, carrying `login` and `userType`, and writes
|
|
196
|
+
* nothing. That refusal comes from the store's own constraint, never from a
|
|
197
|
+
* read made first.
|
|
198
|
+
*/
|
|
199
|
+
insertUser(record: UserRecord): Promise<UserRecord>;
|
|
200
|
+
/** The user with this id, whatever their type, or `null`. */
|
|
201
|
+
findUser(id: Id): Promise<UserRecord | null>;
|
|
202
|
+
/**
|
|
203
|
+
* The user of this type holding this login, or `null`.
|
|
204
|
+
*
|
|
205
|
+
* `login` is compared byte for byte: the core normalised it with the same
|
|
206
|
+
* rule it used when the login was written.
|
|
207
|
+
*/
|
|
208
|
+
findUserByLogin(type: string, login: string): Promise<UserRecord | null>;
|
|
209
|
+
/**
|
|
210
|
+
* One page of users of one type, in ascending id order — which is creation
|
|
211
|
+
* order.
|
|
212
|
+
*
|
|
213
|
+
* `nextCursor` is the last id of the page when more follow, and `null` on
|
|
214
|
+
* the last page. An empty store answers an empty page, never `null`.
|
|
215
|
+
*/
|
|
216
|
+
listUsers(page: UserPageRequest): Promise<CursorPage<UserRecord>>;
|
|
217
|
+
/**
|
|
218
|
+
* Applies a patch, **only if the stored version is exactly `ifVersion`**,
|
|
219
|
+
* and answers the record as written, with `version` one higher.
|
|
220
|
+
*
|
|
221
|
+
* `ifVersion` is required: every update in the core comes from a record it
|
|
222
|
+
* has just read, so a version is always at hand — and an optional check is
|
|
223
|
+
* the one somebody forgets on the one write where it mattered. An adapter
|
|
224
|
+
* therefore has no unchecked path to write.
|
|
225
|
+
*
|
|
226
|
+
* Rejects, **writing nothing**, with:
|
|
227
|
+
*
|
|
228
|
+
* - `NotFoundError` when no user has this id. The one method on this port
|
|
229
|
+
* that throws for an absence: it always follows a read, so an absence here
|
|
230
|
+
* is a race, not an answer;
|
|
231
|
+
* - `StoreConflict('version', …)` with `expectedVersion` and
|
|
232
|
+
* `actualVersion` when the version moved. A conditional write cannot tell
|
|
233
|
+
* this from an absent id on its own, so the adapter reads again after a
|
|
234
|
+
* write that matched nothing;
|
|
235
|
+
* - `StoreConflict('login', …)` when the patch's logins collide with another
|
|
236
|
+
* user's of the same type.
|
|
237
|
+
*/
|
|
238
|
+
updateUser(id: Id, patch: UserPatch, ifVersion: number): Promise<UserRecord>;
|
|
239
|
+
/**
|
|
240
|
+
* Deletes the user with this id, whatever their type, and frees their
|
|
241
|
+
* logins. `true` when there was one, `false` when there was none.
|
|
242
|
+
*
|
|
243
|
+
* **Idempotent**, so a deletion interrupted half-way can be replayed: the
|
|
244
|
+
* replay answers `false` here and goes on to the sessions and the tokens.
|
|
245
|
+
* This deletes the record only — those two are other stores', and the core
|
|
246
|
+
* deletes them next.
|
|
247
|
+
*/
|
|
248
|
+
deleteUser(id: Id): Promise<boolean>;
|
|
249
|
+
}
|
|
250
|
+
/** A session's id: a UUIDv7 minted by the core, as a user's is. */
|
|
251
|
+
export type SessionId = string;
|
|
252
|
+
/**
|
|
253
|
+
* A session, as a store holds it.
|
|
254
|
+
*
|
|
255
|
+
* **No secret is stored, ever.** The core mints 32 random bytes, hands the store
|
|
256
|
+
* `sha256(secret)` as `tokenHash`, and gives the plain secret to the
|
|
257
|
+
* application once. A dump of the store cannot be replayed.
|
|
258
|
+
*/
|
|
259
|
+
export interface SessionRecord {
|
|
260
|
+
readonly id: SessionId;
|
|
261
|
+
/** `sha256` of the session token, hex. Unique across the store. */
|
|
262
|
+
readonly tokenHash: string;
|
|
263
|
+
readonly userId: Id;
|
|
264
|
+
/** When credentials were last presented — not when the session was last extended. */
|
|
265
|
+
readonly authenticatedAt: Date;
|
|
266
|
+
readonly expiresAt: Date;
|
|
267
|
+
/** `null` while the session stands. */
|
|
268
|
+
readonly revokedAt: Date | null;
|
|
269
|
+
readonly createdAt: Date;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Sessions. Derived state: losing them all signs everybody out, which recovers.
|
|
273
|
+
*
|
|
274
|
+
* **Expiry is the core's decision, not the store's.** A store may still hold a
|
|
275
|
+
* lapsed session or may already have dropped it — a TTL index is an
|
|
276
|
+
* optimisation, and this interface treats it as one. A read answers a present
|
|
277
|
+
* record **verbatim**, lapsed or revoked, and may answer `null` once its expiry
|
|
278
|
+
* has passed. What it must never do is answer a record it has *changed*.
|
|
279
|
+
*/
|
|
280
|
+
export interface SessionStore {
|
|
281
|
+
/**
|
|
282
|
+
* Stores a new session. Idempotent under retry: when a session with this id
|
|
283
|
+
* exists, it writes nothing.
|
|
284
|
+
*/
|
|
285
|
+
insertSession(record: SessionRecord): Promise<void>;
|
|
286
|
+
/** The session whose token hashes to this, verbatim, or `null`. */
|
|
287
|
+
findSessionByTokenHash(tokenHash: string): Promise<SessionRecord | null>;
|
|
288
|
+
/**
|
|
289
|
+
* Moves `expiresAt`, **only while the session is not revoked**, and answers
|
|
290
|
+
* the record as written.
|
|
291
|
+
*
|
|
292
|
+
* `null` when there is no such session or it has been revoked — an extension
|
|
293
|
+
* racing a revocation must never bring the session back. Whether it is
|
|
294
|
+
* *allowed* to be extended yet is the core's decision, made before the call.
|
|
295
|
+
*/
|
|
296
|
+
extendSession(id: SessionId, expiresAt: Date): Promise<SessionRecord | null>;
|
|
297
|
+
/**
|
|
298
|
+
* Revokes one session. `true` when the session exists — revoked by this call
|
|
299
|
+
* or already — and `false` when there is none.
|
|
300
|
+
*
|
|
301
|
+
* A session already revoked keeps its first `revokedAt`.
|
|
302
|
+
*/
|
|
303
|
+
revokeSession(id: SessionId, at: Date): Promise<boolean>;
|
|
304
|
+
/**
|
|
305
|
+
* Revokes every standing session of one user, except `except` when given —
|
|
306
|
+
* "sign out everywhere else". Answers how many this call revoked; `0` is an
|
|
307
|
+
* answer, not a failure.
|
|
308
|
+
*/
|
|
309
|
+
revokeUserSessions(userId: Id, at: Date, except?: SessionId): Promise<number>;
|
|
310
|
+
/**
|
|
311
|
+
* Deletes every session of one user — standing, revoked or lapsed — and
|
|
312
|
+
* answers how many. `0` is an answer, not a failure. What deleting a user
|
|
313
|
+
* calls: a revoked session still names who held it.
|
|
314
|
+
*/
|
|
315
|
+
deleteUserSessions(userId: Id): Promise<number>;
|
|
316
|
+
/**
|
|
317
|
+
* **Optional capability.** Deletes every session whose `expiresAt` is at or
|
|
318
|
+
* before `before`, and answers how many.
|
|
319
|
+
*
|
|
320
|
+
* A store with its own expiry — a TTL index, a key TTL — does not implement
|
|
321
|
+
* it. The core reads its presence rather than assuming it, and
|
|
322
|
+
* `collectExpired()` throws `UNSUPPORTED`, naming this method and the
|
|
323
|
+
* `sessions` slot, when it is absent.
|
|
324
|
+
*/
|
|
325
|
+
deleteExpiredSessions?(before: Date): Promise<number>;
|
|
326
|
+
}
|
|
327
|
+
/** What a one-time token is for. A token redeemed for the other purpose is unknown. */
|
|
328
|
+
export type TokenKind = 'verifyEmail' | 'resetPassword';
|
|
329
|
+
/** A one-time token, as a store holds it: its hash, never its secret. */
|
|
330
|
+
export interface TokenRecord {
|
|
331
|
+
/** `sha256` of the token's secret, hex. Unique across the store. */
|
|
332
|
+
readonly tokenHash: string;
|
|
333
|
+
readonly kind: TokenKind;
|
|
334
|
+
readonly userId: Id;
|
|
335
|
+
/** The e-mail the token was sent to — the one a verification marks verified. */
|
|
336
|
+
readonly address: string;
|
|
337
|
+
readonly expiresAt: Date;
|
|
338
|
+
/** `null` until spent. Once set, never changes. */
|
|
339
|
+
readonly spentAt: Date | null;
|
|
340
|
+
readonly createdAt: Date;
|
|
341
|
+
}
|
|
342
|
+
/** One-time tokens. Ephemeral by construction. */
|
|
343
|
+
export interface TokenStore {
|
|
344
|
+
/**
|
|
345
|
+
* Stores a new token. Idempotent under retry: when a token with this hash
|
|
346
|
+
* exists, it writes nothing.
|
|
347
|
+
*/
|
|
348
|
+
insertToken(record: TokenRecord): Promise<void>;
|
|
349
|
+
/**
|
|
350
|
+
* **The most important method on this port.** Spends the token and answers
|
|
351
|
+
* it **as it was before this call**.
|
|
352
|
+
*
|
|
353
|
+
* - `spentAt: null` in the answer means *this call* spent it. Exactly one
|
|
354
|
+
* call ever sees that.
|
|
355
|
+
* - `spentAt` set means it was already spent, and nothing was written.
|
|
356
|
+
* - `null` means no token of this `kind` has this hash — including one the
|
|
357
|
+
* store has already dropped. A token of the other kind is not touched.
|
|
358
|
+
*
|
|
359
|
+
* **One conditional write, never a read followed by a write.** A reset code
|
|
360
|
+
* two concurrent requests both redeem is an account takeover: twenty
|
|
361
|
+
* concurrent calls must produce exactly one answer with `spentAt: null`, and
|
|
362
|
+
* the conformance suite runs exactly that. In MongoDB this is one
|
|
363
|
+
* `findOneAndUpdate` returning the document *before* the update.
|
|
364
|
+
*
|
|
365
|
+
* A lapsed token is spent all the same. Comparing `expiresAt` is the core's
|
|
366
|
+
* job, after the call, so a lapsed token can never be retried.
|
|
367
|
+
*/
|
|
368
|
+
consumeToken(tokenHash: string, kind: TokenKind, at: Date): Promise<TokenRecord | null>;
|
|
369
|
+
/**
|
|
370
|
+
* Deletes every token of one user, spent or not, and answers how many.
|
|
371
|
+
* What deleting a user calls: a token holds the e-mail it was sent to, which
|
|
372
|
+
* must not outlive the user until its expiry.
|
|
373
|
+
*/
|
|
374
|
+
deleteUserTokens(userId: Id): Promise<number>;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* The three stores `janus()` takes.
|
|
378
|
+
*
|
|
379
|
+
* Each slot may come from a different adapter. That is the point of the seam:
|
|
380
|
+
* `@nxgt/janus-redis` can serve `sessions` and `tokens` while
|
|
381
|
+
* `@nxgt/janus-mongo` serves `users`.
|
|
382
|
+
*/
|
|
383
|
+
export interface JanusStores {
|
|
384
|
+
readonly users: UserStore;
|
|
385
|
+
readonly sessions: SessionStore;
|
|
386
|
+
readonly tokens: TokenStore;
|
|
387
|
+
}
|
|
388
|
+
/** What `assertStores` found beyond the required methods. */
|
|
389
|
+
export interface StoreCapabilities {
|
|
390
|
+
/** Whether `sessions.deleteExpiredSessions` is implemented. */
|
|
391
|
+
readonly collectExpired: boolean;
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/auth/port/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A fresh secret: 32 random bytes, base64url — safe in a header, a cookie and a
|
|
3
|
+
* URL without escaping.
|
|
4
|
+
*
|
|
5
|
+
* Given to the application **once**. No store ever holds it.
|
|
6
|
+
*/
|
|
7
|
+
export declare function mintSecret(): string;
|
|
8
|
+
/**
|
|
9
|
+
* What a store holds instead of the secret: `sha256`, hex.
|
|
10
|
+
*
|
|
11
|
+
* SHA-256 and not argon2, on purpose. The secret carries 256 bits of entropy, so
|
|
12
|
+
* there is nothing to brute-force, and an argon2 per request would put ~50 ms on
|
|
13
|
+
* every authenticated call. What the hash buys is that a dump of the store
|
|
14
|
+
* cannot be replayed.
|
|
15
|
+
*/
|
|
16
|
+
export declare function hashSecret(secret: string): string;
|
|
17
|
+
//# sourceMappingURL=secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/auth/secrets.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ResolvedType } from './config';
|
|
2
|
+
import { type AnyUser, type Context } from './context';
|
|
3
|
+
import type { SessionRecord, UserRecord } from './port/types';
|
|
4
|
+
import type { Authenticated, RequestLike, Session, SharedApi, SignedIn } from './types';
|
|
5
|
+
/** `SharedApi`, degenericised: `janus()` casts it to the typed form once. */
|
|
6
|
+
export type InternalSharedApi = Omit<SharedApi<AnyUser>, 'authenticate'> & {
|
|
7
|
+
authenticate(request: RequestLike, options?: {
|
|
8
|
+
readonly type?: string;
|
|
9
|
+
}): Promise<Authenticated<AnyUser> | null>;
|
|
10
|
+
};
|
|
11
|
+
/** A session as application code sees it: everything but the token's hash. */
|
|
12
|
+
export declare function toSession(record: SessionRecord): Session;
|
|
13
|
+
/**
|
|
14
|
+
* Opens a session for a user who just proved who they are. The token is in
|
|
15
|
+
* the answer and nowhere else: the store holds its hash.
|
|
16
|
+
*/
|
|
17
|
+
export declare function openSession(context: Context, type: ResolvedType, user: UserRecord): Promise<SignedIn<AnyUser>>;
|
|
18
|
+
/** Everything `janus()` answers whatever its user types. */
|
|
19
|
+
export declare function sharedApi(context: Context): InternalSharedApi;
|
|
20
|
+
/**
|
|
21
|
+
* The session token a request presents: `Authorization: Bearer`, then
|
|
22
|
+
* `X-Session-Token`, then the cookie.
|
|
23
|
+
*
|
|
24
|
+
* **The first credential present wins, not the first valid one.** A browser
|
|
25
|
+
* sending a lapsed bearer beside a live cookie is anonymous, and should fix its
|
|
26
|
+
* header rather than be rescued in silence — the rule `resolve` in `nxgt-ory`'s
|
|
27
|
+
* SDK learned. An `Authorization` header of another scheme (`Basic`) is not a
|
|
28
|
+
* session credential, and does not count as one.
|
|
29
|
+
*/
|
|
30
|
+
export declare function presentedToken(request: RequestLike, cookieName: string): string | null;
|
|
31
|
+
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/auth/sessions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAKZ,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE9D,OAAO,KAAK,EACX,aAAa,EAEb,WAAW,EACX,OAAO,EACP,SAAS,EACT,QAAQ,EACR,MAAM,SAAS,CAAC;AAEjB,6EAA6E;AAC7E,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,GAAG;IAC1E,YAAY,CACX,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAClC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;CAC1C,CAAC;AAEF,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAGxD;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAChC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,UAAU,GACd,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAe5B;AAED,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,CA4G7D;AAqBD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CA+Bf"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Standard Schema interface, v1 — copied, as the specification asks.
|
|
3
|
+
*
|
|
4
|
+
* <https://standardschema.dev>. Zod 4, Valibot and ArkType implement it, so a
|
|
5
|
+
* definition accepts whichever the application already uses and this package
|
|
6
|
+
* has **no validation peer at all**. The specification is written to be copied
|
|
7
|
+
* rather than depended on: it is a type, and a type has nothing to install.
|
|
8
|
+
*/
|
|
9
|
+
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
10
|
+
readonly '~standard': StandardSchemaV1.Props<Input, Output>;
|
|
11
|
+
}
|
|
12
|
+
export declare namespace StandardSchemaV1 {
|
|
13
|
+
interface Props<Input = unknown, Output = Input> {
|
|
14
|
+
readonly version: 1;
|
|
15
|
+
readonly vendor: string;
|
|
16
|
+
readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
|
|
17
|
+
readonly types?: Types<Input, Output> | undefined;
|
|
18
|
+
}
|
|
19
|
+
type Result<Output> = SuccessResult<Output> | FailureResult;
|
|
20
|
+
interface SuccessResult<Output> {
|
|
21
|
+
readonly value: Output;
|
|
22
|
+
readonly issues?: undefined;
|
|
23
|
+
}
|
|
24
|
+
interface FailureResult {
|
|
25
|
+
readonly issues: ReadonlyArray<Issue>;
|
|
26
|
+
}
|
|
27
|
+
interface Issue {
|
|
28
|
+
readonly message: string;
|
|
29
|
+
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
|
30
|
+
}
|
|
31
|
+
interface PathSegment {
|
|
32
|
+
readonly key: PropertyKey;
|
|
33
|
+
}
|
|
34
|
+
interface Types<Input = unknown, Output = Input> {
|
|
35
|
+
readonly input: Input;
|
|
36
|
+
readonly output: Output;
|
|
37
|
+
}
|
|
38
|
+
type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input'];
|
|
39
|
+
type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=standard-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standard-schema.d.ts","sourceRoot":"","sources":["../../src/auth/standard-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,GAAG,KAAK;IAChE,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;CAC5D;AAED,MAAM,CAAC,OAAO,WAAW,gBAAgB,CAAC;IACzC,UAAiB,KAAK,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,GAAG,KAAK;QACrD,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,EAAE,CAClB,KAAK,EAAE,OAAO,KACV,MAAM,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9C,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;KAClD;IAED,KAAY,MAAM,CAAC,MAAM,IAAI,aAAa,CAAC,MAAM,CAAC,GAAG,aAAa,CAAC;IAEnE,UAAiB,aAAa,CAAC,MAAM;QACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;KAC5B;IAED,UAAiB,aAAa;QAC7B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;KACtC;IAED,UAAiB,KAAK;QACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,WAAW,GAAG,WAAW,CAAC,GAAG,SAAS,CAAC;KACrE;IAED,UAAiB,WAAW;QAC3B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;KAC1B;IAED,UAAiB,KAAK,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,GAAG,KAAK;QACrD,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACxB;IAED,KAAY,UAAU,CAAC,MAAM,SAAS,gBAAgB,IAAI,WAAW,CACpE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAC5B,CAAC,OAAO,CAAC,CAAC;IAEX,KAAY,WAAW,CAAC,MAAM,SAAS,gBAAgB,IAAI,WAAW,CACrE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAC5B,CAAC,QAAQ,CAAC,CAAC;CACZ"}
|