@nxgt/janus 0.22.0 → 0.23.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.
Files changed (54) hide show
  1. package/README.md +97 -17
  2. package/dist/auth/config/janus-config.d.ts +2 -0
  3. package/dist/auth/config/janus-config.d.ts.map +1 -1
  4. package/dist/auth/config/resolved-config.d.ts +1 -0
  5. package/dist/auth/config/resolved-config.d.ts.map +1 -1
  6. package/dist/auth/config/warn-throttle-window.d.ts.map +1 -1
  7. package/dist/auth/email-flows/change-email-token.d.ts +42 -0
  8. package/dist/auth/email-flows/change-email-token.d.ts.map +1 -0
  9. package/dist/auth/email-flows/change-email.d.ts +11 -0
  10. package/dist/auth/email-flows/change-email.d.ts.map +1 -0
  11. package/dist/auth/email-flows/flows.d.ts +5 -5
  12. package/dist/auth/email-flows/flows.d.ts.map +1 -1
  13. package/dist/auth/events.d.ts +3 -2
  14. package/dist/auth/events.d.ts.map +1 -1
  15. package/dist/auth/index.d.ts +1 -1
  16. package/dist/auth/index.d.ts.map +1 -1
  17. package/dist/auth/mail-requests.d.ts +6 -3
  18. package/dist/auth/mail-requests.d.ts.map +1 -1
  19. package/dist/auth/one-time/issue.d.ts +7 -1
  20. package/dist/auth/one-time/issue.d.ts.map +1 -1
  21. package/dist/auth/port/types/tokens.d.ts +10 -4
  22. package/dist/auth/port/types/tokens.d.ts.map +1 -1
  23. package/dist/auth/types/change-email.d.ts +51 -0
  24. package/dist/auth/types/change-email.d.ts.map +1 -0
  25. package/dist/auth/types/index.d.ts +1 -0
  26. package/dist/auth/types/index.d.ts.map +1 -1
  27. package/dist/auth/types/janus.d.ts +2 -1
  28. package/dist/auth/types/janus.d.ts.map +1 -1
  29. package/dist/auth/types/user-type.d.ts +3 -1
  30. package/dist/auth/types/user-type.d.ts.map +1 -1
  31. package/dist/auth/users/any-type-api.d.ts +2 -2
  32. package/dist/auth/users/any-type-api.d.ts.map +1 -1
  33. package/dist/auth/users/update.d.ts +2 -1
  34. package/dist/auth/users/update.d.ts.map +1 -1
  35. package/dist/conformance/cases/tokens/change-email.d.ts +8 -0
  36. package/dist/conformance/cases/tokens/change-email.d.ts.map +1 -0
  37. package/dist/conformance/cases/tokens/index.d.ts.map +1 -1
  38. package/dist/conformance/cases/tokens/kinds.d.ts.map +1 -1
  39. package/dist/conformance/index.js +92 -57
  40. package/dist/conformance/index.js.map +6 -5
  41. package/dist/errors/janus-error/codes.d.ts +7 -3
  42. package/dist/errors/janus-error/codes.d.ts.map +1 -1
  43. package/dist/index.js +134 -31
  44. package/dist/index.js.map +13 -11
  45. package/docs/README.md +1 -1
  46. package/docs/guide/adapters.md +13 -7
  47. package/docs/guide/email-flows.md +90 -10
  48. package/docs/guide/errors.md +3 -3
  49. package/docs/guide/events.md +5 -4
  50. package/docs/guide/users.md +10 -8
  51. package/docs/guide/vocabulary.md +3 -3
  52. package/docs/roadmap.md +13 -10
  53. package/docs/troubleshooting.md +32 -12
  54. package/package.json +3 -2
package/README.md CHANGED
@@ -27,7 +27,7 @@ const current = await auth.authenticate(request); // { user, session, token, ren
27
27
  bun add @nxgt/janus
28
28
  ```
29
29
 
30
- No runtime dependency. `typescript` (6) is a required peer. Your tsconfig resolves as a
30
+ No runtime dependency. `typescript` (6 or 7) is a required peer. Your tsconfig resolves as a
31
31
  bundler does (`"moduleResolution": "bundler"`, which Bun and every bundler
32
32
  use): the declarations import without extensions, so `nodenext` is not
33
33
  supported.
@@ -322,6 +322,9 @@ await auth.signOut(request);
322
322
  await auth.verifyEmail.send(user); // { token, email, expiresAt } — sending it is yours
323
323
  await auth.verifyEmail.confirm(token);
324
324
  await auth.verifyEmail.proveFirst(user); // privileged: FirstProof — { user, proved }; drops a squatter's password and factor
325
+ // { token, email, expiresAt } — email is the NEW address; gate it behind a fresh sign-in
326
+ await auth.changeEmail.send(user, newEmail);
327
+ await auth.changeEmail.confirm(token); // the user, moved to the new address and verified
325
328
  await auth.resetPassword.request(email); // … | null
326
329
  await auth.resetPassword.confirm(token, newPassword);
327
330
  await auth.signInCode.request(email); // { code, challenge, email, expiresAt, user } | null
@@ -379,8 +382,10 @@ else reaches the store and is asynchronous.
379
382
  meant. It is normalised with `'lowercaseTrim'` unless you say otherwise.
380
383
  - **`email`** defaults to the field named `email`. A type without one has no
381
384
  `findByEmail`, no `verifyEmail`, no `resetPassword`, no `signInCode` and
382
- no `magicLink` — they are absent from its type, not failing at run time.
383
- Changing the e-mail sets `emailVerified` back to `false`.
385
+ no `magicLink` and no `changeEmail` — they are absent from its type, not
386
+ failing at run time. Changing the e-mail with `update` sets `emailVerified`
387
+ back to `false`; to move a user to an address they have proved, use
388
+ [`changeEmail`](#changing-an-e-mail--changeemail).
384
389
  - **Per type**: `create`, `find` (or `null`), `findMany`, `get` (or
385
390
  `NOT_FOUND`), `list`, `update(user, patch)` — merged over the stored fields,
386
391
  then validated whole — `setActive`, `delete` and `signInProven` (below);
@@ -426,7 +431,8 @@ else reaches the store and is asynchronous.
426
431
  **Each flow counts on its own**: per address for
427
432
  `magicLink.request`, `signInCode.request` and `resetPassword.request` — an
428
433
  address nobody holds is counted and refused alike — and per user for
429
- `verifyEmail.send` and for `stepUp.request` when it e-mails a code.
434
+ `verifyEmail.send`, `changeEmail.send` (and, for its new address, per
435
+ address too) and for `stepUp.request` when it e-mails a code.
430
436
  `prepare` counts as `request` does, in the same window
431
437
  ([requests in two steps](#requests-in-two-steps--prepare)).
432
438
  `mail: { throttle: { attempts, window } }` changes it, `mail: { throttle:
@@ -545,7 +551,11 @@ A token of kind `stepUp` is that confirmation's challenge: kept apart from
545
551
  `magicLink`, new in 0.15, is a sign-in link's, kept apart from `signInCode`
546
552
  the same way: a sign-in code's challenge is held by whoever asked for it,
547
553
  and must never sign anyone in as a link. A store that lists the kinds — a
548
- `CHECK`, a validator's enum — adds `magicLink`.
554
+ `CHECK`, a validator's enum — adds `magicLink`. A token of kind
555
+ `changeEmail`, new in 0.23, is the link sent to a user's new address: its
556
+ `address` is that new address and its `codeHash` the address it replaces,
557
+ kept apart from `verifyEmail` so neither is redeemed as the other. The same
558
+ store adds `changeEmail`.
549
559
 
550
560
  An adapter written against `@nxgt/janus` 0.3 does not compile against this
551
561
  port until it implements `countAttempt`, nor one written against 0.6 until it
@@ -731,6 +741,64 @@ to type.
731
741
  confirms from a `POST` so mail scanners spend nothing, every error, routes
732
742
  and a test.
733
743
 
744
+ ### Changing an e-mail — `changeEmail`
745
+
746
+ ```ts
747
+ // Gate it: only a user who just proved who they are (assertFresh, or a step-up).
748
+ const issued = await auth.changeEmail.send(user, newEmail); // { token, email, expiresAt }
749
+ const link = `https://app.example/email/confirm?token=${issued.token}`;
750
+ await sendMail(issued.email, link); // the NEW address; say "ignore it if it was not you"
751
+
752
+ // the page's button POSTs the token back — never confirm on the link's GET
753
+ const changed = await auth.changeEmail.confirm(token); // now at the new address, verified
754
+ ```
755
+
756
+ A link sent to the **new** address moves the user there: nothing changes
757
+ until it is followed, and following it proves the address.
758
+
759
+ **Confirming moves the requester's whole account to the new address** — the
760
+ password and the second factor go with it, so whoever holds the new inbox
761
+ and follows the link signs in with them. The mail to the new address should
762
+ say so, and say "ignore it if it was not you".
763
+
764
+ **For now, send the link to the new address with your own mailer**:
765
+ `@nxgt/janus-mail` has no template for it yet (it sends the notice to the
766
+ former address, `emailChanged`). `mail.verifyEmail(issued)` is not the
767
+ change-of-e-mail mail: it would tell the new address to verify, not that the
768
+ account moves there.
769
+
770
+ - **On every user type with an e-mail**, with a password or without one. The
771
+ address is validated by the type's schema as `update` would: a malformed
772
+ one is `USER_INVALID` at `send`, and so is the user's current address
773
+ (equal once normalised): a change moves to another one, and verifying the
774
+ current one is `verifyEmail`'s job. That refusal spends nothing.
775
+ - **`send(user, email)`** takes a user you hold, so it is yours to gate:
776
+ behind `assertFresh` or a step-up, since whoever changes the e-mail takes
777
+ over password resets. An inactive user is `USER_INACTIVE`. One link is
778
+ live per user: a new `send` spends the ones before. It lives `'1h'`
779
+ (`tokens.changeEmail`). Throttled per new address and per user, five per
780
+ 10 minutes each (`MAIL_THROTTLED`, carrying the user's id), before anything
781
+ is issued. The user is counted first, so an account past its own count
782
+ exhausts no address; an address other people exhausted costs the asking
783
+ user one of their own.
784
+ - **Whether the address is taken is not asked at `send`**: it would tell who
785
+ has an account. `confirm` writes, and the store's own unique constraint
786
+ answers `LOGIN_TAKEN` — the token already spent, nothing written.
787
+ - **`confirm(token)`** spends the token, moves the user to the address and
788
+ marks it verified. The password, the second factor and the sessions are
789
+ untouched. A link whose e-mail is no longer the one `send` was asked from
790
+ is `TOKEN_SPENT` after an `update` that moved it (`update` spends the
791
+ links) or after a newer link; `TOKEN_STALE` comes from a write outside
792
+ janus, or from a `send` racing an `update`, and is checked again on the
793
+ record written. A write racing `confirm` is `VERSION_CONFLICT`, after the
794
+ token is spent.
795
+ - **Events**: `user.emailChanged`, carrying `formerEmail`, then
796
+ `user.emailVerified`. An `update` that moves the e-mail spends the
797
+ user's pending links, so a move back to the old address revives none.
798
+
799
+ [The e-mail flows guide](docs/guide/email-flows.md#changeemail) has a Hono
800
+ route for each step, with the notice to the former address.
801
+
734
802
  ### Requests in two steps — `prepare`
735
803
 
736
804
  ```ts
@@ -770,8 +838,8 @@ still goes out off their request.
770
838
  it replaces the decoy an unknown address needed; for nobody it confirms as
771
839
  `TOKEN_UNKNOWN`. `send()` answers the code issued under it.
772
840
  - Typed per flow: `PreparedRequest<IssuedToken & { user }>` for a link and
773
- a reset, `PreparedCode<User>` for a code. `verifyEmail.send` and
774
- `stepUp.request` have no `prepare`: they take a user the application
841
+ a reset, `PreparedCode<User>` for a code. `verifyEmail.send`,
842
+ `changeEmail.send` and `stepUp.request` have no `prepare`: they take a user the application
775
843
  already holds, so their time tells nothing about who has an account.
776
844
 
777
845
  [The e-mail flows guide](docs/guide/email-flows.md#requests-in-two-steps)
@@ -1000,10 +1068,10 @@ await auth.signUp({ email, password }); // the listener has the event before thi
1000
1068
  | `type` | Sent by |
1001
1069
  | --- | --- |
1002
1070
  | `user.created` | `create`, `signUp` |
1003
- | `user.emailVerified` | `verifyEmail.confirm` and `verifyEmail.proveFirst`; `resetPassword.confirm`, `magicLink.confirm` and `signInCode.confirm`, whose link or code proves the e-mail too — never for an e-mail already verified |
1071
+ | `user.emailVerified` | `verifyEmail.confirm` and `verifyEmail.proveFirst`; `changeEmail.confirm`, `resetPassword.confirm`, `magicLink.confirm` and `signInCode.confirm`, whose link or code proves the e-mail too — never for an e-mail already verified |
1004
1072
  | `user.passwordReset` | `resetPassword.confirm` |
1005
1073
  | `user.passwordChanged` | `changePassword`, `setPassword`; `signInCode.confirm`, `magicLink.confirm` and `verifyEmail.proveFirst` when their first proof of the e-mail dropped a password — never a reset, which is `user.passwordReset` alone |
1006
- | `user.emailChanged` | `update`, when it changed the e-mail — carrying `formerEmail`, the address before (`null` for none) |
1074
+ | `user.emailChanged` | `update` and `changeEmail.confirm`, when they changed the e-mail — carrying `formerEmail`, the address before (`null` for none) |
1007
1075
  | `user.secondFactorEnabled` | `secondFactor.activate`, once the factor is active — not `enroll`, which leaves it waiting |
1008
1076
  | `user.secondFactorDisabled` | `secondFactor.disable`, when it removed an active factor; `signInCode.confirm`, `magicLink.confirm` and `verifyEmail.proveFirst` when their first proof of the e-mail removed one, after `user.emailVerified` and any `user.passwordChanged` — never for a user who had none, or one still waiting |
1009
1077
  | `user.recoveryCodesRegenerated` | `secondFactor.regenerateRecoveryCodes` — not `activate`, whose codes come with `user.secondFactorEnabled` |
@@ -1182,7 +1250,7 @@ describeJanusStores({
1182
1250
  });
1183
1251
  ```
1184
1252
 
1185
- There are 58 cases. They cover:
1253
+ There are 59 cases. They cover:
1186
1254
  - round-trip, byte for byte — including every edge character the core lets
1187
1255
  through (control characters, U+FFFF, a surrogate pair);
1188
1256
  - uniqueness, as a constraint: of twenty concurrent inserts of one login,
@@ -1195,9 +1263,9 @@ There are 58 cases. They cover:
1195
1263
  and never brings back a revoked session, even one revoked at the same
1196
1264
  moment;
1197
1265
  - one-time tokens: a token of every kind the port names is stored, counted
1198
- and spent — a `CHECK` or an enum that forgot `stepUp` or `magicLink` fails
1266
+ and spent — a `CHECK` or an enum that forgot `stepUp`, `magicLink` or `changeEmail` fails
1199
1267
  here — and neither a step-up nor a sign-in link is ever counted, spent nor
1200
- answered as a sign-in code; of twenty
1268
+ answered as a sign-in code, nor an e-mail change as a verification; of twenty
1201
1269
  concurrent redemptions, exactly one succeeds; of twenty concurrent
1202
1270
  `countAttempt` calls, each answers a distinct count, and none is counted
1203
1271
  once a racing redemption spent the token;
@@ -1352,7 +1420,19 @@ e-mail changed by `update` is unverified again, and its first proof by link
1352
1420
  or code drops both too. **So does a user who enrolled a factor before
1353
1421
  proving their own e-mail**: they sign in with no challenge, and enrol again
1354
1422
  — offer `secondFactor.enroll` after such a sign-in, or send `verifyEmail`
1355
- before letting anyone enrol. An e-mail already verified keeps both.
1423
+ before letting anyone enrol. An e-mail already verified keeps both — and
1424
+ `changeEmail.confirm` verifies the new address itself, so the first proof
1425
+ afterwards drops nothing.
1426
+
1427
+ **Gate `changeEmail.send`, and tell the former address.** Whoever changes the
1428
+ e-mail takes over every password reset and sign-in link: call `send` behind
1429
+ `assertFresh` or a step-up, never on a session alone, and mail the address the
1430
+ user had from `user.emailChanged` (`formerEmail`) — once the change landed it
1431
+ is kept nowhere else.
1432
+
1433
+ **Run the adapter's migration before using `changeEmail`.** The tokens store
1434
+ must list the `changeEmail` kind (janus-drizzle 0.8's migration, janus-mongo
1435
+ 0.9's `syncMongoAdapter`); until then every `send` is `STORE_FAILED`.
1356
1436
 
1357
1437
  **Every `janus()` that signs users in needs the same `secondFactor`.** An
1358
1438
  instance without keys never signs in a user whose factor is active: `signIn`
@@ -1507,7 +1587,7 @@ and a tokens store that cannot answer fails every password sign-in with
1507
1587
  **The mail throttle counts each flow on its own, and it is not a per-client
1508
1588
  limit.** Five requests per window for one address (`magicLink.request`,
1509
1589
  `signInCode.request`, `resetPassword.request`) or one user
1510
- (`verifyEmail.send`, and `stepUp.request` when it e-mails), then
1590
+ (`verifyEmail.send`, `changeEmail.send`, and `stepUp.request` when it e-mails), then
1511
1591
  `MailThrottledError`: answer 429 with `Retry-After: retryAfter`. It is safe to
1512
1592
  show the visitor — it reveals nothing about accounts, unlike `reason`. A refused
1513
1593
  request is counted too, but the window is fixed and cannot be extended.
@@ -1617,7 +1697,7 @@ that sends one, since it is awaited: queue the event and return.
1617
1697
 
1618
1698
  ## Type safety, counted
1619
1699
 
1620
- **One hundred and sixty-nine plausible mistakes, one hundred and sixty-nine refused at compile time — and
1700
+ **One hundred and seventy-two plausible mistakes, one hundred and seventy-two refused at compile time — and
1621
1701
  two gaps, named.**
1622
1702
 
1623
1703
  The lists are typechecked and never run, with one `@ts-expect-error` per
@@ -1625,10 +1705,10 @@ mistake beside the shapes that must keep compiling. One is a single file:
1625
1705
  `test/types/refusals.ts` (fifteen, on the shared vocabulary). The other three
1626
1706
  are folders with one file per behaviour: `test/types/port/` (twenty-five, on
1627
1707
  the identity stores' port, from the point of view of the person implementing
1628
- it), `test/types/auth/` (eighty-one, on `janus()`, from the point of view of
1708
+ it), `test/types/auth/` (eighty-four, on `janus()`, from the point of view of
1629
1709
  the application — twelve of them on the second factor, three on sign-in codes,
1630
1710
  five on user events, four on step-ups, six on the sign-in throttle, five on the mail throttle, four on
1631
- 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) and `test/types/permissions/` (forty-eight, on the
1711
+ 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/` (forty-eight, on the
1632
1712
  permission model and the questions asked of it). The rule comes from
1633
1713
  `nxgt-data`, and so does the reason to distrust the claim without the files:
1634
1714
  when it was last measured on `@nxgt/mongo`, *seven of twelve plausible
@@ -52,6 +52,8 @@ interface SharedConfig {
52
52
  readonly magicLink?: Duration;
53
53
  /** How long a step-up's challenge waits, e-mailed or not. `'10m'` when absent. */
54
54
  readonly stepUp?: Duration;
55
+ /** How long the link sent to a new address waits. `'1h'` when absent. */
56
+ readonly changeEmail?: Duration;
55
57
  };
56
58
  /**
57
59
  * How `signIn` answers password guessing. **On by default**: past ten
@@ -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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"}
@@ -27,6 +27,7 @@ export interface ResolvedConfig {
27
27
  readonly signInCode: number;
28
28
  readonly magicLink: number;
29
29
  readonly stepUp: number;
30
+ readonly changeEmail: number;
30
31
  };
31
32
  /** `null` when `signIn.throttle` is `false`. */
32
33
  readonly signInThrottle: {
@@ -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,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"}
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;QACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;KAC7B,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"}
@@ -1 +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"}
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;AAyBxD,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,42 @@
1
+ /**
2
+ * The token a change of e-mail sends and redeems: issued for the NEW
3
+ * address, bound to the address it replaces, and refused when the user's
4
+ * e-mail is no longer that one.
5
+ *
6
+ * `address` holds the new address; `codeHash` holds the former one, keyed by
7
+ * the token's secret (`TokenRecord.codeHash`), so the port needs no field and
8
+ * a dump of the store reveals neither.
9
+ */
10
+ import { type ResolvedType } from '../config';
11
+ import { type Context } from '../context';
12
+ import type { TokenRecord, UserRecord } from '../port/types';
13
+ import type { IssuedToken } from '../types';
14
+ /** What binds a token to the address it replaces. */
15
+ export declare const formerHash: (secret: string, former: string | null) => string;
16
+ /**
17
+ * Validates `email` as the user's new address — by the type's own schema, the
18
+ * other fields as they are — and gives it back as the schema left it. An
19
+ * address equal to the current one once normalised is `USER_INVALID`: moving
20
+ * to where the user already is means nothing (`verifyEmail` proves it).
21
+ */
22
+ export declare function newAddressOf(type: ResolvedType, user: UserRecord, email: string, where: string): Promise<string>;
23
+ /**
24
+ * Issues the token for the validated new `address`, bound to the address the
25
+ * user has now, and spends the user's earlier ones.
26
+ */
27
+ export declare function issueChangeEmailToken(context: Context, type: ResolvedType, user: UserRecord, address: string): Promise<IssuedToken>;
28
+ /**
29
+ * Refuses a token asked from an address the user has left: the link would
30
+ * move somebody who is no longer where the request came from.
31
+ */
32
+ export declare function refuseMoved(type: ResolvedType, user: UserRecord, token: TokenRecord, secret: string, where: string): void;
33
+ /**
34
+ * Spends a change's token and says why it cannot be used, when it cannot. A
35
+ * user gone since, or of another type, is as good as no token; a user who
36
+ * moved since the link was asked is stale.
37
+ */
38
+ export declare function redeemChangeEmailToken(context: Context, type: ResolvedType, secret: string, where: string): Promise<{
39
+ token: TokenRecord;
40
+ user: UserRecord;
41
+ }>;
42
+ //# sourceMappingURL=change-email-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-email-token.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/change-email-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,KAAK,OAAO,EAAuC,MAAM,YAAY,CAAC;AAE/E,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,qDAAqD;AACrD,eAAO,MAAM,UAAU,GAAI,QAAQ,MAAM,EAAE,QAAQ,MAAM,GAAG,IAAI,KAAG,MAGjE,CAAC;AAEH;;;;;GAKG;AACH,wBAAsB,YAAY,CACjC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CA0BjB;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAC1C,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,MAAM,GACb,OAAO,CAAC,WAAW,CAAC,CAmBtB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAC1B,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACX,IAAI,CAQN;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC3C,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAanD"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Changing a user's e-mail: send a token to the NEW address, then redeem it
3
+ * to move the user there — verified, since the link reached its inbox.
4
+ */
5
+ import type { At } from '../at';
6
+ import type { ResolvedType } from '../config';
7
+ import { type AnyUser, type Context } from '../context';
8
+ import type { ChangeEmailApi } from '../types';
9
+ /** `changeEmail` of one user type: `send` to the new address, then `confirm`. */
10
+ export declare function changeEmailFlows(context: Context, type: ResolvedType, at: At): ChangeEmailApi<AnyUser>['changeEmail'];
11
+ //# sourceMappingURL=change-email.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-email.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/change-email.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,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,EAMZ,MAAM,YAAY,CAAC;AAIpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAe/C,iFAAiF;AACjF,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,cAAc,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CA4BxC"}
@@ -2,12 +2,12 @@
2
2
  import type { At } from '../at';
3
3
  import type { ResolvedType } from '../config';
4
4
  import type { AnyUser, Context } from '../context';
5
- import type { ResetPasswordApi, VerifyEmailApi } from '../types';
5
+ import type { ChangeEmailApi, ResetPasswordApi, VerifyEmailApi } from '../types';
6
6
  /**
7
7
  * The e-mail flows: send a one-time token by e-mail, then confirm it —
8
- * verifying the address, or resetting the password. Both confirm the same
9
- * way: spend the token, read the user, and check the address again on the
10
- * very record the write replaces.
8
+ * verifying the address, resetting the password, or moving to a new address.
9
+ * All three confirm the same way: spend the token, read the user, and check
10
+ * the address again on the very record the write replaces.
11
11
  */
12
- export declare function emailFlows(context: Context, type: ResolvedType, at: At): VerifyEmailApi<AnyUser> & ResetPasswordApi<AnyUser>;
12
+ export declare function emailFlows(context: Context, type: ResolvedType, at: At): VerifyEmailApi<AnyUser> & ResetPasswordApi<AnyUser> & ChangeEmailApi<AnyUser>;
13
13
  //# sourceMappingURL=flows.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/flows.ts"],"names":[],"mappings":"AAAA,mFAAmF;AAEnF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAIjE;;;;;GAKG;AACH,wBAAgB,UAAU,CACzB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,cAAc,CAAC,OAAO,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAKrD"}
1
+ {"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/flows.ts"],"names":[],"mappings":"AAAA,mFAAmF;AAEnF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EACX,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,MAAM,UAAU,CAAC;AAKlB;;;;;GAKG;AACH,wBAAgB,UAAU,CACzB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,cAAc,CAAC,OAAO,CAAC,GACzB,gBAAgB,CAAC,OAAO,CAAC,GACzB,cAAc,CAAC,OAAO,CAAC,CAMvB"}
@@ -5,7 +5,7 @@ import type { Context } from './context';
5
5
  *
6
6
  * - `user.created` — by `create` or `signUp`;
7
7
  * - `user.emailVerified` — by `verifyEmail.confirm`, or by the link of
8
- * `resetPassword.confirm` or `magicLink.confirm`, or the code of
8
+ * `resetPassword.confirm`, `magicLink.confirm` or `changeEmail.confirm`, or the code of
9
9
  * `signInCode.confirm`, which prove the e-mail too; never for an e-mail
10
10
  * already verified;
11
11
  * - `user.passwordReset` — by `resetPassword.confirm`;
@@ -13,7 +13,8 @@ import type { Context } from './context';
13
13
  * `signInCode.confirm` and `magicLink.confirm` when their first proof of
14
14
  * the e-mail dropped a password, after `user.emailVerified`; never by a
15
15
  * reset, which is `user.passwordReset` alone;
16
- * - `user.emailChanged` — by `update`, when it changed the e-mail — compared
16
+ * - `user.emailChanged` — by `update` and `changeEmail.confirm`, when they
17
+ * changed the e-mail — compared
17
18
  * normalised, the same test that makes the new one unverified. The one
18
19
  * event that carries more than the user: `formerEmail`;
19
20
  * - `user.secondFactorEnabled` — by `secondFactor.activate`, once the factor
@@ -1 +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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,MAAM,aAAa,GACtB,cAAc,GACd,oBAAoB,GACpB,oBAAoB,GACpB,sBAAsB,GACtB,mBAAmB,GACnB,0BAA0B,GAC1B,2BAA2B,GAC3B,+BAA+B,GAC/B,uBAAuB,GACvB,wBAAwB,GACxB,cAAc,CAAC;AAElB;;;;;;;GAOG;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;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,uEAAuE;AACvE,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,GAAG,WAAW,CAAC,CAAC;AAEvE;;;;;;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,EAChB,MAAM,GAAE,WAAgB,GACtB,OAAO,CAAC,IAAI,CAAC,CAqBf"}
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,MAAM,aAAa,GACtB,cAAc,GACd,oBAAoB,GACpB,oBAAoB,GACpB,sBAAsB,GACtB,mBAAmB,GACnB,0BAA0B,GAC1B,2BAA2B,GAC3B,+BAA+B,GAC/B,uBAAuB,GACvB,wBAAwB,GACxB,cAAc,CAAC;AAElB;;;;;;;GAOG;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;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,uEAAuE;AACvE,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,GAAG,WAAW,CAAC,CAAC;AAEvE;;;;;;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,EAChB,MAAM,GAAE,WAAgB,GACtB,OAAO,CAAC,IAAI,CAAC,CAqBf"}
@@ -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, 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, 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
@@ -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,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,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"}
@@ -22,7 +22,8 @@
22
22
  * past the limit, as a registered one is, so being throttled never tells
23
23
  * who has an account;
24
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.
25
+ * knows — `verifyEmail.send`, `changeEmail.send` (and, for the address it
26
+ * names, per address too), and `stepUp.request` when it e-mails a code.
26
27
  * Nobody without the user's session or the application's call can spend
27
28
  * that count, so a loop on their address leaves their own requests alone;
28
29
  * and whatever addresses one user moves through, they are sent at most as
@@ -40,12 +41,14 @@
40
41
  import { type ResolvedType } from './config';
41
42
  import type { Context } from './context';
42
43
  /** The flows that hand out something to e-mail, each counted on its own. */
43
- export type MailFlow = 'magicLink' | 'signInCode' | 'resetPassword' | 'verifyEmail' | 'stepUp';
44
+ export type MailFlow = 'magicLink' | 'signInCode' | 'resetPassword' | 'verifyEmail' | 'stepUp' | 'changeEmail';
44
45
  /**
45
46
  * Counts one request of `flow` for `email` — before it is looked up — and
46
47
  * refuses past the last one, whether anybody holds the address or not.
48
+ * `userId`, for a request made for a user (`changeEmail.send`), is only
49
+ * carried by the refusal; the count stays the address's.
47
50
  */
48
- export declare function countMailByAddress(context: Context, type: ResolvedType, flow: MailFlow, email: string, where: string): Promise<void>;
51
+ export declare function countMailByAddress(context: Context, type: ResolvedType, flow: MailFlow, email: string, where: string, userId?: string): Promise<void>;
49
52
  /** Counts one request of `flow` for the user `userId`, and refuses past the last one. */
50
53
  export declare function countMailByUser(context: Context, type: ResolvedType, flow: MailFlow, userId: string, where: string): Promise<void>;
51
54
  //# sourceMappingURL=mail-requests.d.ts.map
@@ -1 +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
+ {"version":3,"file":"mail-requests.d.ts","sourceRoot":"","sources":["../../src/auth/mail-requests.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;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,GACR,aAAa,CAAC;AAUjB;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAWf;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"}
@@ -20,7 +20,13 @@ interface OneTimeRequest {
20
20
  * Issues a one-time token: 32 random bytes, given back once, and only their
21
21
  * hash stored.
22
22
  */
23
- export declare function issueOneTime(context: Context, token: OneTimeRequest): Promise<{
23
+ export declare function issueOneTime(context: Context, token: OneTimeRequest & {
24
+ /**
25
+ * What the token is bound to, stored as its `codeHash`: `changeEmail`
26
+ * binds the address it replaces. `null` by default.
27
+ */
28
+ readonly boundHash?: string;
29
+ }): Promise<{
24
30
  readonly secret: string;
25
31
  readonly expiresAt: Date;
26
32
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"issue.d.ts","sourceRoot":"","sources":["../../../src/auth/one-time/issue.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAI/C,UAAU,cAAc;IACvB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CACjC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,cAAc,GACnB,OAAO,CAAC;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC,CAEhE;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC9B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,cAAc,GACnB,OAAO,CAAC;IACV,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB,CAAC,CAGD"}
1
+ {"version":3,"file":"issue.d.ts","sourceRoot":"","sources":["../../../src/auth/one-time/issue.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAI/C,UAAU,cAAc;IACvB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CACjC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,cAAc,GAAG;IACvB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B,GACC,OAAO,CAAC;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC,CAEhE;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC9B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,cAAc,GACnB,OAAO,CAAC;IACV,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB,CAAC,CAGD"}
@@ -23,8 +23,12 @@ import type { Id } from '../../../ids/id';
23
23
  * sent by e-mail, or, with an active second factor, a code from their app.
24
24
  * A kind of its own, so a sign-in code never confirms an action nor an
25
25
  * action's code signs anyone in.
26
+ * - `changeEmail`: a link sent by e-mail to a user's new address, to confirm
27
+ * it. A kind of its own, so a verification link — which proves the address
28
+ * the user already holds — never changes an address, nor this one verifies
29
+ * the current one.
26
30
  */
27
- export type TokenKind = 'verifyEmail' | 'resetPassword' | 'secondFactor' | 'signInCode' | 'magicLink' | 'stepUp';
31
+ export type TokenKind = 'verifyEmail' | 'resetPassword' | 'secondFactor' | 'signInCode' | 'magicLink' | 'stepUp' | 'changeEmail';
28
32
  /** A one-time token, as a store holds it: its hash, never its secret. */
29
33
  export interface TokenRecord {
30
34
  /** `sha256` of the token's secret, hex. Unique across the store. */
@@ -33,14 +37,16 @@ export interface TokenRecord {
33
37
  readonly userId: Id;
34
38
  /**
35
39
  * The e-mail the token was sent to — the one a verification marks
36
- * verified. `''` for a second-factor challenge, which nothing was sent for.
40
+ * verified; for `changeEmail`, the new address the user will hold.
41
+ * `''` for a second-factor challenge, which nothing was sent for.
37
42
  */
38
43
  readonly address: string;
39
44
  /**
40
45
  * For a token redeemed with an e-mailed code — `signInCode`, and a
41
46
  * `stepUp` sent by e-mail — the code's hash, keyed
42
- * by the token's secret, so the tokens alone do not reveal it. `null` for
43
- * every other kind: the core writes it so, and a store keeps what it is
47
+ * by the token's secret, so the tokens alone do not reveal it. For
48
+ * `changeEmail`, the address it replaces, keyed by the secret the same way.
49
+ * `null` for every other kind: the core writes it so, and a store keeps what it is
44
50
  * given.
45
51
  */
46
52
  readonly codeHash: string | null;
@@ -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;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,SAAS,GAClB,aAAa,GACb,eAAe,GACf,cAAc,GACd,YAAY,GACZ,WAAW,GACX,QAAQ,CAAC;AAEZ,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;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;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;CAC9C"}
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;CAC9C"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * What a user type with an e-mail answers besides: moving the user to a new
3
+ * address, by a link sent to it.
4
+ *
5
+ * Part of what `janus()` hands back; `./index` gathers it.
6
+ */
7
+ import type { IssuedToken } from './email-flows';
8
+ import type { UserRef } from './user';
9
+ /**
10
+ * Changing a user's e-mail, proved: a link is sent to the **new** address,
11
+ * and following it moves the user there.
12
+ */
13
+ export interface ChangeEmailApi<U> {
14
+ readonly changeEmail: {
15
+ /**
16
+ * Issues a link's token for the new address `email`. `email` is the
17
+ * `email` field's value, validated by the type's schema as `update`
18
+ * would — a malformed one is `USER_INVALID` — and the answer's `email`
19
+ * is the address to send the link to. Nothing changes until `confirm`.
20
+ *
21
+ * **Gate it**: behind a fresh sign-in (`assertFresh`) or a step-up, since
22
+ * whoever changes the e-mail takes over password resets. Put `token` in
23
+ * a link to a page of yours, and **only in the e-mail** to the new
24
+ * address. Tell the former address from `user.emailChanged`.
25
+ *
26
+ * Whether somebody already holds the new address is **not** checked here
27
+ * — it would tell who has an account; `confirm` refuses `LOGIN_TAKEN`.
28
+ * The user's earlier links are spent: only the last one sent works.
29
+ * **Throttled** per user and per new address (`janus({ mail: { throttle } })`):
30
+ * `MAIL_THROTTLED` with `retryAfter`, and no token. An unknown user is
31
+ * `NOT_FOUND`, an inactive one `USER_INACTIVE`.
32
+ */
33
+ send(user: UserRef, email: string): Promise<IssuedToken>;
34
+ /**
35
+ * Spends the link's token, moves the user to the address it was sent to
36
+ * and marks it verified — the link reached its inbox. Password, second
37
+ * factor and sessions are untouched. Emits `user.emailChanged` (with
38
+ * `formerEmail`) and `user.emailVerified`.
39
+ *
40
+ * **Call it from a `POST`**, never from the `GET` of the link. An
41
+ * unknown, spent or lapsed token is `TOKEN_UNKNOWN`, `TOKEN_SPENT` or
42
+ * `TOKEN_EXPIRED`; a user whose e-mail is no longer the one the link
43
+ * was asked from is `TOKEN_STALE`; an address another user of the type
44
+ * holds is `LOGIN_TAKEN` — the token already spent; an inactive user
45
+ * is `USER_INACTIVE`. A user written in between is `VERSION_CONFLICT`,
46
+ * also after the token is spent.
47
+ */
48
+ confirm(token: string): Promise<U>;
49
+ };
50
+ }
51
+ //# sourceMappingURL=change-email.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-email.d.ts","sourceRoot":"","sources":["../../../src/auth/types/change-email.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE;QACrB;;;;;;;;;;;;;;;;;WAiBG;QACH,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QACzD;;;;;;;;;;;;;WAaG;QACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACnC,CAAC;CACF"}
@@ -7,6 +7,7 @@
7
7
  * keeps each schema's type travelling through every public method without
8
8
  * dragging a type parameter through every internal function.
9
9
  */
10
+ export type { ChangeEmailApi } from './change-email';
10
11
  export type { Checked } from './checks';
11
12
  export type { FirstProof, IssuedCode, IssuedToken, ResetPasswordApi, SignInCodeApi, VerifyEmailApi, } from './email-flows';
12
13
  export type { FindByEmailApi } from './find-by-email';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/types/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,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,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"}
@@ -5,6 +5,7 @@
5
5
  * Part of what `janus()` hands back; `./index` gathers it.
6
6
  */
7
7
  import type { JanusConfig, MultiTypeConfig, SingleTypeConfig } from '../config';
8
+ import type { ChangeEmailApi } from './change-email';
8
9
  import type { ResetPasswordApi, SignInCodeApi, VerifyEmailApi } from './email-flows';
9
10
  import type { FindByEmailApi } from './find-by-email';
10
11
  import type { EmailOf, FieldsInput, LoginOf, SingleAsType, UserOf, UserOfType } from './inference';
@@ -28,7 +29,7 @@ type SignInAnswer<Name extends string, Def, TwoFactor extends boolean> = TwoFact
28
29
  * The whole surface of one user type, with only the flows its configuration
29
30
  * allows. `TwoFactor` is whether `janus()` was given a `secondFactor`.
30
31
  */
31
- 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>> & 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>>);
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>>);
32
33
  /** What every configuration answers, whatever its user types. */
33
34
  export interface SharedApi<U extends {
34
35
  readonly type: string;