@nxgt/janus 0.7.0 → 0.8.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 +56 -4
- package/dist/auth/config.d.ts +8 -0
- package/dist/auth/config.d.ts.map +1 -1
- package/dist/auth/context.d.ts +4 -1
- package/dist/auth/context.d.ts.map +1 -1
- package/dist/auth/email-flows.d.ts.map +1 -1
- package/dist/auth/events.d.ts +52 -0
- package/dist/auth/events.d.ts.map +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/janus.d.ts.map +1 -1
- package/dist/auth/sign-in-code.d.ts.map +1 -1
- package/dist/auth/users.d.ts.map +1 -1
- package/dist/index.js +67 -12
- package/dist/index.js.map +10 -9
- package/docs/README.md +1 -0
- package/docs/guide/email-flows.md +5 -1
- package/docs/guide/events.md +199 -0
- package/docs/guide/sign-in-code.md +2 -1
- package/docs/guide/users.md +5 -1
- package/docs/guide/vocabulary.md +2 -0
- package/docs/roadmap.md +13 -9
- package/docs/troubleshooting.md +52 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ supported.
|
|
|
36
36
|
|
|
37
37
|
| Import | What it holds |
|
|
38
38
|
| --- | --- |
|
|
39
|
-
| `@nxgt/janus` | **Identities**: `janus()`, the identity stores' port and its in-memory reference (`createMemoryStores`), the hashers. And the **shared vocabulary**: errors, subjects and the tuple notation, ids, pagination, time |
|
|
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
40
|
| `@nxgt/janus/permissions` | **Permissions**: `defineModel`, `fromField`, `when`, `permissions()` — `can`, `list`, `grant`, `revoke` — 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
|
|
|
@@ -560,6 +560,51 @@ A six-digit code sent to the user's e-mail signs them in, with no password.
|
|
|
560
560
|
[The sign-in code guide](docs/guide/sign-in-code.md) has the request that
|
|
561
561
|
tells nobody who exists, the challenge in a cookie, every error, and a test.
|
|
562
562
|
|
|
563
|
+
### User events — `events`
|
|
564
|
+
|
|
565
|
+
```ts
|
|
566
|
+
import { z } from 'zod';
|
|
567
|
+
import { createMemoryStores, janus, scryptHasher, type UserEvent } from '@nxgt/janus';
|
|
568
|
+
|
|
569
|
+
const auth = janus({
|
|
570
|
+
user: z.object({ email: z.email() }),
|
|
571
|
+
password: { login: 'email' },
|
|
572
|
+
store: createMemoryStores(),
|
|
573
|
+
hasher: scryptHasher(),
|
|
574
|
+
async events(event: UserEvent) {
|
|
575
|
+
// { id, type: 'user.created', occurredAt, userId, userType }
|
|
576
|
+
await queue.add(event.type, event, { jobId: event.id }); // deliver it once
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
await auth.signUp({ email, password }); // the listener has the event before this answers
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
`janus({ events })` hears what happened to a user, once it is written:
|
|
584
|
+
|
|
585
|
+
| `type` | Sent by |
|
|
586
|
+
| --- | --- |
|
|
587
|
+
| `user.created` | `create`, `signUp` |
|
|
588
|
+
| `user.emailVerified` | `verifyEmail.confirm`; `resetPassword.confirm` and `signInCode.confirm`, whose link or code proves the e-mail too — never for an e-mail already verified |
|
|
589
|
+
| `user.passwordReset` | `resetPassword.confirm` |
|
|
590
|
+
| `user.deleted` | `delete`, once — a replay that deletes nobody sends nothing |
|
|
591
|
+
|
|
592
|
+
- **The user is named by id, and nothing else**: no login, no e-mail, no
|
|
593
|
+
field, no password, no token. Whoever receives the event reads the rest
|
|
594
|
+
from where it is kept, if they may.
|
|
595
|
+
- **Each event has an `id` of its own**, a UUIDv7: the key to deliver it once.
|
|
596
|
+
- **The listener runs after the write, and is awaited** before the
|
|
597
|
+
flow answers, so a durable queue has the event by then. `occurredAt` is the
|
|
598
|
+
write's own time. A refused flow sends nothing.
|
|
599
|
+
- **Typed**: `events` is a `UserEventListener`; `UserEventType` is the closed
|
|
600
|
+
union of the four types, so a `switch` on `event.type` is exhaustive.
|
|
601
|
+
- **A listener that throws fails no flow** — the write happened. It is a
|
|
602
|
+
`JANUS_EVENT_FAILED` warning naming the event's type, its id and the user's
|
|
603
|
+
id, never the failure's message.
|
|
604
|
+
|
|
605
|
+
[The user events guide](docs/guide/events.md) has the listener, the four
|
|
606
|
+
types, what a failure costs, and a test.
|
|
607
|
+
|
|
563
608
|
### Permissions — `@nxgt/janus/permissions`
|
|
564
609
|
|
|
565
610
|
```ts
|
|
@@ -859,6 +904,13 @@ a query of your own.
|
|
|
859
904
|
`list()` with its own error, as it threw. Never answer `[]` for a database that
|
|
860
905
|
could not answer: that is a denial made of an outage.
|
|
861
906
|
|
|
907
|
+
**A user event is sent at most once, from the process that wrote.** It is
|
|
908
|
+
handed to the listener after the write, in the same process: a crash between
|
|
909
|
+
the two loses it, and nothing sends it later. Build what must not miss one
|
|
910
|
+
— a billing sync, a search index — to also read the users themselves now and
|
|
911
|
+
then, and treat events as the fast path. A slow listener slows every flow
|
|
912
|
+
that sends one, since it is awaited: queue the event and return.
|
|
913
|
+
|
|
862
914
|
## Documentation
|
|
863
915
|
|
|
864
916
|
- [Guides](docs/README.md) — one page per area, every option with an example
|
|
@@ -867,16 +919,16 @@ could not answer: that is a denial made of an outage.
|
|
|
867
919
|
|
|
868
920
|
## Type safety, counted
|
|
869
921
|
|
|
870
|
-
**One hundred and
|
|
922
|
+
**One hundred and eighteen plausible mistakes, one hundred and eighteen refused at compile time — and
|
|
871
923
|
two gaps, named.**
|
|
872
924
|
|
|
873
925
|
The lists are typechecked and never run, with one `@ts-expect-error` per
|
|
874
926
|
mistake beside the shapes that must keep compiling:
|
|
875
927
|
`test/types/refusals.ts` (fourteen, on the shared vocabulary),
|
|
876
928
|
`test/types/port.ts` (twenty-two, on the identity stores' port, from the point
|
|
877
|
-
of view of the person implementing it), `test/types/auth.ts` (thirty-
|
|
929
|
+
of view of the person implementing it), `test/types/auth.ts` (thirty-four, on
|
|
878
930
|
`janus()`, from the point of view of the application — eight of them on the
|
|
879
|
-
second factor, three on sign-in codes) and `test/types/permissions.ts` (forty-eight, on the
|
|
931
|
+
second factor, three on sign-in codes, three on user events) and `test/types/permissions.ts` (forty-eight, on the
|
|
880
932
|
permission model and the questions asked of it). The rule
|
|
881
933
|
comes from `nxgt-data`, and so does the reason to
|
|
882
934
|
distrust the claim without the files: when it was last measured on
|
package/dist/auth/config.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import type { RelationStore } from '../permissions/port/types';
|
|
10
10
|
import type { Clock } from '../time/clock';
|
|
11
11
|
import { type Duration } from '../time/duration';
|
|
12
|
+
import type { UserEventListener } from './events';
|
|
12
13
|
import type { JanusStores } from './port/types';
|
|
13
14
|
import { type Sealer, type SealingKey } from './sealing';
|
|
14
15
|
import type { StandardSchemaV1 } from './standard-schema';
|
|
@@ -145,6 +146,13 @@ interface SharedConfig {
|
|
|
145
146
|
* `secondFactor` flows exist and `signIn` answers a session directly.
|
|
146
147
|
*/
|
|
147
148
|
readonly secondFactor?: SecondFactorConfig;
|
|
149
|
+
/**
|
|
150
|
+
* Called with every user event — `user.created`, `user.emailVerified`,
|
|
151
|
+
* `user.passwordReset`, `user.deleted` — once the write landed,
|
|
152
|
+
* and awaited before the flow answers. Any function will do; the coming
|
|
153
|
+
* `@nxgt/janus-webhooks` will sign and deliver them.
|
|
154
|
+
*/
|
|
155
|
+
readonly events?: UserEventListener;
|
|
148
156
|
}
|
|
149
157
|
/** What a TOTP second factor needs: a name for the app, and the keys that seal. */
|
|
150
158
|
export interface SecondFactorConfig {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/auth/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,KAAK,QAAQ,EAAiB,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAiB,KAAK,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAClB,MAAM,GACN,WAAW,GACX,eAAe,GACf,mBAAmB,GACnB,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;AAE/B;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GACnB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,UAAU,EAAE,GACrB;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;CAAE,CAAC;AAEtD;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,gBAAgB,CACxC,OAAO,EACP;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;CAAE,CAClD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC9B,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD;;;;;OAKG;IACH,WAAW,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CACpC;AAED,kCAAkC;AAClC,MAAM,WAAW,cAAc;IAC9B;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,mCAAmC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC7B,0BAA0B;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC;CACvC;AAED,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC5B,oFAAoF;IACpF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,yBAAyB;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC9C,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,UAAU,YAAY;IACrB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC;IACjC,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE;QACjB,2BAA2B;QAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC;QAChC,0BAA0B;QAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC;QAClC,oEAAoE;QACpE,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC;KAC/B,CAAC;IACF;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/auth/config.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/context.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import type { RelationStore } from '../permissions/port/types';
|
|
10
10
|
import type { Clock } from '../time/clock';
|
|
11
11
|
import { type PasswordHasher, type ResolvedConfig, type ResolvedType } from './config';
|
|
12
|
+
import type { UserEventListener } from './events';
|
|
12
13
|
import type { JanusStores, JsonObject, StoreCapabilities, UserPatch, UserRecord } from './port/types';
|
|
13
14
|
import type { User, UserRef, WriteOptions } from './types';
|
|
14
15
|
export interface Context {
|
|
@@ -24,8 +25,10 @@ export interface Context {
|
|
|
24
25
|
readonly verifiers: readonly PasswordHasher[];
|
|
25
26
|
/** Hashed once, lazily: what a missing user's password is compared against. */
|
|
26
27
|
dummyHash(): Promise<string>;
|
|
28
|
+
/** What `janus({ events })` was given, or `null`. */
|
|
29
|
+
readonly events: UserEventListener | null;
|
|
27
30
|
}
|
|
28
|
-
export declare function createContext(config: ResolvedConfig, store: JanusStores, relations: RelationStore | null, capabilities: StoreCapabilities, clock: Clock, hasher: PasswordHasher | null, verifiers: readonly PasswordHasher[]): Context;
|
|
31
|
+
export declare function createContext(config: ResolvedConfig, store: JanusStores, relations: RelationStore | null, capabilities: StoreCapabilities, clock: Clock, hasher: PasswordHasher | null, verifiers: readonly PasswordHasher[], events?: UserEventListener | null): Context;
|
|
29
32
|
/** A user of any type, as the degenericised core handles them. */
|
|
30
33
|
export type AnyUser = User<string, Record<string, unknown>>;
|
|
31
34
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/auth/context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAEN,KAAK,cAAc,EAEnB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/auth/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-flows.d.ts","sourceRoot":"","sources":["../../src/auth/email-flows.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAWZ,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"email-flows.d.ts","sourceRoot":"","sources":["../../src/auth/email-flows.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAWZ,MAAM,WAAW,CAAC;AAUnB,OAAO,KAAK,EAAe,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE7E;;;;;GAKG;AACH,wBAAgB,UAAU,CACzB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAC/B,cAAc,CAAC,OAAO,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,CA6IrD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type Id } from '../ids/id';
|
|
2
|
+
import type { Context } from './context';
|
|
3
|
+
/**
|
|
4
|
+
* What happened to a user, once it is written:
|
|
5
|
+
*
|
|
6
|
+
* - `user.created` — by `create` or `signUp`;
|
|
7
|
+
* - `user.emailVerified` — by `verifyEmail.confirm`, or by the link of
|
|
8
|
+
* `resetPassword.confirm` or the code of `signInCode.confirm`, which prove
|
|
9
|
+
* the e-mail too; never for an e-mail already verified;
|
|
10
|
+
* - `user.passwordReset` — by `resetPassword.confirm`;
|
|
11
|
+
* - `user.deleted` — by `delete`, once; a replay that finds nobody is none.
|
|
12
|
+
*/
|
|
13
|
+
export type UserEventType = 'user.created' | 'user.emailVerified' | 'user.passwordReset' | 'user.deleted';
|
|
14
|
+
/**
|
|
15
|
+
* A user event: **the user named by id, and nothing else** — no login, no
|
|
16
|
+
* e-mail, no field, no password, no token. Whoever receives it reads the rest
|
|
17
|
+
* from where it is kept, if they may.
|
|
18
|
+
*/
|
|
19
|
+
export interface UserEvent {
|
|
20
|
+
/** A UUIDv7 minted for this event: the key to deliver it once. */
|
|
21
|
+
readonly id: Id;
|
|
22
|
+
readonly type: UserEventType;
|
|
23
|
+
/**
|
|
24
|
+
* When the write landed: the `createdAt` or `updatedAt` it wrote, or, for
|
|
25
|
+
* a deletion, the time read just before it — not when the listener ran.
|
|
26
|
+
*/
|
|
27
|
+
readonly occurredAt: Date;
|
|
28
|
+
readonly userId: Id;
|
|
29
|
+
readonly userType: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* What `janus({ events })` takes: called once per event, **after** the write
|
|
33
|
+
* landed and before the flow answers — awaited, so a queue that stores the
|
|
34
|
+
* event durably has done so by then. A listener that throws fails nothing:
|
|
35
|
+
* the write happened, and the flow answers as it would have. The failure is a
|
|
36
|
+
* `JANUS_EVENT_FAILED` warning naming the event, never silence.
|
|
37
|
+
*/
|
|
38
|
+
export type UserEventListener = (event: UserEvent) => void | Promise<void>;
|
|
39
|
+
/** The listener given to `janus()`, or a wiring refusal for a JavaScript caller. */
|
|
40
|
+
export declare function resolveEvents(events: unknown, where: string): UserEventListener | null;
|
|
41
|
+
/**
|
|
42
|
+
* Hands one event to the listener after the write it reports. A flow whose
|
|
43
|
+
* steps after the write matter to the listener — a reset's revocation, a
|
|
44
|
+
* deletion's cleanup — runs them in a `try` and emits from its `finally`, so
|
|
45
|
+
* an outage there does not lose the event for good. Its failure is the application's, not the flow's: warned about, with what
|
|
46
|
+
* it takes to send the event again — never thrown, since the write has landed.
|
|
47
|
+
*/
|
|
48
|
+
export declare function emit(context: Context, type: UserEventType, user: {
|
|
49
|
+
readonly id: Id;
|
|
50
|
+
readonly type: string;
|
|
51
|
+
}, occurredAt: Date): Promise<void>;
|
|
52
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/auth/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,EAAU,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GACtB,cAAc,GACd,oBAAoB,GACpB,oBAAoB,GACpB,cAAc,CAAC;AAElB;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACzB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE3E,oFAAoF;AACpF,wBAAgB,aAAa,CAC5B,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,MAAM,GACX,iBAAiB,GAAG,IAAI,CAQ1B;AAED;;;;;;GAMG;AACH,wBAAsB,IAAI,CACzB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,aAAa,EACnB,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAChD,UAAU,EAAE,IAAI,GACd,OAAO,CAAC,IAAI,CAAC,CAoBf"}
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type { CookieConfig, FieldsJson, JanusConfig, MultiTypeConfig, Normalize, PasswordConfig, PasswordHasher, SecondFactorConfig, SessionConfig, SingleTypeConfig, UserSchema, UserTypeConfig, } from './config';
|
|
2
|
+
export type { UserEvent, UserEventListener, UserEventType, } from './events';
|
|
2
3
|
export { bunHasher, scryptHasher } from './hashers';
|
|
3
4
|
export { janus } from './janus';
|
|
4
5
|
export { assertStores } from './port/assert-stores';
|
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,UAAU,EACV,WAAW,EACX,eAAe,EACf,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,cAAc,GACd,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,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,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,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,UAAU,EACV,WAAW,EACX,eAAe,EACf,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,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,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,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,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;AAM3D,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,CA0DV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign-in-code.d.ts","sourceRoot":"","sources":["../../src/auth/sign-in-code.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAKZ,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"sign-in-code.d.ts","sourceRoot":"","sources":["../../src/auth/sign-in-code.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAKZ,MAAM,WAAW,CAAC;AAanB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE3D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC9B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,EACjC,MAAM,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,GAC3E,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAkF7D"}
|
package/dist/auth/users.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/auth/users.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAcZ,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/auth/users.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAcZ,MAAM,WAAW,CAAC;AAQnB,OAAO,KAAK,EACX,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,WAAW,EACX,cAAc,EACd,MAAM,SAAS,CAAC;AAEjB,KAAK,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAErC,mGAAmG;AACnG,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,GACnD,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAC1D,eAAe,CAAC,OAAO,CAAC,GACxB,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,GAC7C,cAAc,CAAC,OAAO,CAAC,GACvB,gBAAgB,CAAC,OAAO,CAAC,CAAC;AAE3B,wBAAgB,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,GAAG,UAAU,CAoSxE"}
|
package/dist/index.js
CHANGED
|
@@ -389,7 +389,7 @@ async function unlessVersionConflict(write) {
|
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
// src/auth/context.ts
|
|
392
|
-
function createContext(config, store, relations, capabilities, clock, hasher, verifiers) {
|
|
392
|
+
function createContext(config, store, relations, capabilities, clock, hasher, verifiers, events = null) {
|
|
393
393
|
let dummy = null;
|
|
394
394
|
return {
|
|
395
395
|
config,
|
|
@@ -399,6 +399,7 @@ function createContext(config, store, relations, capabilities, clock, hasher, ve
|
|
|
399
399
|
clock,
|
|
400
400
|
hasher,
|
|
401
401
|
verifiers,
|
|
402
|
+
events,
|
|
402
403
|
dummyHash: () => {
|
|
403
404
|
if (hasher === null) {
|
|
404
405
|
throw new TypeError("janus: no hasher to compare a dummy hash with");
|
|
@@ -595,6 +596,33 @@ async function holderOfEmail(context, type, email) {
|
|
|
595
596
|
return held !== null && normalizeEmail(held) === wanted ? record : null;
|
|
596
597
|
}
|
|
597
598
|
|
|
599
|
+
// src/auth/events.ts
|
|
600
|
+
function resolveEvents(events, where) {
|
|
601
|
+
if (events === undefined)
|
|
602
|
+
return null;
|
|
603
|
+
if (typeof events !== "function") {
|
|
604
|
+
throw new TypeError(`${where}: events must be a function that takes a user event — webhooks({ … }) from @nxgt/janus-webhooks, or your own`);
|
|
605
|
+
}
|
|
606
|
+
return events;
|
|
607
|
+
}
|
|
608
|
+
async function emit(context, type, user, occurredAt) {
|
|
609
|
+
const listener = context.events;
|
|
610
|
+
if (listener === null)
|
|
611
|
+
return;
|
|
612
|
+
const event = Object.freeze({
|
|
613
|
+
id: mintId2(occurredAt.getTime()),
|
|
614
|
+
type,
|
|
615
|
+
occurredAt: new Date(occurredAt.getTime()),
|
|
616
|
+
userId: user.id,
|
|
617
|
+
userType: user.type
|
|
618
|
+
});
|
|
619
|
+
try {
|
|
620
|
+
await listener(event);
|
|
621
|
+
} catch (failure) {
|
|
622
|
+
process.emitWarning(`janus: the events listener failed on ${type} ${event.id} for user ${user.id}: ${failure instanceof Error ? failure.name : typeof failure}`, { code: "JANUS_EVENT_FAILED" });
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
598
626
|
// src/auth/port/assert-stores.ts
|
|
599
627
|
var REQUIRED = {
|
|
600
628
|
users: [
|
|
@@ -948,10 +976,16 @@ function emailFlows(context, type, at) {
|
|
|
948
976
|
async confirm(secret) {
|
|
949
977
|
const where = at("verifyEmail.confirm");
|
|
950
978
|
const { token, user } = await redeem("verifyEmail", secret, where);
|
|
951
|
-
|
|
979
|
+
let newlyVerified = false;
|
|
980
|
+
const written = await writeUser(context, user.id, type, undefined, where, (record, now) => {
|
|
952
981
|
refuseStale(type, record, token, where, "token");
|
|
982
|
+
newlyVerified = record.emailVerifiedAt === null;
|
|
953
983
|
return { emailVerifiedAt: now };
|
|
954
|
-
})
|
|
984
|
+
});
|
|
985
|
+
if (newlyVerified) {
|
|
986
|
+
await emit(context, "user.emailVerified", written, written.updatedAt);
|
|
987
|
+
}
|
|
988
|
+
return toUser(written);
|
|
955
989
|
}
|
|
956
990
|
},
|
|
957
991
|
resetPassword: {
|
|
@@ -970,15 +1004,24 @@ function emailFlows(context, type, at) {
|
|
|
970
1004
|
checkPassword(type, password, where);
|
|
971
1005
|
const hash = await requireHasher(context, where).hash(password);
|
|
972
1006
|
const { token, user } = await redeem("resetPassword", secret, where);
|
|
1007
|
+
let newlyVerified = false;
|
|
973
1008
|
const written = await writeUser(context, user.id, type, undefined, where, (record, now) => {
|
|
974
1009
|
refuseStale(type, record, token, where, "token");
|
|
1010
|
+
newlyVerified = record.emailVerifiedAt === null;
|
|
975
1011
|
return {
|
|
976
1012
|
password: { hash, updatedAt: now },
|
|
977
1013
|
...record.emailVerifiedAt === null ? { emailVerifiedAt: now } : {}
|
|
978
1014
|
};
|
|
979
1015
|
});
|
|
980
|
-
|
|
981
|
-
|
|
1016
|
+
try {
|
|
1017
|
+
await store.sessions.revokeUserSessions(written.id, clock.now());
|
|
1018
|
+
await endSignInsWaiting(context, written.id);
|
|
1019
|
+
} finally {
|
|
1020
|
+
await emit(context, "user.passwordReset", written, written.updatedAt);
|
|
1021
|
+
if (newlyVerified) {
|
|
1022
|
+
await emit(context, "user.emailVerified", written, written.updatedAt);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
982
1025
|
return toUser(written);
|
|
983
1026
|
}
|
|
984
1027
|
}
|
|
@@ -1263,7 +1306,10 @@ function signInCodeFlows(context, type, at, finish) {
|
|
|
1263
1306
|
userType: type.name
|
|
1264
1307
|
});
|
|
1265
1308
|
}
|
|
1266
|
-
|
|
1309
|
+
if (user.emailVerifiedAt !== null)
|
|
1310
|
+
return finish(user, where);
|
|
1311
|
+
const proved = await writeUser(context, user.id, type, { ifVersion: user.version }, where, (_, now) => ({ emailVerifiedAt: now }));
|
|
1312
|
+
await emit(context, "user.emailVerified", proved, proved.updatedAt);
|
|
1267
1313
|
return finish(proved, where);
|
|
1268
1314
|
}
|
|
1269
1315
|
};
|
|
@@ -1288,7 +1334,7 @@ function typeApi(context, type) {
|
|
|
1288
1334
|
updatedAt: now
|
|
1289
1335
|
};
|
|
1290
1336
|
}
|
|
1291
|
-
|
|
1337
|
+
const inserted = await store.users.insertUser({
|
|
1292
1338
|
id: mintId2(now.getTime()),
|
|
1293
1339
|
type: type.name,
|
|
1294
1340
|
schemaVersion: type.schemaVersion,
|
|
@@ -1302,6 +1348,8 @@ function typeApi(context, type) {
|
|
|
1302
1348
|
createdAt: now,
|
|
1303
1349
|
updatedAt: now
|
|
1304
1350
|
});
|
|
1351
|
+
await emit(context, "user.created", inserted, inserted.createdAt);
|
|
1352
|
+
return inserted;
|
|
1305
1353
|
};
|
|
1306
1354
|
return {
|
|
1307
1355
|
async create(input) {
|
|
@@ -1354,10 +1402,17 @@ function typeApi(context, type) {
|
|
|
1354
1402
|
const record = await store.users.findUser(id);
|
|
1355
1403
|
if (record !== null && record.type !== type.name)
|
|
1356
1404
|
return false;
|
|
1405
|
+
const deletedAt = clock.now();
|
|
1357
1406
|
const deleted = record !== null && await store.users.deleteUser(id);
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1407
|
+
try {
|
|
1408
|
+
await store.sessions.deleteUserSessions(id);
|
|
1409
|
+
await store.tokens.deleteUserTokens(id);
|
|
1410
|
+
await context.relations?.deleteEntity({ type: type.name, id });
|
|
1411
|
+
} finally {
|
|
1412
|
+
if (deleted) {
|
|
1413
|
+
await emit(context, "user.deleted", { id, type: type.name }, deletedAt);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1361
1416
|
return deleted;
|
|
1362
1417
|
},
|
|
1363
1418
|
async signUp(input) {
|
|
@@ -1464,7 +1519,7 @@ function janus(config) {
|
|
|
1464
1519
|
if (relations !== null && typeof relations.deleteEntity !== "function") {
|
|
1465
1520
|
throw new TypeError(`${where}: relations must be a relation store — relations.deleteEntity is missing`);
|
|
1466
1521
|
}
|
|
1467
|
-
const context = createContext(resolved, guardStores(config.store), relations === null ? null : guardRelations(relations), capabilities, config.clock ?? systemClock, hasher, verifiers);
|
|
1522
|
+
const context = createContext(resolved, guardStores(config.store), relations === null ? null : guardRelations(relations), capabilities, config.clock ?? systemClock, hasher, verifiers, resolveEvents(config.events, where));
|
|
1468
1523
|
const shared = sharedApi(context);
|
|
1469
1524
|
const types = Object.fromEntries([...resolved.types.values()].map((type) => [
|
|
1470
1525
|
type.name,
|
|
@@ -1512,5 +1567,5 @@ export {
|
|
|
1512
1567
|
systemClock
|
|
1513
1568
|
};
|
|
1514
1569
|
|
|
1515
|
-
//# debugId=
|
|
1570
|
+
//# debugId=52492AAB2CF93D3A64756E2164756E21
|
|
1516
1571
|
//# sourceMappingURL=index.js.map
|