@nxgt/janus 0.23.1 → 0.25.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 +119 -21
- package/dist/auth/email-flows/prove-first.d.ts +23 -1
- package/dist/auth/email-flows/prove-first.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/outage.d.ts +17 -6
- package/dist/auth/outage.d.ts.map +1 -1
- package/dist/auth/port/assert-stores.d.ts.map +1 -1
- package/dist/auth/port/memory/tokens.d.ts.map +1 -1
- package/dist/auth/port/types/stores.d.ts +2 -0
- package/dist/auth/port/types/stores.d.ts.map +1 -1
- package/dist/auth/port/types/tokens.d.ts +12 -0
- package/dist/auth/port/types/tokens.d.ts.map +1 -1
- package/dist/auth/sessions/collect-expired.d.ts +14 -0
- package/dist/auth/sessions/collect-expired.d.ts.map +1 -0
- package/dist/auth/sessions/shared-api.d.ts +1 -1
- package/dist/auth/sessions/shared-api.d.ts.map +1 -1
- package/dist/auth/types/email-flows.d.ts +13 -2
- package/dist/auth/types/email-flows.d.ts.map +1 -1
- package/dist/auth/types/find-or-create.d.ts +59 -0
- package/dist/auth/types/find-or-create.d.ts.map +1 -0
- package/dist/auth/types/index.d.ts +1 -0
- package/dist/auth/types/index.d.ts.map +1 -1
- package/dist/auth/types/janus.d.ts +6 -4
- package/dist/auth/types/janus.d.ts.map +1 -1
- package/dist/auth/users/any-type-api.d.ts +2 -2
- package/dist/auth/users/any-type-api.d.ts.map +1 -1
- package/dist/auth/users/find-or-create.d.ts +12 -0
- package/dist/auth/users/find-or-create.d.ts.map +1 -0
- package/dist/auth/users/insert.d.ts +8 -3
- package/dist/auth/users/insert.d.ts.map +1 -1
- package/dist/auth/users/type-api.d.ts +1 -1
- package/dist/auth/users/type-api.d.ts.map +1 -1
- package/dist/chunks/{index-6bjfv4xv.js → index-q3w3vr69.js} +13 -3
- package/dist/chunks/{index-6bjfv4xv.js.map → index-q3w3vr69.js.map} +3 -3
- package/dist/chunks/{index-cntabs9d.js → index-rpp0bw6e.js} +2 -2
- package/dist/chunks/{index-kppkcbn5.js → index-spwh8b9z.js} +2 -5
- package/dist/chunks/{index-kppkcbn5.js.map → index-spwh8b9z.js.map} +2 -2
- package/dist/chunks/{index-4khsm7mw.js → index-y9b327j8.js} +18 -18
- package/dist/chunks/{index-4khsm7mw.js.map → index-y9b327j8.js.map} +5 -5
- package/dist/conformance/cases/outage.d.ts.map +1 -1
- package/dist/conformance/cases/tokens/deletion.d.ts.map +1 -1
- package/dist/conformance/describe.d.ts +1 -0
- package/dist/conformance/describe.d.ts.map +1 -1
- package/dist/conformance/index.js +39 -5
- package/dist/conformance/index.js.map +5 -5
- package/dist/conformance/types.d.ts +1 -1
- package/dist/conformance/types.d.ts.map +1 -1
- package/dist/index.js +167 -44
- package/dist/index.js.map +11 -9
- package/dist/permissions/engine.d.ts.map +1 -1
- package/dist/permissions/index.d.ts +1 -1
- package/dist/permissions/index.d.ts.map +1 -1
- package/dist/permissions/index.js +52 -19
- package/dist/permissions/index.js.map +6 -5
- package/dist/permissions/input.d.ts +13 -1
- package/dist/permissions/input.d.ts.map +1 -1
- package/dist/permissions/model/grant.d.ts +14 -1
- package/dist/permissions/model/grant.d.ts.map +1 -1
- package/dist/permissions/model/permissions.d.ts +13 -1
- package/dist/permissions/model/permissions.d.ts.map +1 -1
- package/dist/permissions/port/types.d.ts +8 -2
- package/dist/permissions/port/types.d.ts.map +1 -1
- package/dist/permissions/replace.d.ts +25 -0
- package/dist/permissions/replace.d.ts.map +1 -0
- package/docs/README.md +2 -2
- package/docs/guide/adapters.md +40 -3
- package/docs/guide/email-flows.md +35 -5
- package/docs/guide/errors.md +2 -2
- package/docs/guide/passwords.md +7 -10
- package/docs/guide/permissions.md +42 -1
- package/docs/guide/sessions.md +21 -4
- package/docs/guide/users.md +44 -0
- package/docs/guide/vocabulary.md +1 -1
- package/docs/roadmap.md +33 -38
- package/docs/troubleshooting.md +50 -21
- package/package.json +1 -1
- /package/dist/chunks/{index-cntabs9d.js.map → index-rpp0bw6e.js.map} +0 -0
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ supported.
|
|
|
37
37
|
| Import | What it holds |
|
|
38
38
|
| --- | --- |
|
|
39
39
|
| `@nxgt/janus` | **Identities**: `janus()`, the identity stores' port and its in-memory reference (`createMemoryStores`), the hashers, the user events (`UserEvent`, `UserEventListener`, `UserEventType`). And the **shared vocabulary**: errors, subjects and the tuple notation, ids, pagination, time |
|
|
40
|
-
| `@nxgt/janus/permissions` | **Permissions**: `defineModel`, `fromField`, `when`, `permissions()` — `can`, `list`, `grant`, `revoke` — the relation store's port and its in-memory reference (`createMemoryRelations`) |
|
|
40
|
+
| `@nxgt/janus/permissions` | **Permissions**: `defineModel`, `fromField`, `when`, `permissions()` — `can`, `list`, `grant`, `revoke`, `replace` — the relation store's port and its in-memory reference (`createMemoryRelations`) |
|
|
41
41
|
| `@nxgt/janus/conformance` | **For adapters**: the suites a store runs — `describeJanusStores`, `describeRelationStores` — their cases as data, and the reference harnesses |
|
|
42
42
|
|
|
43
43
|
A subpath appears in `exports` only once it exports something you should call:
|
|
@@ -333,6 +333,7 @@ await auth.magicLink.request(email); // { token, email, expiresAt,
|
|
|
333
333
|
await auth.magicLink.confirm(token); // { status: 'signedIn', user, session, token }
|
|
334
334
|
await auth.magicLink.prepare(email); // counted, nobody looked up: { send() } — and signInCode, resetPassword
|
|
335
335
|
await auth.findByEmail(email); // User | null — server-side only, counts nothing; every type with an e-mail
|
|
336
|
+
await auth.findOrCreate(key, () => input); // { user, created } — by login (e-mail without a password); input() only when nobody holds the key
|
|
336
337
|
await auth.signInProven(user.id); // privileged: after a proof YOUR server verified — { status: 'signedIn', … }
|
|
337
338
|
await auth.stepUp.request(user); // { via: 'email', code, challenge, email, expiresAt, user }
|
|
338
339
|
await auth.stepUp.confirm(request, challenge, code); // the request's session, authenticatedAt: now
|
|
@@ -409,7 +410,8 @@ else reaches the store and is asynchronous.
|
|
|
409
410
|
- **Shared**: `authenticate`, `signOut`, `signOutEverywhere(user, { except })`,
|
|
410
411
|
`findUser` and `getUser` across types, `cookie.serialize(token, session)` and
|
|
411
412
|
`cookie.clear()` — `HttpOnly; SameSite=Lax; Secure` unless you say otherwise
|
|
412
|
-
— and `collectExpired
|
|
413
|
+
— and `collectExpired`, which deletes lapsed sessions and lapsed one-time
|
|
414
|
+
tokens.
|
|
413
415
|
- **Password guessing is throttled**, on by default: past ten passwords
|
|
414
416
|
tried at one login in a 15-minute window, `signIn` refuses every one — the
|
|
415
417
|
right password included — with `CREDENTIALS_INVALID`, `reason:
|
|
@@ -920,6 +922,55 @@ password and reset flows on it.
|
|
|
920
922
|
`STORE_FAILED`, never `null`.
|
|
921
923
|
- **Server-side only**: it tells whether an account exists. See *Traps*.
|
|
922
924
|
|
|
925
|
+
### Finding a user, or creating them — `findOrCreate`
|
|
926
|
+
|
|
927
|
+
```ts
|
|
928
|
+
// Accepting an invitation: the account may exist; if not, it is created only when a password was given.
|
|
929
|
+
const invitation = await invitations.redeem(body.invitationToken); // yours: verified first
|
|
930
|
+
if (invitation === null) return new Response(null, { status: 404 });
|
|
931
|
+
|
|
932
|
+
const { user } = await auth.findOrCreate(invitation.email, () => {
|
|
933
|
+
if (body.password === undefined) throw new Error('a password is required'); // refused: nothing is written
|
|
934
|
+
return { email: invitation.email, name: body.name }; // no password here: proveFirst drops it, setPassword sets it once
|
|
935
|
+
});
|
|
936
|
+
await auth.verifyEmail.proveFirst(user); // drops a squatter's password, factor and sessions: first
|
|
937
|
+
if (typeof body.password === 'string') await auth.setPassword(user, body.password); // then the invitee's own
|
|
938
|
+
const result = await auth.signInProven(user); // last
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
The user of this type holding `key`, or the one `input()` describes,
|
|
942
|
+
created — in one call, with the lost race handled. Typed by the exported
|
|
943
|
+
`FindOrCreateApi`; the
|
|
944
|
+
[users guide](docs/guide/users.md#finding-a-user-or-creating-them--findorcreate)
|
|
945
|
+
has the details. On **every type with a
|
|
946
|
+
login or an e-mail**: the key is the login, normalised as `findByLogin`
|
|
947
|
+
normalises it, on a type with a password, and the e-mail, normalised as
|
|
948
|
+
`findByEmail` does, on a type without one. On a type whose login is a
|
|
949
|
+
username, an e-mail key finds an existing user (e-mails are stored as
|
|
950
|
+
logins) but can never create one: the input's username is not the key, so
|
|
951
|
+
it is `USER_INVALID`.
|
|
952
|
+
|
|
953
|
+
- **Found: nothing else happens.** The user is answered
|
|
954
|
+
`{ user, created: false }` — an inactive one included — `input` is not
|
|
955
|
+
called and nothing is written.
|
|
956
|
+
- **`input` is the refusal point.** It runs only when nobody holds the key,
|
|
957
|
+
and what it throws propagates with nothing written: refuse there what the
|
|
958
|
+
caller must refuse before a write, such as a missing password.
|
|
959
|
+
- **The key is a string.** Any other value is `USER_INVALID` before `input()`
|
|
960
|
+
runs.
|
|
961
|
+
- **The input must name the key.** Once validated, its login (its e-mail,
|
|
962
|
+
without a password) must equal the normalised `key`, or the call is
|
|
963
|
+
`USER_INVALID` before the write.
|
|
964
|
+
- **A lost race is not an error.** When another call created the user between
|
|
965
|
+
the read and the write, the store's `LOGIN_TAKEN` is followed by one more
|
|
966
|
+
read of the key; the winner is answered `{ user, created: false }`. The
|
|
967
|
+
`LOGIN_TAKEN` is rethrown only when that read finds nobody — another login
|
|
968
|
+
of the new user is taken.
|
|
969
|
+
- **Created as `create` creates**: the same validation, the same hashing,
|
|
970
|
+
`PASSWORD_TOO_SHORT` below the policy, and `user.created` sent only when
|
|
971
|
+
this call created. Signs nobody in.
|
|
972
|
+
- **Server-side only**, an account-existence oracle as `findByEmail` is.
|
|
973
|
+
|
|
923
974
|
### Proving an e-mail on your own proof — `verifyEmail.proveFirst`
|
|
924
975
|
|
|
925
976
|
```ts
|
|
@@ -949,8 +1000,15 @@ type with an e-mail; `signInProven` alone proves nothing.
|
|
|
949
1000
|
or sign-in-throttle count.
|
|
950
1001
|
- **Refused as `signInProven` refuses**: an inactive user is
|
|
951
1002
|
`USER_INACTIVE`; a malformed id, an unknown one or one of another type is
|
|
952
|
-
`NOT_FOUND`. A
|
|
953
|
-
|
|
1003
|
+
`NOT_FOUND`. A write landing between the read and the proof is
|
|
1004
|
+
retried once on a fresh read (`proved: false` when that write proved the
|
|
1005
|
+
e-mail); `VERSION_CONFLICT`, with nothing proved, when it changed the
|
|
1006
|
+
address or the retry conflicts too; an outage is `STORE_FAILED`.
|
|
1007
|
+
- **The address you hold is checked.** Given a `User` (an object carrying
|
|
1008
|
+
the type's e-mail field), it compares that address, normalised as e-mails
|
|
1009
|
+
are, with the one it reads, on the first read and on the retry read:
|
|
1010
|
+
another address is `VERSION_CONFLICT` and nothing is proved. Given a bare
|
|
1011
|
+
id, or an object without the e-mail field, it proves the current address.
|
|
954
1012
|
- **Privileged, and the order matters**: see *Traps*.
|
|
955
1013
|
|
|
956
1014
|
[E-mail flows](docs/guide/email-flows.md#proving-an-e-mail-on-your-own-proof--provefirst)
|
|
@@ -1215,6 +1273,28 @@ would have to read another object's field.
|
|
|
1215
1273
|
[permissions guide](docs/guide/permissions.md#a-function-of-your-own-around-can)
|
|
1216
1274
|
has the example.
|
|
1217
1275
|
|
|
1276
|
+
**Sync a relation to a wanted list with `replace`.** When the application
|
|
1277
|
+
holds the list a group's members should match, `replace(object, relation,
|
|
1278
|
+
subjects)` adds the missing holders and removes the others in one write, and
|
|
1279
|
+
answers the tuples added and the tuples removed:
|
|
1280
|
+
|
|
1281
|
+
```ts
|
|
1282
|
+
const wanted = await db.staff.membersOf('cardiology'); // your own source of truth
|
|
1283
|
+
const { added, removed } = await access.replace({ type: 'team', id: 'cardiology' }, 'members', wanted); // RelationTuple[] each
|
|
1284
|
+
await access.replace({ type: 'team', id: 'cardiology' }, 'members', []); // empties the group
|
|
1285
|
+
```
|
|
1286
|
+
|
|
1287
|
+
The subjects are typed as `grant`'s is, as an array, and every one is checked
|
|
1288
|
+
before anything is read, so a refusal writes nothing. Duplicates count once,
|
|
1289
|
+
holders already right are not written (no write at all when nothing changes),
|
|
1290
|
+
and a subject set is not its entity. Other relations and objects are untouched.
|
|
1291
|
+
**Two concurrent `replace` calls on one object and relation are not
|
|
1292
|
+
serialised**: each reads, then writes its diff, so the result can hold the
|
|
1293
|
+
union of both adds — serialise them yourself when one must win. A relation
|
|
1294
|
+
store must answer every holder of a relation to `findEntities` and
|
|
1295
|
+
`findSubjectSets`, since `replace` reads them all; the
|
|
1296
|
+
[permissions guide](docs/guide/permissions.md#replace) has the details.
|
|
1297
|
+
|
|
1218
1298
|
**A user type may also be an object type**: declare `staff` under `types`, and
|
|
1219
1299
|
a staff member is an object too — who may edit them is a relation on them. See
|
|
1220
1300
|
[permissions on a user](docs/guide/permissions.md#permissions-on-a-user).
|
|
@@ -1251,7 +1331,7 @@ describeJanusStores({
|
|
|
1251
1331
|
});
|
|
1252
1332
|
```
|
|
1253
1333
|
|
|
1254
|
-
There are
|
|
1334
|
+
There are 61 cases. They cover:
|
|
1255
1335
|
- round-trip, byte for byte — including every edge character the core lets
|
|
1256
1336
|
through (control characters, U+FFFF, a surrogate pair);
|
|
1257
1337
|
- uniqueness, as a constraint: of twenty concurrent inserts of one login,
|
|
@@ -1280,12 +1360,15 @@ There are 59 cases. They cover:
|
|
|
1280
1360
|
goes, with a replay answering `false` or `0` rather than failing;
|
|
1281
1361
|
- the optional `users.findUsers`: each stored user once, whatever its type, an
|
|
1282
1362
|
id nobody holds left out, and `[]` — never `null` — when nobody holds any;
|
|
1283
|
-
-
|
|
1363
|
+
- the optional `tokens.deleteExpiredTokens`: lapsed tokens deleted, spent or
|
|
1364
|
+
not, and a token one millisecond short of lapsing kept, still spent when it
|
|
1365
|
+
was;
|
|
1366
|
+
- **outages**, one case for each of the sixteen methods whose honest answer can
|
|
1284
1367
|
be "nothing".
|
|
1285
1368
|
|
|
1286
1369
|
**An optional method a store lacks skips its cases, with the reason** — a
|
|
1287
|
-
store without `sessions.deleteExpiredSessions
|
|
1288
|
-
suite. A case's `needs` names what it cannot run without, one `CaseNeed` or a list
|
|
1370
|
+
store without `sessions.deleteExpiredSessions`, `tokens.deleteExpiredTokens`
|
|
1371
|
+
or `users.findUsers` passes the suite. A case's `needs` names what it cannot run without, one `CaseNeed` or a list
|
|
1289
1372
|
(`outage.findUsers` needs `['faults', 'findUsers']`).
|
|
1290
1373
|
|
|
1291
1374
|
The suite imports no test framework and no assertion library. It runs under
|
|
@@ -1373,8 +1456,9 @@ acceptance answer 429.
|
|
|
1373
1456
|
every session.** On an e-mail never proved, it does what a first sign-in by
|
|
1374
1457
|
link does, so a password set before it is erased — the invitee's own,
|
|
1375
1458
|
chosen on your acceptance form, included — and a session opened before it
|
|
1376
|
-
is revoked. Accept an invitation in this order: `
|
|
1377
|
-
|
|
1459
|
+
is revoked. Accept an invitation in this order: `findOrCreate` (find or
|
|
1460
|
+
create in one call; `findByEmail` then `create` does the same in two), then
|
|
1461
|
+
`verifyEmail.proveFirst`, then `setPassword` on a type with
|
|
1378
1462
|
a password, then `signInProven`. Called after `setPassword`, the person
|
|
1379
1463
|
signs in once and then finds `signIn` answering `CREDENTIALS_INVALID`
|
|
1380
1464
|
(`reason: 'noPassword'`). **It is privileged, like `signInProven`**: it
|
|
@@ -1384,10 +1468,14 @@ address — on the user that proof names, whose e-mail is the address the
|
|
|
1384
1468
|
proof reached; never with an id a request supplied, or anybody can wipe
|
|
1385
1469
|
anybody's password and second factor and sign them out.
|
|
1386
1470
|
|
|
1387
|
-
**`findByEmail`
|
|
1388
|
-
anonymous client.**
|
|
1389
|
-
address, at once, with no throttle
|
|
1390
|
-
|
|
1471
|
+
**`findByEmail` and `findOrCreate` are account-existence oracles — never
|
|
1472
|
+
expose either to an anonymous client.** `findByEmail` answers whether
|
|
1473
|
+
somebody of the type holds an address, at once, with no throttle;
|
|
1474
|
+
`findOrCreate` is worse, since it also creates accounts: an address nobody
|
|
1475
|
+
holds becomes one. Call `findOrCreate` on the server only, after your own
|
|
1476
|
+
proof, with the key read from that proof's record. A route that calls
|
|
1477
|
+
`findByEmail` on an address a visitor typed tells anybody who has an account.
|
|
1478
|
+
Call `findByEmail` on the server, after
|
|
1391
1479
|
a proof of your own — an invitation token your server issued and just
|
|
1392
1480
|
verified — with the address read from that proof's record, never from the
|
|
1393
1481
|
request. A visitor who types an address goes through `magicLink.request` or
|
|
@@ -1577,8 +1665,8 @@ test that tries more than ten wrong passwords at one login over a
|
|
|
1577
1665
|
`fixedClock` is throttled too: advance the clock past `retryAfter`, or wire
|
|
1578
1666
|
`signIn: { throttle: false }`. **Somebody who knows a login can keep its
|
|
1579
1667
|
password sign-in shut**, ten tries a window; a sign-in code or link, when you wire them, still opens it — the throttle counts passwords, never those.
|
|
1580
|
-
On
|
|
1581
|
-
row per window. One password tried against
|
|
1668
|
+
On a store with no TTL (PostgreSQL through `@nxgt/janus-drizzle`), schedule
|
|
1669
|
+
`collectExpired()`: every login tried adds a row per window, and it deletes them. One password tried against
|
|
1582
1670
|
many logins is not counted: rate-limit `signIn` per client address yourself
|
|
1583
1671
|
([passwords](docs/guide/passwords.md#what-you-still-limit-yourself)). The
|
|
1584
1672
|
counts live in the tokens store: a flushed or evicting Redis forgets them,
|
|
@@ -1609,11 +1697,21 @@ counted: add a per-IP ceiling with `@nxgt/redis` rate limits
|
|
|
1609
1697
|
[example](docs/guide/email-flows.md#requests-that-send-e-mail-are-throttled)). A test suite that requests more than five of
|
|
1610
1698
|
one flow for one address or user over a `fixedClock` is throttled: advance the
|
|
1611
1699
|
clock past `retryAfter`, or wire `mail: { throttle: false }`. The counts live
|
|
1612
|
-
in the tokens store, so on
|
|
1700
|
+
in the tokens store, so on a store with no TTL (PostgreSQL through `@nxgt/janus-drizzle`) schedule
|
|
1701
|
+
`collectExpired()`, which deletes them; a
|
|
1613
1702
|
flushed or evicting Redis forgets them, and a tokens store that cannot count
|
|
1614
1703
|
throws `STORE_FAILED` with nothing issued
|
|
1615
1704
|
([e-mail flows](docs/guide/email-flows.md#requests-that-send-e-mail-are-throttled)).
|
|
1616
1705
|
|
|
1706
|
+
**`collectExpired()` also collects lapsed one-time tokens, and a custom
|
|
1707
|
+
tokens store has to implement `deleteExpiredTokens` for it.** Without the
|
|
1708
|
+
method, only the sessions are collected — or `UNSUPPORTED`, when the sessions
|
|
1709
|
+
store has none either — and the throttles' counts pile up on a database with
|
|
1710
|
+
no TTL. Once a lapsed token is collected, its link answers `TOKEN_UNKNOWN`
|
|
1711
|
+
instead of `TOKEN_EXPIRED` or `TOKEN_SPENT`: a page that tells the two apart
|
|
1712
|
+
should treat the third as "this link is no longer valid"
|
|
1713
|
+
([sessions](docs/guide/sessions.md#collectexpired)).
|
|
1714
|
+
|
|
1617
1715
|
**A sign-in can move a user's `version`.** Rewriting a stale hash is a write. A
|
|
1618
1716
|
user object read before that sign-in, and then passed as `ifVersion`, gets
|
|
1619
1717
|
`VERSION_CONFLICT`. That is the conflict doing its job: read the user again.
|
|
@@ -1698,18 +1796,18 @@ that sends one, since it is awaited: queue the event and return.
|
|
|
1698
1796
|
|
|
1699
1797
|
## Type safety, counted
|
|
1700
1798
|
|
|
1701
|
-
**One hundred and
|
|
1799
|
+
**One hundred and eighty-one plausible mistakes, one hundred and eighty-one refused at compile time — and
|
|
1702
1800
|
two gaps, named.**
|
|
1703
1801
|
|
|
1704
1802
|
The lists are typechecked and never run, with one `@ts-expect-error` per
|
|
1705
1803
|
mistake beside the shapes that must keep compiling. One is a single file:
|
|
1706
1804
|
`test/types/refusals.ts` (fifteen, on the shared vocabulary). The other three
|
|
1707
|
-
are folders with one file per behaviour: `test/types/port/` (twenty-
|
|
1805
|
+
are folders with one file per behaviour: `test/types/port/` (twenty-six, on
|
|
1708
1806
|
the identity stores' port, from the point of view of the person implementing
|
|
1709
|
-
it), `test/types/auth/` (eighty-
|
|
1807
|
+
it), `test/types/auth/` (eighty-eight, on `janus()`, from the point of view of
|
|
1710
1808
|
the application — twelve of them on the second factor, three on sign-in codes,
|
|
1711
1809
|
five on user events, four on step-ups, six on the sign-in throttle, five on the mail throttle, four on
|
|
1712
|
-
sign-in links, six on devices, two on `findMany`, five on requests in two steps, three on signing in on the application's proof, three on finding a user by e-mail, three on proving an e-mail on the application's proof, three on changing an e-mail) and `test/types/permissions/` (
|
|
1810
|
+
sign-in links, six on devices, two on `findMany`, five on requests in two steps, three on signing in on the application's proof, three on finding a user by e-mail, three on proving an e-mail on the application's proof, three on changing an e-mail, four on `findOrCreate`) and `test/types/permissions/` (fifty-two, on the
|
|
1713
1811
|
permission model and the questions asked of it). The rule comes from
|
|
1714
1812
|
`nxgt-data`, and so does the reason to distrust the claim without the files:
|
|
1715
1813
|
when it was last measured on `@nxgt/mongo`, *seven of twelve plausible
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* sign-in by link or code gives an e-mail never proved (`../first-proof`).
|
|
5
5
|
*/
|
|
6
6
|
import type { At } from '../at';
|
|
7
|
-
import type
|
|
7
|
+
import { type ResolvedType } from '../config';
|
|
8
8
|
import { type AnyUser, type Context } from '../context';
|
|
9
9
|
import type { VerifyEmailApi } from '../types';
|
|
10
10
|
/**
|
|
@@ -15,6 +15,28 @@ import type { VerifyEmailApi } from '../types';
|
|
|
15
15
|
* on the record read, `emailVerifiedAt === null`. An e-mail already proved
|
|
16
16
|
* is answered as it is, with nothing written.
|
|
17
17
|
*
|
|
18
|
+
* **The address the caller holds is checked.** When the `UserRef` is an
|
|
19
|
+
* object carrying the type's e-mail field — a `User` — that address,
|
|
20
|
+
* normalised as e-mails are, must be the one read, on the first read and on
|
|
21
|
+
* the retry read; otherwise it is a `VERSION_CONFLICT` and nothing is proved.
|
|
22
|
+
* With a bare id, or an object without the e-mail field, the current address
|
|
23
|
+
* is proved.
|
|
24
|
+
*
|
|
25
|
+
* **A write landing between the read and the proof is retried once.**
|
|
26
|
+
* `VERSION_CONFLICT` from `proveFirstEmail` runs the whole flow again on a
|
|
27
|
+
* fresh read: nobody, or an inactive user, is refused as before; an e-mail
|
|
28
|
+
* that is no longer the one first read — compared as e-mails are — is a
|
|
29
|
+
* `VERSION_CONFLICT` naming both versions, since the address changed since the
|
|
30
|
+
* first read (or is not the one the passed `User` carries) and the proof the
|
|
31
|
+
* caller holds is for the address it read; an e-mail proved meanwhile is
|
|
32
|
+
* answered `proved: false`; otherwise the proof runs under the new version.
|
|
33
|
+
* A second `VERSION_CONFLICT` propagates.
|
|
34
|
+
*
|
|
35
|
+
* Running `proveFirstEmail` twice is harmless: a failed attempt wrote
|
|
36
|
+
* nothing and sent no event (events follow the write); all it left is the
|
|
37
|
+
* sessions revoked and the tokens spent before the write, which the second
|
|
38
|
+
* attempt does again to the same end.
|
|
39
|
+
*
|
|
18
40
|
* No throttle is counted and no count restarted, no e-mail sent and no
|
|
19
41
|
* token issued; the only tokens spent are the ones `proveFirstEmail`
|
|
20
42
|
* spends, what the password opened.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prove-first.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/prove-first.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"prove-first.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/prove-first.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAKZ,MAAM,YAAY,CAAC;AAIpB,OAAO,KAAK,EAAW,cAAc,EAAE,MAAM,UAAU,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,cAAc,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,CA2BtD"}
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -8,5 +8,5 @@ export type { JanusStores, Json, JsonObject, PasswordRecord, SecondFactorRecord,
|
|
|
8
8
|
export type { SealingKey } from './sealing';
|
|
9
9
|
export { assertFresh } from './sessions/fresh';
|
|
10
10
|
export type { StandardSchemaV1 } from './standard-schema';
|
|
11
|
-
export type { Authenticated, ChangeEmailApi, Checked, EmailOf, FindByEmailApi, FirstProof, HeaderRecord, IssuedCode, IssuedToken, Janus, LoginOf, MagicLinkApi, PasswordApi, PreparedCode, PreparedRequest, ProvenSignInApi, RecoveredSignIn, RecoveryCodesIssued, RequestLike, RequiredStringKeys, ResetPasswordApi, SecondFactorApi, SecondFactorEnrolment, SecondFactorRequired, Session, SharedApi, SignedIn, SignInCodeApi, SignInOptions, SignInResult, StepUpApi, StepUpByApp, StepUpByEmail, TypeApi, TypesOf, User, UserBase, UserOf, UserRef, UserTypeApi, VerifyEmailApi, WriteOptions, } from './types';
|
|
11
|
+
export type { Authenticated, ChangeEmailApi, Checked, EmailOf, FindByEmailApi, FindOrCreateApi, FirstProof, HeaderRecord, IssuedCode, IssuedToken, Janus, LoginOf, MagicLinkApi, PasswordApi, PreparedCode, PreparedRequest, ProvenSignInApi, RecoveredSignIn, RecoveryCodesIssued, RequestLike, RequiredStringKeys, ResetPasswordApi, SecondFactorApi, SecondFactorEnrolment, SecondFactorRequired, Session, SharedApi, SignedIn, SignInCodeApi, SignInOptions, SignInResult, StepUpApi, StepUpByApp, StepUpByEmail, TypeApi, TypesOf, User, UserBase, UserOf, UserRef, UserTypeApi, VerifyEmailApi, WriteOptions, } from './types';
|
|
12
12
|
//# sourceMappingURL=index.d.ts.map
|
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,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,cAAc,EACd,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,eAAe,EACf,eAAe,EACf,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,cAAc,EACd,OAAO,EACP,OAAO,EACP,cAAc,EACd,eAAe,EACf,UAAU,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,eAAe,EACf,eAAe,EACf,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/outage.d.ts
CHANGED
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
* {@link guardStores} puts the three identity stores behind the shared guard
|
|
8
8
|
* (`src/stores/guard.ts`), the one place a store's answer is caught.
|
|
9
9
|
* {@link required} turns an absence the caller cannot accept into its refusal,
|
|
10
|
-
*
|
|
11
|
-
* everything else.
|
|
10
|
+
* {@link unlessVersionConflict} absorbs one named conflict and rethrows
|
|
11
|
+
* everything else, and {@link unlessLoginTaken} hands back the other one.
|
|
12
12
|
*
|
|
13
13
|
* `outage.scan.spec.ts` reads every other file of `src/auth/`, `src/permissions/`
|
|
14
14
|
* and `src/stores/` and fails if a `catch` appears in one, because the failure
|
|
15
15
|
* this design exists to prevent is a single careless `catch { return null }`.
|
|
16
16
|
*/
|
|
17
|
-
import { type JanusError } from '../errors/janus-error';
|
|
17
|
+
import { type JanusError, StoreConflict } from '../errors/janus-error';
|
|
18
18
|
import type { JanusStores } from './port/types';
|
|
19
19
|
/**
|
|
20
20
|
* The stores, with every method guarded.
|
|
@@ -29,8 +29,8 @@ import type { JanusStores } from './port/types';
|
|
|
29
29
|
* Rule 2 of the port, enforced at run time for the JavaScript adapter the
|
|
30
30
|
* compiler never saw.
|
|
31
31
|
*
|
|
32
|
-
* The optional `deleteExpiredSessions`
|
|
33
|
-
* when absent, so capability detection still reads the truth.
|
|
32
|
+
* The optional `deleteExpiredSessions` and `deleteExpiredTokens` are guarded
|
|
33
|
+
* when present and left absent when absent, so capability detection still reads the truth.
|
|
34
34
|
*/
|
|
35
35
|
export declare function guardStores(stores: JanusStores): JanusStores;
|
|
36
36
|
/**
|
|
@@ -49,7 +49,18 @@ export declare function required<T>(value: T | null, absent: () => JanusError):
|
|
|
49
49
|
* taken login still throws, `NOT_FOUND` still throws: an outage never reads as
|
|
50
50
|
* "somebody else wrote first". It exists for writes the caller did not ask for
|
|
51
51
|
* — rewriting a password hash on sign-in — where losing to a concurrent update
|
|
52
|
-
* means only that the next sign-in tries again.
|
|
52
|
+
* means only that the next sign-in tries again — and for `verifyEmail.proveFirst`,
|
|
53
|
+
* which reads again and retries once.
|
|
53
54
|
*/
|
|
54
55
|
export declare function unlessVersionConflict<T>(write: Promise<T>): Promise<T | null>;
|
|
56
|
+
/**
|
|
57
|
+
* A write that may lose a race on a login, and says so: its answer, or the
|
|
58
|
+
* `StoreConflict('login')` the store refused it with, **as a value**, for the
|
|
59
|
+
* caller to read the login again and then rethrow or answer the winner.
|
|
60
|
+
*
|
|
61
|
+
* **Only `StoreConflict('login')` is handed back.** A failure still throws, a
|
|
62
|
+
* version conflict still throws: an outage never reads as "somebody else
|
|
63
|
+
* created the user first".
|
|
64
|
+
*/
|
|
65
|
+
export declare function unlessLoginTaken<T>(write: Promise<T>): Promise<T | StoreConflict>;
|
|
55
66
|
//# sourceMappingURL=outage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outage.d.ts","sourceRoot":"","sources":["../../src/auth/outage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"outage.d.ts","sourceRoot":"","sources":["../../src/auth/outage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAM5D;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,UAAU,GAAG,CAAC,CAGxE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAC5C,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GACf,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAOnB;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACvC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GACf,OAAO,CAAC,CAAC,GAAG,aAAa,CAAC,CAO5B"}
|
|
@@ -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,CAqCnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/memory/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,UAAU,CAAC;AAGxD,gDAAgD;AAChD,wBAAgB,gBAAgB,IAAI,UAAU,
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/memory/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,UAAU,CAAC;AAGxD,gDAAgD;AAChD,wBAAgB,gBAAgB,IAAI,UAAU,CA+E7C"}
|
|
@@ -24,6 +24,8 @@ export interface JanusStores {
|
|
|
24
24
|
export interface StoreCapabilities {
|
|
25
25
|
/** Whether `sessions.deleteExpiredSessions` is implemented. */
|
|
26
26
|
readonly collectExpired: boolean;
|
|
27
|
+
/** Whether `tokens.deleteExpiredTokens` is implemented. */
|
|
28
|
+
readonly collectExpiredTokens: boolean;
|
|
27
29
|
/**
|
|
28
30
|
* Whether `users.findUsers` is implemented: `findMany` answers in one query
|
|
29
31
|
* when it is, and with `findUser`, a few ids at a time, when it is not.
|
|
@@ -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;IACjC;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B"}
|
|
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,2DAA2D;IAC3D,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B"}
|
|
@@ -134,5 +134,17 @@ export interface TokenStore {
|
|
|
134
134
|
* must not outlive the user until its expiry.
|
|
135
135
|
*/
|
|
136
136
|
deleteUserTokens(userId: Id): Promise<number>;
|
|
137
|
+
/**
|
|
138
|
+
* **Optional capability.** Deletes every token — spent or not, of any kind
|
|
139
|
+
* and any user — whose `expiresAt` is at or before `before`, and answers how
|
|
140
|
+
* many. Never one whose `expiresAt` is later, spent or not: a spent token is
|
|
141
|
+
* kept until it lapses, or a count would start over.
|
|
142
|
+
*
|
|
143
|
+
* What collects the sign-in and mail throttles' counts, which are ordinary
|
|
144
|
+
* tokens under a keyed-hash user id that `deleteUserTokens` never reaches.
|
|
145
|
+
* A store with its own expiry — a TTL index, a key TTL — does not implement
|
|
146
|
+
* it; `collectExpired()` reads its presence.
|
|
147
|
+
*/
|
|
148
|
+
deleteExpiredTokens?(before: Date): Promise<number>;
|
|
137
149
|
}
|
|
138
150
|
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/types/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,SAAS,GAClB,aAAa,GACb,eAAe,GACf,cAAc,GACd,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,aAAa,CAAC;AAEjB,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CACzB;AAED,kDAAkD;AAClD,MAAM,WAAW,UAAU;IAC1B;;;OAGG;IACH,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD;;;;;;;;;;;;;;;;;;OAkBG;IACH,YAAY,CACX,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,EACf,EAAE,EAAE,IAAI,GACN,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE9E;;;;;;;;;;;;;;;;;OAiBG;IACH,eAAe,CACd,MAAM,EAAE,EAAE,EACV,IAAI,EAAE,SAAS,EACf,EAAE,EAAE,IAAI,EACR,MAAM,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB;;;;OAIG;IACH,gBAAgB,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/types/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,SAAS,GAClB,aAAa,GACb,eAAe,GACf,cAAc,GACd,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,aAAa,CAAC;AAEjB,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CACzB;AAED,kDAAkD;AAClD,MAAM,WAAW,UAAU;IAC1B;;;OAGG;IACH,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD;;;;;;;;;;;;;;;;;;OAkBG;IACH,YAAY,CACX,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,EACf,EAAE,EAAE,IAAI,GACN,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE9E;;;;;;;;;;;;;;;;;OAiBG;IACH,eAAe,CACd,MAAM,EAAE,EAAE,EACV,IAAI,EAAE,SAAS,EACf,EAAE,EAAE,IAAI,EACR,MAAM,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB;;;;OAIG;IACH,gBAAgB,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE9C;;;;;;;;;;OAUG;IACH,mBAAmB,CAAC,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACpD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `collectExpired()`: the lapsed sessions and the lapsed one-time tokens, from
|
|
3
|
+
* whichever of the two stores implements its collector.
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '../context';
|
|
6
|
+
/**
|
|
7
|
+
* Deletes what has lapsed at `clock.now()` and answers how many in all.
|
|
8
|
+
*
|
|
9
|
+
* Sessions first, then tokens, one after the other: both deletions are
|
|
10
|
+
* idempotent, so a token failure after the sessions went leaves a call that can
|
|
11
|
+
* simply be repeated. `UNSUPPORTED` only when neither store has a collector.
|
|
12
|
+
*/
|
|
13
|
+
export declare function collectExpired(context: Context): Promise<number>;
|
|
14
|
+
//# sourceMappingURL=collect-expired.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect-expired.d.ts","sourceRoot":"","sources":["../../../src/auth/sessions/collect-expired.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBtE"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* What `janus()` answers whatever its user types: authenticating a request,
|
|
3
3
|
* signing out, finding a user by id, the cookie, and collecting expired
|
|
4
|
-
* sessions.
|
|
4
|
+
* sessions and one-time tokens.
|
|
5
5
|
*/
|
|
6
6
|
import { type AnyUser, type Context } from '../context';
|
|
7
7
|
import type { Authenticated, RequestLike, SharedApi } from '../types';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-api.d.ts","sourceRoot":"","sources":["../../../src/auth/sessions/shared-api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"shared-api.d.ts","sourceRoot":"","sources":["../../../src/auth/sessions/shared-api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAKZ,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAMtE,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,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,CA8C7D"}
|
|
@@ -119,8 +119,19 @@ export interface VerifyEmailApi<U> {
|
|
|
119
119
|
* Counts nothing in the mail or the sign-in throttle, sends no e-mail
|
|
120
120
|
* and issues no token. An inactive user is `USER_INACTIVE`; a
|
|
121
121
|
* malformed id, an unknown one or one of another type is `NOT_FOUND`;
|
|
122
|
-
* a user written
|
|
123
|
-
*
|
|
122
|
+
* a user written between the read and the proof is read again and the
|
|
123
|
+
* proof retried **once**: `proved: false` when that write proved the
|
|
124
|
+
* e-mail, `VERSION_CONFLICT` with nothing proved when it changed the
|
|
125
|
+
* address (the proof you hold is for the address you read) or when
|
|
126
|
+
* the retry conflicts too, `USER_INACTIVE` when it deactivated the
|
|
127
|
+
* user. Running the proof twice is harmless.
|
|
128
|
+
*
|
|
129
|
+
* **The address you hold is checked.** Given a `User` — an object
|
|
130
|
+
* carrying the type's e-mail field — it compares that address,
|
|
131
|
+
* normalised as e-mails are, with the one it reads, on the first read
|
|
132
|
+
* and on the retry read: a different one is `VERSION_CONFLICT` and
|
|
133
|
+
* nothing is proved. Given a bare id, or an object without the e-mail
|
|
134
|
+
* field, it proves the current address.
|
|
124
135
|
*/
|
|
125
136
|
proveFirst(user: UserRef): Promise<FirstProof<U>>;
|
|
126
137
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-flows.d.ts","sourceRoot":"","sources":["../../../src/auth/types/email-flows.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAgB,MAAM,WAAW,CAAC;AACvE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,0EAA0E;AAC1E,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CACzB;AAED,oFAAoF;AACpF,MAAM,WAAW,UAAU,CAAC,CAAC;IAC5B,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uCAAuC;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;CACjB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,UAAU,EAAE;QACpB;;;;;;;;;;;;WAYG;QACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACtD;;;;;;WAMG;QACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QACjD;;;;;;;;;;;;WAYG;QACH,OAAO,CACN,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,CAAC;KACnB,CAAC;CACF;AAED,uDAAuD;AACvD,MAAM,WAAW,cAAc,CAAC,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE;QACrB;;;;;WAKG;QACH,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QAC1C;;;WAGG;QACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACnC
|
|
1
|
+
{"version":3,"file":"email-flows.d.ts","sourceRoot":"","sources":["../../../src/auth/types/email-flows.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAgB,MAAM,WAAW,CAAC;AACvE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,0EAA0E;AAC1E,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CACzB;AAED,oFAAoF;AACpF,MAAM,WAAW,UAAU,CAAC,CAAC;IAC5B,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uCAAuC;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;CACjB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,UAAU,EAAE;QACpB;;;;;;;;;;;;WAYG;QACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACtD;;;;;;WAMG;QACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QACjD;;;;;;;;;;;;WAYG;QACH,OAAO,CACN,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,CAAC;KACnB,CAAC;CACF;AAED,uDAAuD;AACvD,MAAM,WAAW,cAAc,CAAC,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE;QACrB;;;;;WAKG;QACH,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QAC1C;;;WAGG;QACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAyCG;QACH,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;KAClD,CAAC;CACF;AAED,gGAAgG;AAChG,MAAM,WAAW,UAAU,CAAC,CAAC;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CACzB;AAED,sEAAsE;AACtE,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE;QACvB;;;;;;;;WAQG;QACH,OAAO,CACN,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,CAAC,WAAW,GAAG;YAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;SAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QACxD;;;;;WAKG;QACH,OAAO,CACN,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,CAAC,WAAW,GAAG;YAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;SAAE,CAAC,CAAC,CAAC;QAChE;;;;;;WAMG;QACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACrD,CAAC;CACF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a user type with a login or an e-mail answers besides: finding its
|
|
3
|
+
* user by that key, or creating it when nobody holds it.
|
|
4
|
+
*
|
|
5
|
+
* Part of what `janus()` hands back; `./index` gathers it.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Finding a user by the key an invitation names, or creating them — the
|
|
9
|
+
* accept step of an invitation, without the race between the read and the
|
|
10
|
+
* write.
|
|
11
|
+
*/
|
|
12
|
+
export interface FindOrCreateApi<U, In> {
|
|
13
|
+
/**
|
|
14
|
+
* The user of this type holding `key`, or the user `input()` describes,
|
|
15
|
+
* created. Answers `{ user, created }`.
|
|
16
|
+
*
|
|
17
|
+
* The key is looked up as the type's own lookup would: the login, normalised
|
|
18
|
+
* as `findByLogin` normalises it, on a type that signs in with a password;
|
|
19
|
+
* the e-mail, normalised as `findByEmail` normalises it, on a type that does
|
|
20
|
+
* not. A user found, an inactive one included, is answered
|
|
21
|
+
* `{ user, created: false }`: `input` is not called and nothing is written.
|
|
22
|
+
*
|
|
23
|
+
* When nobody holds the key, `input` is called (it may be async) and the
|
|
24
|
+
* user is created exactly as `create` creates one. **What `input` throws
|
|
25
|
+
* propagates and nothing is written** — that is how the caller refuses before
|
|
26
|
+
* any write, such as "a password is required". The input must name the same
|
|
27
|
+
* key: once validated, its login (its e-mail, on a type with no password)
|
|
28
|
+
* must equal the normalised `key`, or the call is refused with
|
|
29
|
+
* `USER_INVALID` before the write.
|
|
30
|
+
*
|
|
31
|
+
* A call that loses a race to another that creates the same user gets the
|
|
32
|
+
* `LOGIN_TAKEN` of the store, reads the key once more and answers the
|
|
33
|
+
* winner `{ user, created: false }`; the `LOGIN_TAKEN` is rethrown only
|
|
34
|
+
* when that read finds nobody — another login of the new user is taken.
|
|
35
|
+
*
|
|
36
|
+
* On a type whose login is a username, an e-mail key finds an existing user
|
|
37
|
+
* (e-mails are stored as logins) but can never create one: the input's
|
|
38
|
+
* username is not the key, so it is `USER_INVALID`.
|
|
39
|
+
*
|
|
40
|
+
* **An account-existence oracle, as `findByEmail` is: never expose it to an
|
|
41
|
+
* anonymous client.** A key that is no storable string is nobody's, and goes
|
|
42
|
+
* through `input` and the creation, which refuses it.
|
|
43
|
+
*
|
|
44
|
+
* Rejects with `USER_INVALID`, `PASSWORD_TOO_SHORT`, `LOGIN_TAKEN`, or
|
|
45
|
+
* `STORE_FAILED`, and with whatever `input` throws. Sends `user.created`
|
|
46
|
+
* only when it created.
|
|
47
|
+
*/
|
|
48
|
+
findOrCreate(key: string, input: () => (In & {
|
|
49
|
+
readonly password?: string;
|
|
50
|
+
readonly active?: boolean;
|
|
51
|
+
}) | Promise<In & {
|
|
52
|
+
readonly password?: string;
|
|
53
|
+
readonly active?: boolean;
|
|
54
|
+
}>): Promise<{
|
|
55
|
+
readonly user: U;
|
|
56
|
+
readonly created: boolean;
|
|
57
|
+
}>;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=find-or-create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-or-create.d.ts","sourceRoot":"","sources":["../../../src/auth/types/find-or-create.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,EAAE,EAAE;IACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,YAAY,CACX,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MACJ,CAAC,EAAE,GAAG;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,GAChE,OAAO,CAAC,EAAE,GAAG;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,GACxE,OAAO,CAAC;QAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC5D"}
|
|
@@ -11,6 +11,7 @@ export type { ChangeEmailApi } from './change-email';
|
|
|
11
11
|
export type { Checked } from './checks';
|
|
12
12
|
export type { FirstProof, IssuedCode, IssuedToken, ResetPasswordApi, SignInCodeApi, VerifyEmailApi, } from './email-flows';
|
|
13
13
|
export type { FindByEmailApi } from './find-by-email';
|
|
14
|
+
export type { FindOrCreateApi } from './find-or-create';
|
|
14
15
|
export type { EmailOf, LoginOf, RequiredStringKeys, TypesOf, UserOf, } from './inference';
|
|
15
16
|
export type { Janus, SharedApi, TypeApi } from './janus';
|
|
16
17
|
export type { MagicLinkApi } from './magic-link';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/types/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,cAAc,GACd,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,YAAY,EACX,OAAO,EACP,OAAO,EACP,kBAAkB,EAClB,OAAO,EACP,MAAM,GACN,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EACX,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,qBAAqB,GACrB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACX,aAAa,EACb,YAAY,EACZ,WAAW,EACX,OAAO,GACP,MAAM,WAAW,CAAC;AACnB,YAAY,EACX,oBAAoB,EACpB,QAAQ,EACR,aAAa,EACb,YAAY,GACZ,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACvE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACpE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/types/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,cAAc,GACd,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EACX,OAAO,EACP,OAAO,EACP,kBAAkB,EAClB,OAAO,EACP,MAAM,GACN,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EACX,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,qBAAqB,GACrB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACX,aAAa,EACb,YAAY,EACZ,WAAW,EACX,OAAO,GACP,MAAM,WAAW,CAAC;AACnB,YAAY,EACX,oBAAoB,EACpB,QAAQ,EACR,aAAa,EACb,YAAY,GACZ,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACvE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACpE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -8,6 +8,7 @@ import type { JanusConfig, MultiTypeConfig, SingleTypeConfig } from '../config';
|
|
|
8
8
|
import type { ChangeEmailApi } from './change-email';
|
|
9
9
|
import type { ResetPasswordApi, SignInCodeApi, VerifyEmailApi } from './email-flows';
|
|
10
10
|
import type { FindByEmailApi } from './find-by-email';
|
|
11
|
+
import type { FindOrCreateApi } from './find-or-create';
|
|
11
12
|
import type { EmailOf, FieldsInput, LoginOf, SingleAsType, UserOf, UserOfType } from './inference';
|
|
12
13
|
import type { MagicLinkApi } from './magic-link';
|
|
13
14
|
import type { PasswordApi } from './password';
|
|
@@ -29,7 +30,7 @@ type SignInAnswer<Name extends string, Def, TwoFactor extends boolean> = TwoFact
|
|
|
29
30
|
* The whole surface of one user type, with only the flows its configuration
|
|
30
31
|
* allows. `TwoFactor` is whether `janus()` was given a `secondFactor`.
|
|
31
32
|
*/
|
|
32
|
-
export type TypeApi<Name extends string, Def, TwoFactor extends boolean = false> = UserTypeApi<UserOfType<Name, Def>, FieldsInput<Def>> & ProvenSignInApi<UserOfType<Name, Def>, SignInAnswer<Name, Def, TwoFactor>> & ([LoginOf<Def>] extends [never] ? unknown : TwoFactor extends true ? PasswordApi<UserOfType<Name, Def>, FieldsInput<Def>, LoginOf<Def>, SignInResult<UserOfType<Name, Def>>> & SecondFactorApi<UserOfType<Name, Def>> : PasswordApi<UserOfType<Name, Def>, FieldsInput<Def>, LoginOf<Def>>) & ([EmailOf<Def>] extends [never] ? unknown : FindByEmailApi<UserOfType<Name, Def>> & VerifyEmailApi<UserOfType<Name, Def>> & ChangeEmailApi<UserOfType<Name, Def>> & SignInCodeApi<UserOfType<Name, Def>, SignInAnswer<Name, Def, TwoFactor>> & MagicLinkApi<UserOfType<Name, Def>, SignInAnswer<Name, Def, TwoFactor>> & StepUpApi<UserOfType<Name, Def>, TwoFactor extends true ? [LoginOf<Def>] extends [never] ? StepUpByEmail<UserOfType<Name, Def>> : StepUpByEmail<UserOfType<Name, Def>> | StepUpByApp<UserOfType<Name, Def>> : StepUpByEmail<UserOfType<Name, Def>>>) & ([LoginOf<Def>] extends [never] ? unknown : [EmailOf<Def>] extends [never] ? unknown : ResetPasswordApi<UserOfType<Name, Def>>);
|
|
33
|
+
export type TypeApi<Name extends string, Def, TwoFactor extends boolean = false> = UserTypeApi<UserOfType<Name, Def>, FieldsInput<Def>> & ProvenSignInApi<UserOfType<Name, Def>, SignInAnswer<Name, Def, TwoFactor>> & ([LoginOf<Def>] extends [never] ? unknown : TwoFactor extends true ? PasswordApi<UserOfType<Name, Def>, FieldsInput<Def>, LoginOf<Def>, SignInResult<UserOfType<Name, Def>>> & SecondFactorApi<UserOfType<Name, Def>> : PasswordApi<UserOfType<Name, Def>, FieldsInput<Def>, LoginOf<Def>>) & ([LoginOf<Def>] extends [never] ? [EmailOf<Def>] extends [never] ? unknown : FindOrCreateApi<UserOfType<Name, Def>, FieldsInput<Def>> : FindOrCreateApi<UserOfType<Name, Def>, FieldsInput<Def>>) & ([EmailOf<Def>] extends [never] ? unknown : FindByEmailApi<UserOfType<Name, Def>> & VerifyEmailApi<UserOfType<Name, Def>> & ChangeEmailApi<UserOfType<Name, Def>> & SignInCodeApi<UserOfType<Name, Def>, SignInAnswer<Name, Def, TwoFactor>> & MagicLinkApi<UserOfType<Name, Def>, SignInAnswer<Name, Def, TwoFactor>> & StepUpApi<UserOfType<Name, Def>, TwoFactor extends true ? [LoginOf<Def>] extends [never] ? StepUpByEmail<UserOfType<Name, Def>> : StepUpByEmail<UserOfType<Name, Def>> | StepUpByApp<UserOfType<Name, Def>> : StepUpByEmail<UserOfType<Name, Def>>>) & ([LoginOf<Def>] extends [never] ? unknown : [EmailOf<Def>] extends [never] ? unknown : ResetPasswordApi<UserOfType<Name, Def>>);
|
|
33
34
|
/** What every configuration answers, whatever its user types. */
|
|
34
35
|
export interface SharedApi<U extends {
|
|
35
36
|
readonly type: string;
|
|
@@ -67,9 +68,10 @@ export interface SharedApi<U extends {
|
|
|
67
68
|
clear(): string;
|
|
68
69
|
};
|
|
69
70
|
/**
|
|
70
|
-
* Deletes lapsed sessions
|
|
71
|
-
*
|
|
72
|
-
*
|
|
71
|
+
* Deletes lapsed sessions and lapsed one-time tokens — the throttles' counts
|
|
72
|
+
* included — from the stores that implement `deleteExpiredSessions` /
|
|
73
|
+
* `deleteExpiredTokens`, and answers how many in all. `UNSUPPORTED` when
|
|
74
|
+
* neither does — a store with its own TTL does not need to.
|
|
73
75
|
*/
|
|
74
76
|
collectExpired(): Promise<number>;
|
|
75
77
|
/** The user type names, in the order they were declared. */
|