@nxgt/janus 0.17.2 → 0.19.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/README.md +107 -27
- package/dist/auth/config/index.d.ts +1 -1
- package/dist/auth/config/index.d.ts.map +1 -1
- package/dist/auth/config/janus-config.d.ts +31 -0
- package/dist/auth/config/janus-config.d.ts.map +1 -1
- package/dist/auth/config/resolve-config.d.ts.map +1 -1
- package/dist/auth/config/resolve-throttle.d.ts +30 -0
- package/dist/auth/config/resolve-throttle.d.ts.map +1 -0
- package/dist/auth/config/resolved-config.d.ts +5 -0
- package/dist/auth/config/resolved-config.d.ts.map +1 -1
- package/dist/auth/config/warn-throttle-window.d.ts +14 -0
- package/dist/auth/config/warn-throttle-window.d.ts.map +1 -0
- package/dist/auth/counter-key.d.ts +19 -0
- package/dist/auth/counter-key.d.ts.map +1 -0
- package/dist/auth/email-flows/reset-password.d.ts.map +1 -1
- package/dist/auth/email-flows/verify-email.d.ts.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/janus.d.ts.map +1 -1
- package/dist/auth/magic-link.d.ts.map +1 -1
- package/dist/auth/mail-requests.d.ts +51 -0
- package/dist/auth/mail-requests.d.ts.map +1 -0
- package/dist/auth/port/assert-stores.d.ts.map +1 -1
- package/dist/auth/port/memory/users.d.ts.map +1 -1
- package/dist/auth/port/types/stores.d.ts +5 -0
- package/dist/auth/port/types/stores.d.ts.map +1 -1
- package/dist/auth/port/types/users.d.ts +17 -0
- package/dist/auth/port/types/users.d.ts.map +1 -1
- package/dist/auth/sign-in-attempts.d.ts +3 -6
- package/dist/auth/sign-in-attempts.d.ts.map +1 -1
- package/dist/auth/sign-in-code.d.ts +3 -2
- package/dist/auth/sign-in-code.d.ts.map +1 -1
- package/dist/auth/step-up/request.d.ts.map +1 -1
- package/dist/auth/types/email-flows.d.ts +16 -6
- package/dist/auth/types/email-flows.d.ts.map +1 -1
- package/dist/auth/types/magic-link.d.ts +4 -2
- package/dist/auth/types/magic-link.d.ts.map +1 -1
- package/dist/auth/types/step-up.d.ts +4 -1
- package/dist/auth/types/step-up.d.ts.map +1 -1
- package/dist/auth/types/user-type.d.ts +15 -0
- package/dist/auth/types/user-type.d.ts.map +1 -1
- package/dist/auth/users/find-many.d.ts +29 -0
- package/dist/auth/users/find-many.d.ts.map +1 -0
- package/dist/auth/users/record-flows.d.ts.map +1 -1
- package/dist/chunks/{index-6fxq0kzf.js → index-4khsm7mw.js} +2 -2
- package/dist/chunks/{index-7xe5x3eg.js → index-6bjfv4xv.js} +9 -3
- package/dist/chunks/{index-7xe5x3eg.js.map → index-6bjfv4xv.js.map} +3 -3
- package/dist/chunks/{index-k136102s.js → index-cntabs9d.js} +2 -2
- package/dist/chunks/{index-ztng6018.js → index-kppkcbn5.js} +10 -3
- package/dist/chunks/{index-ztng6018.js.map → index-kppkcbn5.js.map} +4 -4
- package/dist/conformance/cases/outage.d.ts.map +1 -1
- package/dist/conformance/cases/users/batch.d.ts +8 -0
- package/dist/conformance/cases/users/batch.d.ts.map +1 -0
- package/dist/conformance/cases/users/index.d.ts.map +1 -1
- package/dist/conformance/describe.d.ts +2 -1
- package/dist/conformance/describe.d.ts.map +1 -1
- package/dist/conformance/index.d.ts +1 -1
- package/dist/conformance/index.d.ts.map +1 -1
- package/dist/conformance/index.js +106 -46
- package/dist/conformance/index.js.map +7 -6
- package/dist/conformance/types.d.ts +10 -2
- package/dist/conformance/types.d.ts.map +1 -1
- package/dist/errors/janus-error/codes.d.ts +10 -0
- package/dist/errors/janus-error/codes.d.ts.map +1 -1
- package/dist/errors/janus-error/index.d.ts +1 -1
- package/dist/errors/janus-error/index.d.ts.map +1 -1
- package/dist/errors/janus-error/options.d.ts +4 -3
- package/dist/errors/janus-error/options.d.ts.map +1 -1
- package/dist/errors/janus-error/refusals.d.ts +8 -0
- package/dist/errors/janus-error/refusals.d.ts.map +1 -1
- package/dist/errors/janus-error/status.d.ts +1 -1
- package/dist/errors/janus-error/status.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +171 -30
- package/dist/index.js.map +18 -14
- package/dist/permissions/index.js +3 -3
- package/docs/README.md +1 -1
- package/docs/guide/adapters.md +44 -4
- package/docs/guide/email-flows.md +164 -4
- package/docs/guide/errors.md +12 -3
- package/docs/guide/magic-link.md +41 -5
- package/docs/guide/passwords.md +2 -1
- package/docs/guide/sign-in-code.md +46 -7
- package/docs/guide/step-up.md +38 -7
- package/docs/guide/users.md +49 -0
- package/docs/roadmap.md +26 -15
- package/docs/troubleshooting.md +127 -5
- package/package.json +1 -1
- package/dist/auth/config/resolve-sign-in.d.ts +0 -5
- package/dist/auth/config/resolve-sign-in.d.ts.map +0 -1
- /package/dist/chunks/{index-6fxq0kzf.js.map → index-4khsm7mw.js.map} +0 -0
- /package/dist/chunks/{index-k136102s.js.map → index-cntabs9d.js.map} +0 -0
package/README.md
CHANGED
|
@@ -165,10 +165,10 @@ async function signIn(email: string, password: string): Promise<Response> {
|
|
|
165
165
|
```
|
|
166
166
|
|
|
167
167
|
`JanusError` is the base of everything thrown at call time. It extends `Error`,
|
|
168
|
-
so no consumer has to order their `catch` blocks. `code` is a union of twenty
|
|
168
|
+
so no consumer has to order their `catch` blocks. `code` is a union of twenty-one
|
|
169
169
|
string literals, so a `switch` over it is exhaustive and adding a code breaks the
|
|
170
170
|
compilation of callers that exhaust it. `statusOf(code)` answers the status
|
|
171
|
-
below, as `JanusErrorStatus` — a union of the
|
|
171
|
+
below, as `JanusErrorStatus` — a union of the nine literals, which a
|
|
172
172
|
framework's own status type accepts:
|
|
173
173
|
|
|
174
174
|
```ts
|
|
@@ -190,6 +190,7 @@ if (error instanceof JanusError) {
|
|
|
190
190
|
| `CODE_INVALID` | 401 — a one-time code that does not match: a second factor's, or one sent by e-mail; `attemptsLeft` from either `confirm` belongs in the body |
|
|
191
191
|
| `SECOND_FACTOR_NOT_ENROLLED`, `SECOND_FACTOR_ACTIVE` | 409 — the factor is not in the state the call needs |
|
|
192
192
|
| `USER_INACTIVE` | 403 |
|
|
193
|
+
| `MAIL_THROTTLED` | 429 — too many e-mails asked for one address or one user in the window; `retryAfter` belongs in the body and a `Retry-After` header. Tell the visitor to use the last e-mail they received (it still works) or to wait. Safe to show: it reveals nothing about accounts |
|
|
193
194
|
| `STEP_UP_REQUIRED` | 403 — the session proved who it is too long ago for this action: ask for a step-up, then send the request again |
|
|
194
195
|
| `TOKEN_UNKNOWN`, `TOKEN_SPENT`, `TOKEN_EXPIRED`, `TOKEN_STALE` | 400 |
|
|
195
196
|
| `INVALID_CURSOR` | 400 |
|
|
@@ -198,7 +199,7 @@ if (error instanceof JanusError) {
|
|
|
198
199
|
|
|
199
200
|
Each code has its class, all exported: `StoreFailure`, `StoreConflict` (`on:
|
|
200
201
|
'login' | 'version'`), `NotFoundError`, `UserInvalidError`, `CredentialError`,
|
|
201
|
-
`UserInactiveError`, `StepUpRequiredError`, `TokenError` (the `TOKEN_*` codes and `CODE_INVALID`),
|
|
202
|
+
`UserInactiveError`, `StepUpRequiredError`, `MailThrottledError`, `TokenError` (the `TOKEN_*` codes and `CODE_INVALID`),
|
|
202
203
|
`SecondFactorError`, `InvalidCursorError`, `UnsupportedError` and
|
|
203
204
|
`PermissionDepthError`. `StoreFailure` and `StoreConflict` are exported
|
|
204
205
|
**because an adapter throws them**. An adapter defines no error class of its own, so `instanceof` holds
|
|
@@ -347,6 +348,11 @@ await clinic.staff.signIn({ username, password });
|
|
|
347
348
|
const current = await clinic.authenticate(request);
|
|
348
349
|
if (current?.user.type === 'staff') current.user.service; // narrowed by type
|
|
349
350
|
await clinic.authenticate(request, { type: 'staff' }); // a patient's session → null
|
|
351
|
+
|
|
352
|
+
// Several users by id, in one query: a 100-row staff list, one round trip
|
|
353
|
+
const members = await clinic.staff.findMany(rows.map((row) => row.staffId));
|
|
354
|
+
const byId = new Map(members.map((member) => [member.id, member]));
|
|
355
|
+
const shown = rows.map((row) => ({ ...row, active: byId.get(row.staffId)?.active ?? false }));
|
|
350
356
|
```
|
|
351
357
|
|
|
352
358
|
`janus()` assembles **synchronously and with no I/O**: it checks that every
|
|
@@ -371,11 +377,19 @@ else reaches the store and is asynchronous.
|
|
|
371
377
|
`verifyEmail`, no `resetPassword`, no `signInCode` and no `magicLink` — they
|
|
372
378
|
are absent from its type, not failing at run time. Changing the e-mail sets `emailVerified`
|
|
373
379
|
back to `false`.
|
|
374
|
-
- **Per type**: `create`, `find` (or `null`), `get` (or
|
|
375
|
-
`update(user, patch)` — merged over the stored fields,
|
|
376
|
-
`setActive` and `delete`; with a password, `signUp`,
|
|
377
|
-
`setPassword` and `changePassword`. Every write but
|
|
378
|
-
optional `ifVersion`.
|
|
380
|
+
- **Per type**: `create`, `find` (or `null`), `findMany`, `get` (or
|
|
381
|
+
`NOT_FOUND`), `list`, `update(user, patch)` — merged over the stored fields,
|
|
382
|
+
then validated whole — `setActive` and `delete`; with a password, `signUp`,
|
|
383
|
+
`signIn`, `findByLogin`, `setPassword` and `changePassword`. Every write but
|
|
384
|
+
`delete` takes an optional `ifVersion`.
|
|
385
|
+
- **`findMany(ids)`** answers the type's users **in the order the ids were
|
|
386
|
+
given**, each once — a repeated id at its first place. An id `find` answers
|
|
387
|
+
`null` for — malformed, unknown, another type's — is **left out**, so match
|
|
388
|
+
by `user.id`, never by position. `[]` answers `[]` without reaching the
|
|
389
|
+
store; a long list is read 100 ids per query. One query when the users store
|
|
390
|
+
implements the optional `findUsers` (the reference store, `janus-drizzle`,
|
|
391
|
+
`janus-mongo`); otherwise `findUser`, ten at a time
|
|
392
|
+
([users](docs/guide/users.md#several-users-at-once--findmany)).
|
|
379
393
|
- **`delete(user)`** deletes the user together with every session and one-time
|
|
380
394
|
token they had, so nothing of theirs is kept: a token holds the e-mail it was
|
|
381
395
|
sent to. The user goes first, so an outage half-way leaves only sessions and
|
|
@@ -400,6 +414,22 @@ else reaches the store and is asynchronous.
|
|
|
400
414
|
```ts
|
|
401
415
|
janus({ user, password: { login: 'email' }, store, hasher, signIn: { throttle: { attempts: 5, window: '1h' } } });
|
|
402
416
|
```
|
|
417
|
+
- **Requests that send e-mail are throttled**, on by default: past five
|
|
418
|
+
e-mails asked in a 10-minute window, a request refuses with
|
|
419
|
+
`MailThrottledError` (`MAIL_THROTTLED`, 429) and `retryAfter`, and issues,
|
|
420
|
+
spends and rotates nothing: the last link or code sent still works.
|
|
421
|
+
**Each flow counts on its own**: per address for
|
|
422
|
+
`magicLink.request`, `signInCode.request` and `resetPassword.request` — an
|
|
423
|
+
address nobody holds is counted and refused alike — and per user for
|
|
424
|
+
`verifyEmail.send` and for `stepUp.request` when it e-mails a code.
|
|
425
|
+
`mail: { throttle: { attempts, window } }` changes it, `mail: { throttle:
|
|
426
|
+
false }` turns it off; a store that cannot count throws `STORE_FAILED` and
|
|
427
|
+
nothing is issued
|
|
428
|
+
([e-mail flows](docs/guide/email-flows.md#requests-that-send-e-mail-are-throttled)).
|
|
429
|
+
|
|
430
|
+
```ts
|
|
431
|
+
janus({ user, store, mail: { throttle: { attempts: 3, window: '1h' } } });
|
|
432
|
+
```
|
|
403
433
|
- **Sessions** last `'7d'` and slide: `authenticate` renews one once `renewAfter`
|
|
404
434
|
(`'1d'`) has passed, writing at most once per period, and says so with
|
|
405
435
|
`renewed`. The token is handed back once; the store only holds its `sha256`.
|
|
@@ -685,8 +715,10 @@ to type.
|
|
|
685
715
|
password and the second factor dropped, every session signed out** — are
|
|
686
716
|
handled as for a code.
|
|
687
717
|
- **Not ended by a password write, nor limited by the sign-in throttle**:
|
|
688
|
-
the password proves nothing a link does, and
|
|
689
|
-
passwords.
|
|
718
|
+
the password proves nothing a link does, and that throttle counts
|
|
719
|
+
passwords. The mail throttle counts `request` per address, five per
|
|
720
|
+
10 minutes (`MAIL_THROTTLED`); the last link sent still works. Limit it per
|
|
721
|
+
client yourself.
|
|
690
722
|
|
|
691
723
|
[The sign-in link guide](docs/guide/magic-link.md) has the page that
|
|
692
724
|
confirms from a `POST` so mail scanners spend nothing, every error, routes
|
|
@@ -986,7 +1018,7 @@ describeJanusStores({
|
|
|
986
1018
|
});
|
|
987
1019
|
```
|
|
988
1020
|
|
|
989
|
-
There are
|
|
1021
|
+
There are 58 cases. They cover:
|
|
990
1022
|
- round-trip, byte for byte — including every edge character the core lets
|
|
991
1023
|
through (control characters, U+FFFF, a surrogate pair);
|
|
992
1024
|
- uniqueness, as a constraint: of twenty concurrent inserts of one login,
|
|
@@ -1013,9 +1045,16 @@ There are 55 cases. They cover:
|
|
|
1013
1045
|
removed by one that names `null`;
|
|
1014
1046
|
- deletion: a user's logins are freed, and every session and token of theirs
|
|
1015
1047
|
goes, with a replay answering `false` or `0` rather than failing;
|
|
1016
|
-
-
|
|
1048
|
+
- the optional `users.findUsers`: each stored user once, whatever its type, an
|
|
1049
|
+
id nobody holds left out, and `[]` — never `null` — when nobody holds any;
|
|
1050
|
+
- **outages**, one case for each of the fifteen methods whose honest answer can
|
|
1017
1051
|
be "nothing".
|
|
1018
1052
|
|
|
1053
|
+
**An optional method a store lacks skips its cases, with the reason** — a
|
|
1054
|
+
store without `sessions.deleteExpiredSessions` or `users.findUsers` passes the
|
|
1055
|
+
suite. A case's `needs` names what it cannot run without, one `CaseNeed` or a list
|
|
1056
|
+
(`outage.findUsers` needs `['faults', 'findUsers']`).
|
|
1057
|
+
|
|
1019
1058
|
The suite imports no test framework and no assertion library. It runs under
|
|
1020
1059
|
`bun test`, vitest and jest. Its cases are also exported as data
|
|
1021
1060
|
(`allCases`, or by store: `userStoreCases`, `sessionStoreCases`,
|
|
@@ -1067,20 +1106,25 @@ after a sign-out, is `TOKEN_UNKNOWN`. Its challenge is a secret like
|
|
|
1067
1106
|
`signInCode`'s — and check freshness on the server with `assertFresh`, never
|
|
1068
1107
|
from a flag the client keeps.
|
|
1069
1108
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1109
|
+
**`stepUp.request` is throttled per user, only when it e-mails.** An
|
|
1110
|
+
e-mailed code gets five guesses per challenge and a new challenge takes only
|
|
1111
|
+
a new `request`, so a stolen session could keep asking and fill the user's
|
|
1112
|
+
inbox: the mail throttle refuses the sixth in a window with `MAIL_THROTTLED`; the code
|
|
1113
|
+
last sent still works.
|
|
1114
|
+
A step-up confirmed with the app (`via: 'secondFactor'`) sends nothing and is
|
|
1115
|
+
not counted.
|
|
1074
1116
|
|
|
1075
1117
|
**Answer `signInCode.request` the same whether it issued a code or not** —
|
|
1076
1118
|
the same status, body and cookie: set a random challenge when it answered
|
|
1077
1119
|
`null`. The code route then still tells a decoy (`TOKEN_UNKNOWN`) from a
|
|
1078
1120
|
real challenge (`CODE_INVALID`, `attemptsLeft`, or `TOKEN_SPENT` once a later
|
|
1079
1121
|
request spent it): answer its refusals alike
|
|
1080
|
-
where addresses must stay secret.
|
|
1081
|
-
address
|
|
1082
|
-
|
|
1083
|
-
|
|
1122
|
+
where addresses must stay secret. The request is throttled **per
|
|
1123
|
+
address**, counted before anything is looked up, so an unknown address is
|
|
1124
|
+
refused past the limit as a registered one is. At most one code is live per
|
|
1125
|
+
user — a new `request` spends the one before — so the throttle bounds how
|
|
1126
|
+
often anyone who knows an address can fill its inbox or cancel its owner's
|
|
1127
|
+
code; it does not stop it.
|
|
1084
1128
|
|
|
1085
1129
|
**Confirm a sign-in link from a `POST`, never from its `GET`.** Mail
|
|
1086
1130
|
scanners open every link in an e-mail before the user does: a route that
|
|
@@ -1229,8 +1273,9 @@ your policy. A password refused for its length does not spend the token.
|
|
|
1229
1273
|
`resetPassword.request` spends the user's earlier links, and any password
|
|
1230
1274
|
written — by a link, `changePassword` or `setPassword` — spends every link
|
|
1231
1275
|
still live, so an older e-mail's link answers `TOKEN_SPENT`. Tell the visitor
|
|
1232
|
-
to use the latest e-mail
|
|
1233
|
-
|
|
1276
|
+
to use the latest e-mail. Each request cancels the link before it, so the
|
|
1277
|
+
mail throttle bounds `resetPassword.request` per address, five per 10
|
|
1278
|
+
minutes.
|
|
1234
1279
|
|
|
1235
1280
|
**`signIn` throttles each login, not each client.** Ten passwords per login
|
|
1236
1281
|
per 15 minutes, then `CREDENTIALS_INVALID` with `retryAfter` until the window
|
|
@@ -1249,6 +1294,35 @@ counts live in the tokens store: a flushed or evicting Redis forgets them,
|
|
|
1249
1294
|
and a tokens store that cannot answer fails every password sign-in with
|
|
1250
1295
|
`STORE_FAILED`.
|
|
1251
1296
|
|
|
1297
|
+
**The mail throttle counts each flow on its own, and it is not a per-client
|
|
1298
|
+
limit.** Five requests per window for one address (`magicLink.request`,
|
|
1299
|
+
`signInCode.request`, `resetPassword.request`) or one user
|
|
1300
|
+
(`verifyEmail.send`, and `stepUp.request` when it e-mails), then
|
|
1301
|
+
`MailThrottledError`: answer 429 with `Retry-After: retryAfter`. It is safe to
|
|
1302
|
+
show the visitor — it reveals nothing about accounts, unlike `reason`. A refused
|
|
1303
|
+
request is counted too, but the window is fixed and cannot be extended.
|
|
1304
|
+
**A refused request spends, invalidates and rotates nothing**: the last link or
|
|
1305
|
+
code sent, by anyone's request, still confirms until it expires, so somebody
|
|
1306
|
+
who knows an address cannot shut its e-mailed sign-in or reset — at most they
|
|
1307
|
+
fill the inbox, five e-mails per window per flow. Tell the visitor to use the
|
|
1308
|
+
last e-mail they received, or to wait `retryAfter` seconds. The default window
|
|
1309
|
+
(`'10m'`) is no longer than the shortest default token lifetime; if you set a
|
|
1310
|
+
`tokens.*` lifetime shorter than `mail.throttle.window`, the last code can
|
|
1311
|
+
expire first, so keep the window no longer than the shortest you use — `janus()` warns for
|
|
1312
|
+
it, once per call, through `process.emitWarning` with the code
|
|
1313
|
+
`JANUS_THROTTLE_WINDOW` ([troubleshooting](docs/troubleshooting.md#janus-tokensflow-is-5m-shorter-than-mailthrottlewindow-10m-)). `janus`
|
|
1314
|
+
never sees IP addresses, so one client asking for many addresses is not
|
|
1315
|
+
counted: add a per-IP ceiling with `@nxgt/redis` rate limits
|
|
1316
|
+
(`defineRateLimit`, `bindRateLimit`; `enforce()` throws `GuardError`
|
|
1317
|
+
`RATE_LIMITED`, `retryAfter` in milliseconds —
|
|
1318
|
+
[example](docs/guide/email-flows.md#requests-that-send-e-mail-are-throttled)). A test suite that requests more than five of
|
|
1319
|
+
one flow for one address or user over a `fixedClock` is throttled: advance the
|
|
1320
|
+
clock past `retryAfter`, or wire `mail: { throttle: false }`. The counts live
|
|
1321
|
+
in the tokens store, so on PostgreSQL schedule the delete of lapsed tokens, a
|
|
1322
|
+
flushed or evicting Redis forgets them, and a tokens store that cannot count
|
|
1323
|
+
throws `STORE_FAILED` with nothing issued
|
|
1324
|
+
([e-mail flows](docs/guide/email-flows.md#requests-that-send-e-mail-are-throttled)).
|
|
1325
|
+
|
|
1252
1326
|
**A sign-in can move a user's `version`.** Rewriting a stale hash is a write. A
|
|
1253
1327
|
user object read before that sign-in, and then passed as `ifVersion`, gets
|
|
1254
1328
|
`VERSION_CONFLICT`. That is the conflict doing its job: read the user again.
|
|
@@ -1262,6 +1336,12 @@ stored fields and the result is checked against the schema, so a patch can
|
|
|
1262
1336
|
never leave a user that the schema would refuse. Pass `ifVersion` to make the
|
|
1263
1337
|
write conditional on what you read.
|
|
1264
1338
|
|
|
1339
|
+
**`findMany` answers fewer users than ids, never `null` in their place.** An
|
|
1340
|
+
unknown, malformed or other type's id is left out and a repeated one answered
|
|
1341
|
+
once, so `users[i]` is not the user of `ids[i]`: build a `Map` by `user.id`.
|
|
1342
|
+
An outage is `STORE_FAILED`, never a shorter list — a row whose user is
|
|
1343
|
+
missing from the answer is one nobody holds.
|
|
1344
|
+
|
|
1265
1345
|
**Under `bun test`, pass `runner: { describe, it }`.** Measured: Bun gives a
|
|
1266
1346
|
test file `describe` and `it` as bare identifiers, not as properties of
|
|
1267
1347
|
`globalThis`. jest, and vitest with `globals: true`, are found without it.
|
|
@@ -1327,18 +1407,18 @@ that sends one, since it is awaited: queue the event and return.
|
|
|
1327
1407
|
|
|
1328
1408
|
## Type safety, counted
|
|
1329
1409
|
|
|
1330
|
-
**One hundred and
|
|
1410
|
+
**One hundred and fifty-five plausible mistakes, one hundred and fifty-five refused at compile time — and
|
|
1331
1411
|
two gaps, named.**
|
|
1332
1412
|
|
|
1333
1413
|
The lists are typechecked and never run, with one `@ts-expect-error` per
|
|
1334
1414
|
mistake beside the shapes that must keep compiling. One is a single file:
|
|
1335
1415
|
`test/types/refusals.ts` (fifteen, on the shared vocabulary). The other three
|
|
1336
|
-
are folders with one file per behaviour: `test/types/port/` (twenty-
|
|
1416
|
+
are folders with one file per behaviour: `test/types/port/` (twenty-five, on
|
|
1337
1417
|
the identity stores' port, from the point of view of the person implementing
|
|
1338
|
-
it), `test/types/auth/` (sixty, on `janus()`, from the point of view of
|
|
1418
|
+
it), `test/types/auth/` (sixty-seven, on `janus()`, from the point of view of
|
|
1339
1419
|
the application — twelve of them on the second factor, three on sign-in codes,
|
|
1340
|
-
five on user events, four on step-ups, six on the sign-in throttle, four on
|
|
1341
|
-
sign-in links, six on devices) and `test/types/permissions/` (forty-eight, on the
|
|
1420
|
+
five on user events, four on step-ups, six on the sign-in throttle, five on the mail throttle, four on
|
|
1421
|
+
sign-in links, six on devices, two on `findMany`) and `test/types/permissions/` (forty-eight, on the
|
|
1342
1422
|
permission model and the questions asked of it). The rule comes from
|
|
1343
1423
|
`nxgt-data`, and so does the reason to distrust the claim without the files:
|
|
1344
1424
|
when it was last measured on `@nxgt/mongo`, *seven of twelve plausible
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Gathered here from the files beside this one.
|
|
10
10
|
*/
|
|
11
11
|
export type { PasswordHasher } from './hasher';
|
|
12
|
-
export type { CookieConfig, DevicesConfig, JanusConfig, MultiTypeConfig, SecondFactorConfig, SignInConfig, SignInThrottleConfig, SingleTypeConfig, } from './janus-config';
|
|
12
|
+
export type { CookieConfig, DevicesConfig, JanusConfig, MailConfig, MailThrottleConfig, MultiTypeConfig, SecondFactorConfig, SignInConfig, SignInThrottleConfig, SingleTypeConfig, } from './janus-config';
|
|
13
13
|
export { type Normalize, normalizeEmail } from './normalize';
|
|
14
14
|
export type { PasswordConfig } from './password-config';
|
|
15
15
|
export { RESERVED_FIELDS, RESERVED_TYPES, SINGLE_TYPE } from './reserved';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/config/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EACX,YAAY,EACZ,aAAa,EACb,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,KAAK,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,YAAY,EACX,UAAU,EACV,aAAa,EACb,UAAU,EACV,cAAc,GACd,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/config/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EACX,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,KAAK,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,YAAY,EACX,UAAU,EACV,aAAa,EACb,UAAU,EACV,cAAc,GACd,MAAM,aAAa,CAAC"}
|
|
@@ -60,6 +60,15 @@ interface SharedConfig {
|
|
|
60
60
|
* until the next window. Nothing locks: the next window signs in.
|
|
61
61
|
*/
|
|
62
62
|
readonly signIn?: SignInConfig;
|
|
63
|
+
/**
|
|
64
|
+
* How the requests that hand out something to e-mail answer a loop. **On
|
|
65
|
+
* by default**: past five requests of one flow for one address — or one
|
|
66
|
+
* user, once signed in — in a 10-minute window, the request answers
|
|
67
|
+
* `MAIL_THROTTLED` with `retryAfter`, and issues nothing, until the next
|
|
68
|
+
* window. A refused request spends nothing: the last link or code sent
|
|
69
|
+
* still works.
|
|
70
|
+
*/
|
|
71
|
+
readonly mail?: MailConfig;
|
|
63
72
|
/**
|
|
64
73
|
* A TOTP second factor, for every user type with a password. Absent, no
|
|
65
74
|
* `secondFactor` flows exist and `signIn` answers a session directly.
|
|
@@ -98,6 +107,28 @@ export interface SignInThrottleConfig {
|
|
|
98
107
|
/** How long one window lasts. `'15m'` when absent. */
|
|
99
108
|
readonly window?: Duration;
|
|
100
109
|
}
|
|
110
|
+
/** What the requests that hand out something to e-mail do with a loop. */
|
|
111
|
+
export interface MailConfig {
|
|
112
|
+
/**
|
|
113
|
+
* The requests one address — or one user — may make per flow and per
|
|
114
|
+
* window: `magicLink.request`, `signInCode.request` and
|
|
115
|
+
* `resetPassword.request` per address, `verifyEmail.send` and an e-mailed
|
|
116
|
+
* `stepUp.request` per user. `{ attempts: 5, window: '10m' }` when absent;
|
|
117
|
+
* `false` counts nothing — rate-limit those requests yourself then.
|
|
118
|
+
*/
|
|
119
|
+
readonly throttle?: MailThrottleConfig | false;
|
|
120
|
+
}
|
|
121
|
+
/** How many requests of one flow one address or user may make, and per how long. */
|
|
122
|
+
export interface MailThrottleConfig {
|
|
123
|
+
/** Requests per flow, per address or user, and per window. `5` when absent. */
|
|
124
|
+
readonly attempts?: number;
|
|
125
|
+
/**
|
|
126
|
+
* How long one window lasts. `'10m'` when absent: no longer than the
|
|
127
|
+
* shortest default token lifetime, so the last link or code sent outlives
|
|
128
|
+
* the refusal. A window longer than a token's lifetime leaves a gap.
|
|
129
|
+
*/
|
|
130
|
+
readonly window?: Duration;
|
|
131
|
+
}
|
|
101
132
|
/** What a TOTP second factor needs: a name for the app, and the keys that seal. */
|
|
102
133
|
export interface SecondFactorConfig {
|
|
103
134
|
/** Shown in the authenticator app beside the account: your product's name. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"janus-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/janus-config.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"janus-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/janus-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/resolve-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACX,WAAW,EAGX,MAAM,gBAAgB,CAAC;AAQxB,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,mBAAmB,CAAC;AAGtE,qFAAqF;AACrF,wBAAgB,aAAa,CAC5B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,MAAM,GACX,cAAc,
|
|
1
|
+
{"version":3,"file":"resolve-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/resolve-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACX,WAAW,EAGX,MAAM,gBAAgB,CAAC;AAQxB,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,mBAAmB,CAAC;AAGtE,qFAAqF;AACrF,wBAAgB,aAAa,CAC5B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,MAAM,GACX,cAAc,CA0BhB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the two `{ throttle }` options, which take the same shape and
|
|
3
|
+
* differ in their defaults: `signIn.throttle`, ten passwords per login per
|
|
4
|
+
* fifteen minutes, and `mail.throttle`, five requests per flow, per address
|
|
5
|
+
* or user, per ten minutes. Each is on unless `false`.
|
|
6
|
+
*/
|
|
7
|
+
import { type Duration } from '../../time/duration';
|
|
8
|
+
import type { ResolvedConfig } from './resolved-config';
|
|
9
|
+
/** A throttle once resolved, or `null` when it is `false`. */
|
|
10
|
+
type ResolvedThrottle = ResolvedConfig['signInThrottle'];
|
|
11
|
+
/** What either option takes: `{ throttle }`, a limit or `false`. */
|
|
12
|
+
interface ThrottleOption {
|
|
13
|
+
readonly throttle?: {
|
|
14
|
+
readonly attempts?: number;
|
|
15
|
+
readonly window?: Duration;
|
|
16
|
+
} | false;
|
|
17
|
+
}
|
|
18
|
+
/** Resolves `signIn.throttle`: on unless `false`, ten passwords per login per fifteen minutes. */
|
|
19
|
+
export declare function resolveSignInThrottle(config: ThrottleOption | undefined, where: string): ResolvedThrottle;
|
|
20
|
+
/**
|
|
21
|
+
* Resolves `mail.throttle`: on unless `false`, five requests per flow, per
|
|
22
|
+
* address or user, per ten minutes — room for a visitor who asks again, a
|
|
23
|
+
* loop held to thirty e-mails an hour, and a window **no longer than the
|
|
24
|
+
* shortest default lifetime** (a sign-in code's and a step-up's, `'10m'`):
|
|
25
|
+
* the last link or code sent, issued in the window, still works until the
|
|
26
|
+
* refusal ends.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveMailThrottle(config: ThrottleOption | undefined, where: string): ResolvedThrottle;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=resolve-throttle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-throttle.d.ts","sourceRoot":"","sources":["../../../src/auth/config/resolve-throttle.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,QAAQ,EAAiB,MAAM,qBAAqB,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,8DAA8D;AAC9D,KAAK,gBAAgB,GAAG,cAAc,CAAC,gBAAgB,CAAC,CAAC;AAEzD,oEAAoE;AACpE,UAAU,cAAc;IACvB,QAAQ,CAAC,QAAQ,CAAC,EACf;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAA;KAAE,GAC1D,KAAK,CAAC;CACT;AASD,kGAAkG;AAClG,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,KAAK,EAAE,MAAM,GACX,gBAAgB,CAMlB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,KAAK,EAAE,MAAM,GACX,gBAAgB,CAMlB"}
|
|
@@ -33,6 +33,11 @@ export interface ResolvedConfig {
|
|
|
33
33
|
readonly attempts: number;
|
|
34
34
|
readonly windowMs: number;
|
|
35
35
|
} | null;
|
|
36
|
+
/** `null` when `mail.throttle` is `false`. */
|
|
37
|
+
readonly mailThrottle: {
|
|
38
|
+
readonly attempts: number;
|
|
39
|
+
readonly windowMs: number;
|
|
40
|
+
} | null;
|
|
36
41
|
readonly secondFactor: {
|
|
37
42
|
readonly issuer: string;
|
|
38
43
|
readonly sealer: Sealer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolved-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/resolved-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;QAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC3B,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE;QACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,gDAAgD;IAChD,QAAQ,CAAC,cAAc,EAAE;QACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;KAC1B,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,YAAY,EAAE;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;KAChC,GAAG,IAAI,CAAC;IACT,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACrD,QAAQ,CAAC,MAAM,EAAE;QAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QAC7C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;KACzB,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"resolved-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/resolved-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;QAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC3B,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE;QACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,gDAAgD;IAChD,QAAQ,CAAC,cAAc,EAAE;QACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;KAC1B,GAAG,IAAI,CAAC;IACT,8CAA8C;IAC9C,QAAQ,CAAC,YAAY,EAAE;QACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;KAC1B,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,YAAY,EAAE;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;KAChC,GAAG,IAAI,CAAC;IACT,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACrD,QAAQ,CAAC,MAAM,EAAE;QAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QAC7C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;KACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one wiring mistake the mail throttle cannot refuse: a `tokens.*`
|
|
3
|
+
* lifetime shorter than `mail.throttle.window`. A refused request spends
|
|
4
|
+
* nothing, so the last link or code sent keeps working, but only while it has
|
|
5
|
+
* not expired: with a shorter lifetime it can lapse before the refusal ends.
|
|
6
|
+
* That is a choice an application may make on purpose, so it is a warning,
|
|
7
|
+
* emitted once per `janus()` call, never an error.
|
|
8
|
+
*/
|
|
9
|
+
import type { ResolvedConfig } from './resolved-config';
|
|
10
|
+
/** The warning's text, or `null` when no lifetime is shorter than the window. */
|
|
11
|
+
export declare function throttleWindowMessage(resolved: Pick<ResolvedConfig, 'mailThrottle' | 'tokenTtlMs'>, where: string): string | null;
|
|
12
|
+
/** Emits {@link throttleWindowMessage} through `process.emitWarning`, where the runtime has one. */
|
|
13
|
+
export declare function warnThrottleWindow(resolved: Pick<ResolvedConfig, 'mailThrottle' | 'tokenTtlMs'>, where: string): void;
|
|
14
|
+
//# sourceMappingURL=warn-throttle-window.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"warn-throttle-window.d.ts","sourceRoot":"","sources":["../../../src/auth/config/warn-throttle-window.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAwBxD,iFAAiF;AACjF,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,cAAc,GAAG,YAAY,CAAC,EAC7D,KAAK,EAAE,MAAM,GACX,MAAM,GAAG,IAAI,CAgBf;AAED,oGAAoG;AACpG,wBAAgB,kBAAkB,CACjC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,cAAc,GAAG,YAAY,CAAC,EAC7D,KAAK,EAAE,MAAM,GACX,IAAI,CAUN"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The key that names a count of attempts stored under no user — the
|
|
3
|
+
* passwords tried at a login (`sign-in-attempts.ts`), the e-mails asked for
|
|
4
|
+
* an address (`mail-requests.ts`) — and the keyed hashes made with it.
|
|
5
|
+
*
|
|
6
|
+
* Derived from the sealing keys when `secondFactor` is configured; without
|
|
7
|
+
* them it is a fixed one, and the hash only keeps the login or the address
|
|
8
|
+
* out of plain sight — safe because a `secondFactor` token is redeemed only
|
|
9
|
+
* by the second factor's flows, which do not exist without the keys.
|
|
10
|
+
*/
|
|
11
|
+
import type { Context } from './context';
|
|
12
|
+
/**
|
|
13
|
+
* The key `purpose` names its counts with. Renamed, a purpose's every count
|
|
14
|
+
* in progress would start over.
|
|
15
|
+
*/
|
|
16
|
+
export declare function counterKey(context: Context, purpose: string): Buffer;
|
|
17
|
+
/** A keyed hash of `parts`, base64url: a name, never a secret. */
|
|
18
|
+
export declare const keyed: (key: Buffer, parts: readonly string[]) => string;
|
|
19
|
+
//# sourceMappingURL=counter-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"counter-key.d.ts","sourceRoot":"","sources":["../../src/auth/counter-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAMpE;AAED,kEAAkE;AAClE,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,EAAE,OAAO,SAAS,MAAM,EAAE,KAAG,MAClB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reset-password.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/reset-password.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAOZ,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"reset-password.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/reset-password.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAOZ,MAAM,YAAY,CAAC;AAMpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGjD,KAAK,aAAa,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC;AAEhE;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,aAAa,CA8Cf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-email.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/verify-email.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAKZ,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"verify-email.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/verify-email.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAKZ,MAAM,YAAY,CAAC;AAIpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG/C,uFAAuF;AACvF,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,cAAc,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAwCxC"}
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { CookieConfig, DevicesConfig, FieldsJson, JanusConfig, MultiTypeConfig, Normalize, PasswordConfig, PasswordHasher, SecondFactorConfig, SessionConfig, SignInConfig, SignInThrottleConfig, SingleTypeConfig, UserSchema, UserTypeConfig, } from './config';
|
|
1
|
+
export type { CookieConfig, DevicesConfig, FieldsJson, JanusConfig, MailConfig, MailThrottleConfig, MultiTypeConfig, Normalize, PasswordConfig, PasswordHasher, SecondFactorConfig, SessionConfig, SignInConfig, SignInThrottleConfig, SingleTypeConfig, UserSchema, UserTypeConfig, } from './config';
|
|
2
2
|
export type { UserEvent, UserEventListener, UserEventType, } from './events';
|
|
3
3
|
export { bunHasher, scryptHasher } from './hashers';
|
|
4
4
|
export { janus } from './janus';
|
package/dist/auth/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,YAAY,EACZ,aAAa,EACb,UAAU,EACV,WAAW,EACX,eAAe,EACf,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,cAAc,GACd,MAAM,UAAU,CAAC;AAClB,YAAY,EACX,SAAS,EACT,iBAAiB,EACjB,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACX,WAAW,EACX,IAAI,EACJ,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,SAAS,EACT,UAAU,EACV,SAAS,GACT,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,YAAY,EACZ,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,QAAQ,EACR,aAAa,EACb,aAAa,EACb,YAAY,EACZ,SAAS,EACT,WAAW,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,OAAO,EACP,WAAW,EACX,cAAc,EACd,YAAY,GACZ,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,YAAY,EACZ,aAAa,EACb,UAAU,EACV,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,cAAc,GACd,MAAM,UAAU,CAAC;AAClB,YAAY,EACX,SAAS,EACT,iBAAiB,EACjB,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACX,WAAW,EACX,IAAI,EACJ,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,SAAS,EACT,UAAU,EACV,SAAS,GACT,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,YAAY,EACZ,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,QAAQ,EACR,aAAa,EACb,aAAa,EACb,YAAY,EACZ,SAAS,EACT,WAAW,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,OAAO,EACP,WAAW,EACX,cAAc,EACd,YAAY,GACZ,MAAM,SAAS,CAAC"}
|
package/dist/auth/janus.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"janus.d.ts","sourceRoot":"","sources":["../../src/auth/janus.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,WAAW,EAAiB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"janus.d.ts","sourceRoot":"","sources":["../../src/auth/janus.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,WAAW,EAAiB,MAAM,UAAU,CAAC;AAO3D,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAG9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,WAAW,EAChD,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACpB,KAAK,CAAC,CAAC,CAAC,CA2DV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"magic-link.d.ts","sourceRoot":"","sources":["../../src/auth/magic-link.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,WAAW,CAAC;AAG/D,OAAO,EACN,KAAK,MAAM,EAIX,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"magic-link.d.ts","sourceRoot":"","sources":["../../src/auth/magic-link.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,WAAW,CAAC;AAG/D,OAAO,EACN,KAAK,MAAM,EAIX,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EAAe,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,MAAM,GACZ,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAY3D"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The requests that hand out something to e-mail, counted by the store per
|
|
3
|
+
* window — `janus({ mail: { throttle } })`, five per ten minutes by
|
|
4
|
+
* default. Past the last one, the request answers `MAIL_THROTTLED` with
|
|
5
|
+
* `retryAfter`, and **issues nothing**: no token is minted, so there is
|
|
6
|
+
* nothing to send — and **nothing is spent**: counted before anything is
|
|
7
|
+
* issued, a refused request never reaches the step that spends the earlier
|
|
8
|
+
* tokens, so the last link or code sent still works. Nothing locks past a
|
|
9
|
+
* window. With the default lifetimes, the window (`'10m'`) is no longer than
|
|
10
|
+
* any token's, so the last one sent — issued in the window — outlives the
|
|
11
|
+
* refusal: somebody who asks for an address in a loop mails its owner real
|
|
12
|
+
* links, and the last of them signs in.
|
|
13
|
+
*
|
|
14
|
+
* **Each flow counts on its own**, so a loop on one — a sign-in link asked
|
|
15
|
+
* for again and again — never shuts another, and what one address can be
|
|
16
|
+
* sent is bounded per flow:
|
|
17
|
+
*
|
|
18
|
+
* - **per address**, for the requests anybody can make with an e-mail —
|
|
19
|
+
* `magicLink.request`, `signInCode.request`, `resetPassword.request`.
|
|
20
|
+
* Counted **before anything is looked up**, the address normalised, and
|
|
21
|
+
* whatever the user type: an address nobody holds is counted, and refused
|
|
22
|
+
* past the limit, as a registered one is, so being throttled never tells
|
|
23
|
+
* who has an account;
|
|
24
|
+
* - **per user**, for the requests the application makes for a user it
|
|
25
|
+
* knows — `verifyEmail.send`, and `stepUp.request` when it e-mails a code.
|
|
26
|
+
* Nobody without the user's session or the application's call can spend
|
|
27
|
+
* that count, so a loop on their address leaves their own requests alone;
|
|
28
|
+
* and whatever addresses one user moves through, they are sent at most as
|
|
29
|
+
* many.
|
|
30
|
+
*
|
|
31
|
+
* Counted in an attempt window (`one-time/window.ts`), in `secondFactor`
|
|
32
|
+
* tokens stored under no user: the `userId` of each is a keyed hash of the
|
|
33
|
+
* flow and the address or the user (`counter-key.ts`). Nothing spends a
|
|
34
|
+
* link, and one that is spent carries its count on: the count never starts
|
|
35
|
+
* again before its window ends.
|
|
36
|
+
*
|
|
37
|
+
* A store that cannot count throws `STORE_FAILED`: never a refusal, and
|
|
38
|
+
* never a request let through uncounted.
|
|
39
|
+
*/
|
|
40
|
+
import { type ResolvedType } from './config';
|
|
41
|
+
import type { Context } from './context';
|
|
42
|
+
/** The flows that hand out something to e-mail, each counted on its own. */
|
|
43
|
+
export type MailFlow = 'magicLink' | 'signInCode' | 'resetPassword' | 'verifyEmail' | 'stepUp';
|
|
44
|
+
/**
|
|
45
|
+
* Counts one request of `flow` for `email` — before it is looked up — and
|
|
46
|
+
* refuses past the last one, whether anybody holds the address or not.
|
|
47
|
+
*/
|
|
48
|
+
export declare function countMailByAddress(context: Context, type: ResolvedType, flow: MailFlow, email: string, where: string): Promise<void>;
|
|
49
|
+
/** Counts one request of `flow` for the user `userId`, and refuses past the last one. */
|
|
50
|
+
export declare function countMailByUser(context: Context, type: ResolvedType, flow: MailFlow, userId: string, where: string): Promise<void>;
|
|
51
|
+
//# sourceMappingURL=mail-requests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mail-requests.d.ts","sourceRoot":"","sources":["../../src/auth/mail-requests.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAGH,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQzC,4EAA4E;AAC5E,MAAM,MAAM,QAAQ,GACjB,WAAW,GACX,YAAY,GACZ,eAAe,GACf,aAAa,GACb,QAAQ,CAAC;AAKZ;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,yFAAyF;AACzF,wBAAsB,eAAe,CACpC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAEf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert-stores.d.ts","sourceRoot":"","sources":["../../../src/auth/port/assert-stores.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAoC9D;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC3B,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,MAAM,GACX,iBAAiB,
|
|
1
|
+
{"version":3,"file":"assert-stores.d.ts","sourceRoot":"","sources":["../../../src/auth/port/assert-stores.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAoC9D;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC3B,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,MAAM,GACX,iBAAiB,CA+BnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/memory/users.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,SAAS,EAAE,MAAM,UAAU,CAAC;AAYtD,mFAAmF;AACnF,wBAAgB,eAAe,IAAI,SAAS,
|
|
1
|
+
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/memory/users.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,SAAS,EAAE,MAAM,UAAU,CAAC;AAYtD,mFAAmF;AACnF,wBAAgB,eAAe,IAAI,SAAS,CAwE3C"}
|
|
@@ -24,5 +24,10 @@ export interface JanusStores {
|
|
|
24
24
|
export interface StoreCapabilities {
|
|
25
25
|
/** Whether `sessions.deleteExpiredSessions` is implemented. */
|
|
26
26
|
readonly collectExpired: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Whether `users.findUsers` is implemented: `findMany` answers in one query
|
|
29
|
+
* when it is, and with `findUser`, a few ids at a time, when it is not.
|
|
30
|
+
*/
|
|
31
|
+
readonly findUsers: boolean;
|
|
27
32
|
}
|
|
28
33
|
//# sourceMappingURL=stores.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/types/stores.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC5B;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IACjC,+DAA+D;IAC/D,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/types/stores.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC5B;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IACjC,+DAA+D;IAC/D,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B"}
|
|
@@ -129,6 +129,23 @@ export interface UserStore {
|
|
|
129
129
|
insertUser(record: UserRecord): Promise<UserRecord>;
|
|
130
130
|
/** The user with this id, whatever their type, or `null`. */
|
|
131
131
|
findUser(id: Id): Promise<UserRecord | null>;
|
|
132
|
+
/**
|
|
133
|
+
* **Optional capability.** The users holding these ids, whatever their
|
|
134
|
+
* type, in **one query** — `@nxgt/janus-drizzle`'s `inArray`,
|
|
135
|
+
* `@nxgt/janus-mongo`'s `$in`.
|
|
136
|
+
*
|
|
137
|
+
* An id no user holds is **left out**: an absence here is a shorter list,
|
|
138
|
+
* never `null` and never a rejection. Each record appears once, in any
|
|
139
|
+
* order — the core puts them back in the caller's order. A failure throws,
|
|
140
|
+
* as everywhere on this port: answering `[]` for an outage turns it into
|
|
141
|
+
* "nobody", which is the lockout rule 2 exists to prevent.
|
|
142
|
+
*
|
|
143
|
+
* The core calls it with **1 to 100 distinct, well-formed ids**, never an
|
|
144
|
+
* empty list, and reads a longer list in several calls. A store that does
|
|
145
|
+
* not implement it is read with `findUser`, a few ids at a time, so every
|
|
146
|
+
* store answers `findMany`; this method only makes it one round trip.
|
|
147
|
+
*/
|
|
148
|
+
findUsers?(ids: readonly Id[]): Promise<readonly UserRecord[]>;
|
|
132
149
|
/**
|
|
133
150
|
* The user of this type holding this login, or `null`.
|
|
134
151
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/types/users.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/types/users.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IAC1B,gEAAgE;IAChE,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;;;;OASG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC,iDAAiD;IACjD,QAAQ,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,YAAY,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAClD,QAAQ,CAAC,eAAe,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACvC;AAED,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC/B,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,wDAAwD;AACxD,MAAM,WAAW,SAAS;IACzB;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEpD,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAE7C;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;IAE/D;;;;;OAKG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAEzE;;;;;;OAMG;IACH,SAAS,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IAElE;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAE7E;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACrC"}
|