@nxgt/janus 0.16.1 → 0.17.1

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 (65) hide show
  1. package/README.md +99 -11
  2. package/dist/auth/config/index.d.ts +1 -1
  3. package/dist/auth/config/index.d.ts.map +1 -1
  4. package/dist/auth/config/janus-config.d.ts +24 -3
  5. package/dist/auth/config/janus-config.d.ts.map +1 -1
  6. package/dist/auth/config/resolve-config.d.ts.map +1 -1
  7. package/dist/auth/config/resolve-devices.d.ts +5 -0
  8. package/dist/auth/config/resolve-devices.d.ts.map +1 -0
  9. package/dist/auth/config/resolved-config.d.ts +4 -0
  10. package/dist/auth/config/resolved-config.d.ts.map +1 -1
  11. package/dist/auth/devices/hint.d.ts +30 -0
  12. package/dist/auth/devices/hint.d.ts.map +1 -0
  13. package/dist/auth/devices/index.d.ts +10 -0
  14. package/dist/auth/devices/index.d.ts.map +1 -0
  15. package/dist/auth/devices/report.d.ts +11 -0
  16. package/dist/auth/devices/report.d.ts.map +1 -0
  17. package/dist/auth/devices/token.d.ts +13 -0
  18. package/dist/auth/devices/token.d.ts.map +1 -0
  19. package/dist/auth/email-sign-in.d.ts +4 -2
  20. package/dist/auth/email-sign-in.d.ts.map +1 -1
  21. package/dist/auth/events.d.ts +18 -5
  22. package/dist/auth/events.d.ts.map +1 -1
  23. package/dist/auth/index.d.ts +2 -2
  24. package/dist/auth/index.d.ts.map +1 -1
  25. package/dist/auth/magic-link.d.ts.map +1 -1
  26. package/dist/auth/second-factor/challenge.d.ts +2 -2
  27. package/dist/auth/second-factor/challenge.d.ts.map +1 -1
  28. package/dist/auth/second-factor/flows.d.ts +6 -4
  29. package/dist/auth/second-factor/flows.d.ts.map +1 -1
  30. package/dist/auth/second-factor/recovery.d.ts +6 -2
  31. package/dist/auth/second-factor/recovery.d.ts.map +1 -1
  32. package/dist/auth/sessions/open-session.d.ts +7 -1
  33. package/dist/auth/sessions/open-session.d.ts.map +1 -1
  34. package/dist/auth/sign-in-code.d.ts.map +1 -1
  35. package/dist/auth/types/email-flows.d.ts +2 -2
  36. package/dist/auth/types/email-flows.d.ts.map +1 -1
  37. package/dist/auth/types/index.d.ts +1 -1
  38. package/dist/auth/types/index.d.ts.map +1 -1
  39. package/dist/auth/types/magic-link.d.ts +2 -2
  40. package/dist/auth/types/magic-link.d.ts.map +1 -1
  41. package/dist/auth/types/password.d.ts +10 -3
  42. package/dist/auth/types/password.d.ts.map +1 -1
  43. package/dist/auth/types/second-factor.d.ts +6 -3
  44. package/dist/auth/types/second-factor.d.ts.map +1 -1
  45. package/dist/auth/types/sign-in.d.ts +32 -0
  46. package/dist/auth/types/sign-in.d.ts.map +1 -1
  47. package/dist/auth/users/flow-types.d.ts +2 -1
  48. package/dist/auth/users/flow-types.d.ts.map +1 -1
  49. package/dist/auth/users/password-flows.d.ts.map +1 -1
  50. package/dist/auth/users/sign-in.d.ts +5 -1
  51. package/dist/auth/users/sign-in.d.ts.map +1 -1
  52. package/dist/index.js +172 -73
  53. package/dist/index.js.map +19 -15
  54. package/docs/README.md +2 -1
  55. package/docs/guide/devices.md +429 -0
  56. package/docs/guide/events.md +53 -8
  57. package/docs/guide/magic-link.md +25 -3
  58. package/docs/guide/second-factor.md +30 -5
  59. package/docs/guide/sessions.md +8 -0
  60. package/docs/guide/sign-in-code.md +22 -3
  61. package/docs/guide/users.md +3 -2
  62. package/docs/guide/vocabulary.md +4 -3
  63. package/docs/roadmap.md +12 -8
  64. package/docs/troubleshooting.md +210 -4
  65. package/package.json +1 -1
package/README.md CHANGED
@@ -732,6 +732,53 @@ session should not do alone.
732
732
  [The step-up guide](docs/guide/step-up.md) has the two requests, every
733
733
  error, and the codes an app confirms.
734
734
 
735
+ ### Devices — `devices`
736
+
737
+ ```ts
738
+ import { z } from 'zod';
739
+ import { createMemoryStores, janus, scryptHasher } from '@nxgt/janus';
740
+
741
+ const auth = janus({
742
+ user: z.object({ email: z.email() }),
743
+ password: { login: 'email' },
744
+ store: createMemoryStores(),
745
+ hasher: scryptHasher(),
746
+ devices: { keys: [{ id: '2026-09', key: process.env.DEVICES_KEY ?? '' }] }, // openssl rand -base64 32
747
+ });
748
+
749
+ // `cookie`: the device cookie the request carries — string | undefined
750
+ const signedIn = await auth.signIn({ email, password }, { device: cookie ?? null });
751
+ // { status: 'signedIn', user, session, token, newDevice, deviceToken }
752
+ // keep signedIn.deviceToken in a long-lived HttpOnly cookie; newDevice: true sent user.newDeviceSignedIn
753
+ ```
754
+
755
+ A sign-in from a device the user had not signed in from is told apart, so
756
+ you can tell the user. **Nothing is stored**: the client keeps a device
757
+ token janus signs, and presents it at its next sign-in.
758
+
759
+ - **`{ device }` is the last argument** of `signUp`, `signIn`,
760
+ `secondFactor.confirm`, `secondFactor.recover`, `signInCode.confirm` and
761
+ `magicLink.confirm` — a `SignInOptions`: the token the client holds, or
762
+ `null` when it holds none. **Absent, the call is not tracked**:
763
+ `newDevice: false`, `deviceToken: null`, no event.
764
+ - **Every answer that opens a session carries `newDevice` and
765
+ `deviceToken`.** `signUp` mints the first token and is never new. A
766
+ malformed, forged or another user's token is a new device, never an error.
767
+ - **`user.newDeviceSignedIn`** is sent for a new device, once the sign-in is
768
+ complete, with the new session's `sessionId`.
769
+ - **`devices: { keys }`** — a `DevicesConfig` — wires it; the keys take
770
+ `secondFactor.keys`' format. The first signs,
771
+ every one checks: a token signed by an older key is known, and handed back
772
+ signed with the first. **Removing a key is the only way to forget devices,
773
+ and it forgets every device that key signed.**
774
+
775
+ [`@nxgt/janus-mail`](https://www.npmjs.com/package/@nxgt/janus-mail)'s
776
+ `newSignIn` sends the notice, and
777
+ [`@nxgt/janus-hono`](https://www.npmjs.com/package/@nxgt/janus-hono)'s
778
+ `deviceOf` and `sendSession` keep the cookie.
779
+ [The devices guide](docs/guide/devices.md) has the cookie, the second
780
+ factor, the e-mail, key rotation, every error and a test.
781
+
735
782
  ### User events — `events`
736
783
 
737
784
  ```ts
@@ -765,28 +812,31 @@ await auth.signUp({ email, password }); // the listener has the event before thi
765
812
  | `user.secondFactorDisabled` | `secondFactor.disable`, when it removed an active factor; `signInCode.confirm` and `magicLink.confirm` 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 |
766
813
  | `user.recoveryCodesRegenerated` | `secondFactor.regenerateRecoveryCodes` — not `activate`, whose codes come with `user.secondFactorEnabled` |
767
814
  | `user.recoveryCodeUsed` | `secondFactor.recover`, once the recovery code is spent: a sign-in without the user's phone |
815
+ | `user.newDeviceSignedIn` | `signIn`, `secondFactor.confirm`, `secondFactor.recover`, `signInCode.confirm` and `magicLink.confirm`, given a `device` its token did not prove, once the sign-in is complete — carrying `sessionId`, the new session's. Never `signUp`, nor a call given no device |
768
816
  | `user.deleted` | `delete`, once — a replay that deletes nobody sends nothing |
769
817
 
770
818
  - **The user is named by id, and nothing else**: no login, no field, no
771
819
  password, no token. Whoever receives the event reads the rest from where it
772
- is kept, if they may. One exception: `user.emailChanged` carries
820
+ is kept, if they may. Two exceptions: `user.emailChanged` carries
773
821
  `formerEmail`, which nothing keeps once the write landed, so a notice can
774
- reach the inbox the account just left. `@nxgt/janus-webhooks` never posts
775
- it.
822
+ reach the inbox the account just left; and `user.newDeviceSignedIn`
823
+ carries `sessionId`, the session the new device holds.
824
+ `@nxgt/janus-webhooks` posts neither.
776
825
  - **Each event has an `id` of its own**, a UUIDv7: the key to deliver it once.
777
826
  - **The listener runs after the write, and is awaited** before the
778
827
  flow answers, so a durable queue has the event by then. `occurredAt` is the
779
828
  write's own time. A refused flow sends nothing.
780
829
  - **Typed**: `events` is a `UserEventListener`; `UserEventType` is the closed
781
- union of the ten types, so a `switch` on `event.type` is exhaustive — and
782
- a new type, like the two recovery-code ones in 0.10 or the two change ones
783
- in 0.14, breaks it until handled.
830
+ union of the eleven types, so a `switch` on `event.type` is exhaustive —
831
+ and a new type, like the two recovery-code ones in 0.10, the two change
832
+ ones in 0.14 or `user.newDeviceSignedIn` in 0.17, breaks it until handled.
784
833
  - **A listener that throws fails no flow** — the write happened. It is a
785
834
  `JANUS_EVENT_FAILED` warning naming the event's type, its id and the user's
786
835
  id, never the failure's message.
787
836
 
788
837
  To tell the user their password or e-mail changed, their second factor was
789
- turned on or off, or that a recovery code was used, send
838
+ turned on or off, that a recovery code was used, or that a new device signed
839
+ in, send
790
840
  [`@nxgt/janus-mail`](https://www.npmjs.com/package/@nxgt/janus-mail)'s notice
791
841
  from the listener — or from a queue it feeds:
792
842
 
@@ -816,7 +866,7 @@ async events(event) {
816
866
 
817
867
  To post them as signed webhooks:
818
868
  [`@nxgt/janus-webhooks`](https://www.npmjs.com/package/@nxgt/janus-webhooks).
819
- [The user events guide](docs/guide/events.md) has the listener, the ten
869
+ [The user events guide](docs/guide/events.md) has the listener, the eleven
820
870
  types, mail for a recovery code used or a password or e-mail changed, what a failure costs, and a test.
821
871
 
822
872
  ### Permissions — `@nxgt/janus/permissions`
@@ -1102,6 +1152,43 @@ in again". Two *different* codes typed on one challenge at once are another
1102
1152
  matter: the later one can still be removed from the user before it finds the
1103
1153
  challenge spent, so two codes go for the one session the challenge opens.
1104
1154
 
1155
+ **A sign-in given no `device` tracks nothing.** Absent, `newDevice` is
1156
+ `false`, `deviceToken` is `null` and no event is sent — not "a new device".
1157
+ Pass `{ device: null }` for a client that holds no token yet, or it is never
1158
+ given one.
1159
+
1160
+ **`undefined` is not `null`.** A cookie read as `string | undefined` and
1161
+ passed as `device` is untracked whenever the cookie is missing — a compile
1162
+ error under `exactOptionalPropertyTypes`, silent without it. Write
1163
+ `{ device: cookie ?? null }`.
1164
+
1165
+ **Give the device again to `secondFactor.confirm` and `recover`.** The
1166
+ challenge `signIn`, `signInCode.confirm` or `magicLink.confirm` answers
1167
+ carries no device, so a confirmation called without `{ device }` is
1168
+ untracked, whatever the sign-in was given:
1169
+ `auth.secondFactor.confirm(challenge, code, { device: cookie ?? null })`.
1170
+
1171
+ **Every `janus()` that signs users in needs the same `devices`.** An instance
1172
+ without `devices` throws a `TypeError` for any `device`, and one whose keys
1173
+ differ reports every other instance's devices as new.
1174
+
1175
+ **One device cookie holds one user's token.** Two people sharing a browser
1176
+ who take turns signing in each present the other's token, which proves
1177
+ nothing for them: each gets a new token, and a notice, at every turn.
1178
+
1179
+ **Removing a device key forgets every device it signed.** Each is reported
1180
+ new once, at its next sign-in. It is the only way to forget devices — there
1181
+ is no per-device forget — so rotate by putting a new key first and keeping
1182
+ the old one.
1183
+
1184
+ **A device token is not a credential.** It proves only that this user signed
1185
+ in on this device before. Never authenticate a request with it, and never
1186
+ let it skip a password or a second factor.
1187
+
1188
+ **A `SignedIn` built by hand needs `newDevice` and `deviceToken`** (since
1189
+ 0.17): a test double that answers `{ status, user, session, token }` no
1190
+ longer compiles — add `newDevice: false, deviceToken: null`.
1191
+
1105
1192
  **On a user type, only `setOf` makes a set.** A user passed as it is — or
1106
1193
  `{ type: 'staff', id, relation: 'managers' }` written out — is that one user,
1107
1194
  even with a field named `relation`. The compiler refuses the written-out set
@@ -1234,12 +1321,13 @@ that sends one, since it is awaited: queue the event and return.
1234
1321
  ## Documentation
1235
1322
 
1236
1323
  - [Guides](docs/README.md) — one page per area, every option with an example
1324
+ - [Devices](docs/guide/devices.md) — a sign-in from a new device: the token, the cookie, the notice
1237
1325
  - [Troubleshooting](docs/troubleshooting.md) — by the error message you see
1238
1326
  - [Roadmap](docs/roadmap.md) — what is next, and what is not planned
1239
1327
 
1240
1328
  ## Type safety, counted
1241
1329
 
1242
- **One hundred and forty-one plausible mistakes, one hundred and forty-one refused at compile time — and
1330
+ **One hundred and forty-seven plausible mistakes, one hundred and forty-seven refused at compile time — and
1243
1331
  two gaps, named.**
1244
1332
 
1245
1333
  The lists are typechecked and never run, with one `@ts-expect-error` per
@@ -1247,10 +1335,10 @@ mistake beside the shapes that must keep compiling. One is a single file:
1247
1335
  `test/types/refusals.ts` (fifteen, on the shared vocabulary). The other three
1248
1336
  are folders with one file per behaviour: `test/types/port/` (twenty-four, on
1249
1337
  the identity stores' port, from the point of view of the person implementing
1250
- it), `test/types/auth/` (fifty-four, on `janus()`, from the point of view of
1338
+ it), `test/types/auth/` (sixty, on `janus()`, from the point of view of
1251
1339
  the application — twelve of them on the second factor, three on sign-in codes,
1252
1340
  five on user events, four on step-ups, six on the sign-in throttle, four on
1253
- sign-in links) and `test/types/permissions/` (forty-eight, on the
1341
+ sign-in links, six on devices) and `test/types/permissions/` (forty-eight, on the
1254
1342
  permission model and the questions asked of it). The rule comes from
1255
1343
  `nxgt-data`, and so does the reason to distrust the claim without the files:
1256
1344
  when it was last measured on `@nxgt/mongo`, *seven of twelve plausible
@@ -9,7 +9,7 @@
9
9
  * Gathered here from the files beside this one.
10
10
  */
11
11
  export type { PasswordHasher } from './hasher';
12
- export type { CookieConfig, JanusConfig, MultiTypeConfig, SecondFactorConfig, SignInConfig, SignInThrottleConfig, SingleTypeConfig, } from './janus-config';
12
+ export type { CookieConfig, DevicesConfig, JanusConfig, MultiTypeConfig, SecondFactorConfig, SignInConfig, SignInThrottleConfig, SingleTypeConfig, } from './janus-config';
13
13
  export { type Normalize, normalizeEmail } from './normalize';
14
14
  export type { PasswordConfig } from './password-config';
15
15
  export { RESERVED_FIELDS, RESERVED_TYPES, SINGLE_TYPE } from './reserved';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/config/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EACX,YAAY,EACZ,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,KAAK,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,YAAY,EACX,UAAU,EACV,aAAa,EACb,UAAU,EACV,cAAc,GACd,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/config/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EACX,YAAY,EACZ,aAAa,EACb,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,KAAK,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtE,YAAY,EACX,UAAU,EACV,aAAa,EACb,UAAU,EACV,cAAc,GACd,MAAM,aAAa,CAAC"}
@@ -65,11 +65,18 @@ interface SharedConfig {
65
65
  * `secondFactor` flows exist and `signIn` answers a session directly.
66
66
  */
67
67
  readonly secondFactor?: SecondFactorConfig;
68
+ /**
69
+ * Device tokens, for a notice when a user signs in from a device they had
70
+ * not signed in from. Absent, no sign-in may be given a `device`, and every
71
+ * answer's `newDevice` is `false`.
72
+ */
73
+ readonly devices?: DevicesConfig;
68
74
  /**
69
75
  * Called with every user event — `user.created`, `user.emailVerified`,
70
- * `user.passwordReset`, `user.secondFactorEnabled`,
71
- * `user.secondFactorDisabled`, `user.recoveryCodesRegenerated`,
72
- * `user.recoveryCodeUsed`, `user.deleted` — once the write landed,
76
+ * `user.passwordReset`, `user.passwordChanged`, `user.emailChanged`,
77
+ * `user.secondFactorEnabled`, `user.secondFactorDisabled`,
78
+ * `user.recoveryCodesRegenerated`, `user.recoveryCodeUsed`,
79
+ * `user.newDeviceSignedIn`, `user.deleted` — once the write landed,
73
80
  * and awaited before the flow answers. Any function will do;
74
81
  * `webhooks({ … })` from `@nxgt/janus-webhooks` signs and delivers them.
75
82
  */
@@ -104,6 +111,20 @@ export interface SecondFactorConfig {
104
111
  /** How long `signIn`'s challenge waits for a code. `'5m'` when absent. */
105
112
  readonly challenge?: Duration;
106
113
  }
114
+ /**
115
+ * The keys device tokens are signed with. **Nothing is stored**: a device
116
+ * token is the device's id and a keyed hash binding it to the user, kept by
117
+ * the application in a long-lived cookie.
118
+ */
119
+ export interface DevicesConfig {
120
+ /**
121
+ * **The first signs, every one checks**: a token signed with an older key
122
+ * is known, and answered signed again with the first. A token whose key
123
+ * is gone is a new device — so **removing a key is the only way to forget
124
+ * devices**, and it forgets every device it signed.
125
+ */
126
+ readonly keys: readonly [SealingKey, ...SealingKey[]];
127
+ }
107
128
  /** An application with one user type: `user` is its schema. */
108
129
  export interface SingleTypeConfig extends SharedConfig, Omit<UserTypeConfig, 'schema'> {
109
130
  readonly user: UserSchema;
@@ -1 +1 @@
1
- {"version":3,"file":"janus-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/janus-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE9D,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;QAC/B,oEAAoE;QACpE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC;QAC9B,kFAAkF;QAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;KAC3B,CAAC;IACF;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAC3C;;;;;;;OAOG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;CACpC;AAED,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,oBAAoB,GAAG,KAAK,CAAC;CACjD;AAED,8DAA8D;AAC9D,MAAM,WAAW,oBAAoB;IACpC,0FAA0F;IAC1F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAED,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IAClC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,UAAU,EAAE,GAAG,UAAU,EAAE,CAAC,CAAC;IACtD,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC;CAC9B;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAChB,SAAQ,YAAY,EACnB,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACvB;AAED,mEAAmE;AACnE,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACpD,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,CAAA;KAAE,CAAC;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC;IACzB,QAAQ,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC;CAC/B;AAED,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,eAAe,CAAC"}
1
+ {"version":3,"file":"janus-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/janus-config.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/resolve-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACX,WAAW,EAGX,MAAM,gBAAgB,CAAC;AAOxB,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,mBAAmB,CAAC;AAGtE,qFAAqF;AACrF,wBAAgB,aAAa,CAC5B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,MAAM,GACX,cAAc,CAwBhB"}
1
+ {"version":3,"file":"resolve-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/resolve-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACX,WAAW,EAGX,MAAM,gBAAgB,CAAC;AAQxB,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,mBAAmB,CAAC;AAGtE,qFAAqF;AACrF,wBAAgB,aAAa,CAC5B,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,MAAM,GACX,cAAc,CAyBhB"}
@@ -0,0 +1,5 @@
1
+ /** Resolves the device tokens' keys: the sealer that signs and checks them. */
2
+ import type { DevicesConfig } from './janus-config';
3
+ import type { ResolvedConfig } from './resolved-config';
4
+ export declare function resolveDevices(config: DevicesConfig | undefined, where: string): ResolvedConfig['devices'];
5
+ //# sourceMappingURL=resolve-devices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-devices.d.ts","sourceRoot":"","sources":["../../../src/auth/config/resolve-devices.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAG/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,wBAAgB,cAAc,CAC7B,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,KAAK,EAAE,MAAM,GACX,cAAc,CAAC,SAAS,CAAC,CAI3B"}
@@ -38,6 +38,10 @@ export interface ResolvedConfig {
38
38
  readonly sealer: Sealer;
39
39
  readonly challengeTtlMs: number;
40
40
  } | null;
41
+ /** `null` without `devices`: no sign-in may be given a device. */
42
+ readonly devices: {
43
+ readonly sealer: Sealer;
44
+ } | null;
41
45
  readonly cookie: {
42
46
  readonly name: string;
43
47
  readonly domain: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"resolved-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/resolved-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;QAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC3B,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE;QACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,gDAAgD;IAChD,QAAQ,CAAC,cAAc,EAAE;QACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;KAC1B,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,YAAY,EAAE;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;KAChC,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,MAAM,EAAE;QAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QAC7C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;KACzB,CAAC;CACF"}
1
+ {"version":3,"file":"resolved-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/resolved-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;QAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC3B,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE;QACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,gDAAgD;IAChD,QAAQ,CAAC,cAAc,EAAE;QACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;KAC1B,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,YAAY,EAAE;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;KAChC,GAAG,IAAI,CAAC;IACT,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACrD,QAAQ,CAAC,MAAM,EAAE;QAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QAC7C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;KACzB,CAAC;CACF"}
@@ -0,0 +1,30 @@
1
+ import type { Context } from '../context';
2
+ import type { SignInOptions } from '../types';
3
+ /**
4
+ * What a sign-in was told of the device: `undefined` when nothing — devices
5
+ * are not tracked for that call — `null` for a device holding no token yet,
6
+ * or the token it presented.
7
+ */
8
+ export type DeviceHint = string | null | undefined;
9
+ /** What a session-opening answer says of the device. */
10
+ export interface DeviceOutcome {
11
+ readonly newDevice: boolean;
12
+ readonly deviceToken: string | null;
13
+ }
14
+ /** No device given: nothing tracked, nothing minted. */
15
+ export declare const UNTRACKED: DeviceOutcome;
16
+ /**
17
+ * The device a sign-in's options name. A JavaScript caller's options that
18
+ * are not an object (the token itself), a `device` of another type, and a
19
+ * device given to a `janus()` wired without `devices`, are wiring
20
+ * mistakes — bare `TypeError`s. The value itself, whatever it holds, is the
21
+ * client's, and never refused.
22
+ */
23
+ export declare function deviceHint(context: Context, options: SignInOptions | undefined, where: string): DeviceHint;
24
+ /**
25
+ * Whether a session opening for `userId` comes from a new device, and the
26
+ * token the client keeps from now on. A sign-up mints a token and is never
27
+ * new: the account has no device to be new against.
28
+ */
29
+ export declare function deviceOutcome(context: Context, userId: string, hint: DeviceHint, signUp: boolean): DeviceOutcome;
30
+ //# sourceMappingURL=hint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hint.d.ts","sourceRoot":"","sources":["../../../src/auth/devices/hint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;AAEnD,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,wDAAwD;AACxD,eAAO,MAAM,SAAS,EAAE,aAGtB,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,UAAU,CACzB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,aAAa,GAAG,SAAS,EAClC,KAAK,EAAE,MAAM,GACX,UAAU,CAsBZ;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC5B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,OAAO,GACb,aAAa,CAWf"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Devices: the signed token a client keeps (`token.ts`), what a sign-in was
3
+ * told of the device and what it answers (`hint.ts`), and the event a new
4
+ * one sends (`report.ts`).
5
+ *
6
+ * Gathered here from the files beside this one.
7
+ */
8
+ export { type DeviceHint, type DeviceOutcome, deviceHint, deviceOutcome, UNTRACKED, } from './hint';
9
+ export { reportNewDevice } from './report';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/devices/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,UAAU,EACV,aAAa,EACb,SAAS,GACT,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { AnyUser, Context } from '../context';
2
+ import type { SignInResult } from '../types';
3
+ /**
4
+ * Sends `user.newDeviceSignedIn` for a session a new device just opened —
5
+ * once the sign-in is complete, so a sign-in refused after its session
6
+ * opened (a password written meanwhile) reports nothing. A challenge, and a
7
+ * session from a known device or with no device given, report nothing
8
+ * either.
9
+ */
10
+ export declare function reportNewDevice(context: Context, result: SignInResult<AnyUser>): Promise<void>;
11
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/auth/devices/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C;;;;;;GAMG;AACH,wBAAsB,eAAe,CACpC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,GAC3B,OAAO,CAAC,IAAI,CAAC,CASf"}
@@ -0,0 +1,13 @@
1
+ import type { Sealer } from '../sealing';
2
+ /** A token for a device never seen: a fresh device id, signed with the first key. */
3
+ export declare function mintDeviceToken(sealer: Sealer, userId: string): string;
4
+ /**
5
+ * The token to hand back when `presented` proves this user signed in on this
6
+ * device before — itself, or the same device signed again with the first key
7
+ * when an older key signed it — or `null` when it proves nothing: malformed,
8
+ * another user's, forged, or signed with a key no longer held.
9
+ *
10
+ * `presented` comes from a request, so nothing here throws.
11
+ */
12
+ export declare function knownDeviceToken(sealer: Sealer, userId: string, presented: string): string | null;
13
+ //# sourceMappingURL=token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../../src/auth/devices/token.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AA8BzC,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAgBf"}
@@ -7,10 +7,11 @@
7
7
  */
8
8
  import type { ResolvedType } from './config';
9
9
  import { type AnyUser, type Context } from './context';
10
+ import { type DeviceHint } from './devices';
10
11
  import type { TokenKind, TokenRecord, UserRecord } from './port/types';
11
12
  import type { SignInResult } from './types';
12
13
  /** What a sign-in answers once the user proved who they are: `secondFactor.finish`. */
13
- export type Finish = (record: UserRecord, where: string) => Promise<SignInResult<AnyUser>>;
14
+ export type Finish = (record: UserRecord, where: string, device: DeviceHint) => Promise<SignInResult<AnyUser>>;
14
15
  /**
15
16
  * The user of this type holding `email`, when they may be sent a sign-in:
16
17
  * `null` for nobody, and for an inactive user — the same answer, so the
@@ -26,7 +27,7 @@ export declare function keepOnlyLatest(context: Context, userId: string, kind: T
26
27
  /**
27
28
  * Ends a sign-in by e-mail whose token is spent: refuses an e-mail changed
28
29
  * since it was sent and an inactive user, proves the e-mail — it reached the
29
- * inbox — and finishes as a password would.
30
+ * inbox — and finishes as a password would, reporting a new device.
30
31
  *
31
32
  * **An e-mail proved for the first time drops the password and the second
32
33
  * factor, and revokes every session** before the new one opens
@@ -41,5 +42,6 @@ export declare function finishEmailSignIn(context: Context, type: ResolvedType,
41
42
  readonly token: TokenRecord;
42
43
  readonly where: string;
43
44
  readonly noun: 'token' | 'code';
45
+ readonly device: DeviceHint;
44
46
  }): Promise<SignInResult<AnyUser>>;
45
47
  //# sourceMappingURL=email-sign-in.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"email-sign-in.d.ts","sourceRoot":"","sources":["../../src/auth/email-sign-in.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAiB,MAAM,WAAW,CAAC;AAGtE,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,uFAAuF;AACvF,MAAM,MAAM,MAAM,GAAG,CACpB,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,MAAM,KACT,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;AAEpC;;;;GAIG;AACH,wBAAsB,YAAY,CACjC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAG5B;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CACnC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE;IACP,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;CAChC,GACC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAYhC"}
1
+ {"version":3,"file":"email-sign-in.d.ts","sourceRoot":"","sources":["../../src/auth/email-sign-in.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAiB,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,KAAK,UAAU,EAAmB,MAAM,WAAW,CAAC;AAG7D,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,uFAAuF;AACvF,MAAM,MAAM,MAAM,GAAG,CACpB,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,UAAU,KACd,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;AAEpC;;;;GAIG;AACH,wBAAsB,YAAY,CACjC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAG5B;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CACnC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE;IACP,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC5B,GACC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAiBhC"}
@@ -28,14 +28,21 @@ import type { Context } from './context';
28
28
  * come with `user.secondFactorEnabled`;
29
29
  * - `user.recoveryCodeUsed` — by `secondFactor.recover`, once the code is
30
30
  * spent: a sign-in without the user's phone;
31
+ * - `user.newDeviceSignedIn` — by a sign-in that opened a session from a
32
+ * device the user had not signed in from: `signIn`, `secondFactor.confirm`,
33
+ * `secondFactor.recover`, `signInCode.confirm` and `magicLink.confirm`, when
34
+ * given a `device` its token did not prove. Never by `signUp`, nor without
35
+ * a `device`. Carries `sessionId`;
31
36
  * - `user.deleted` — by `delete`, once; a replay that finds nobody is none.
32
37
  */
33
- export type UserEventType = 'user.created' | 'user.emailVerified' | 'user.passwordReset' | 'user.passwordChanged' | 'user.emailChanged' | 'user.secondFactorEnabled' | 'user.secondFactorDisabled' | 'user.recoveryCodesRegenerated' | 'user.recoveryCodeUsed' | 'user.deleted';
38
+ export type UserEventType = 'user.created' | 'user.emailVerified' | 'user.passwordReset' | 'user.passwordChanged' | 'user.emailChanged' | 'user.secondFactorEnabled' | 'user.secondFactorDisabled' | 'user.recoveryCodesRegenerated' | 'user.recoveryCodeUsed' | 'user.newDeviceSignedIn' | 'user.deleted';
34
39
  /**
35
40
  * A user event: **the user named by id, and nothing else** — no login, no
36
41
  * field, no password, no token. Whoever receives it reads the rest from where
37
- * it is kept, if they may. One exception, `formerEmail` on
38
- * `user.emailChanged`: once the write landed, the old address is kept nowhere.
42
+ * it is kept, if they may. Two exceptions: `formerEmail` on
43
+ * `user.emailChanged` — once the write landed, the old address is kept
44
+ * nowhere — and `sessionId` on `user.newDeviceSignedIn`, the session the new
45
+ * device holds.
39
46
  */
40
47
  export interface UserEvent {
41
48
  /** A UUIDv7 minted for this event: the key to deliver it once. */
@@ -55,9 +62,15 @@ export interface UserEvent {
55
62
  * from every other type, and from an event a webhook delivered.
56
63
  */
57
64
  readonly formerEmail?: string | null;
65
+ /**
66
+ * On `user.newDeviceSignedIn` only: the session the new device opened —
67
+ * to sign it out from a "this wasn't me" page. Absent from every other
68
+ * type, and from an event a webhook delivered.
69
+ */
70
+ readonly sessionId?: string;
58
71
  }
59
- /** What an event carries beyond the user, on the one type that does. */
60
- export type EventExtras = Pick<UserEvent, 'formerEmail'>;
72
+ /** What an event carries beyond the user, on the two types that do. */
73
+ export type EventExtras = Pick<UserEvent, 'formerEmail' | 'sessionId'>;
61
74
  /**
62
75
  * What `janus({ events })` takes: called once per event, **after** the write
63
76
  * landed and before the flow answers — awaited, so a queue that stores the
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;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,cAAc,CAAC;AAElB;;;;;GAKG;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;CACrC;AAED,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAEzD;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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,4 +1,4 @@
1
- export type { CookieConfig, FieldsJson, JanusConfig, MultiTypeConfig, Normalize, PasswordConfig, PasswordHasher, SecondFactorConfig, SessionConfig, SignInConfig, SignInThrottleConfig, SingleTypeConfig, UserSchema, UserTypeConfig, } from './config';
1
+ export type { CookieConfig, DevicesConfig, FieldsJson, JanusConfig, MultiTypeConfig, Normalize, PasswordConfig, PasswordHasher, SecondFactorConfig, SessionConfig, SignInConfig, SignInThrottleConfig, SingleTypeConfig, UserSchema, UserTypeConfig, } from './config';
2
2
  export type { UserEvent, UserEventListener, UserEventType, } from './events';
3
3
  export { bunHasher, scryptHasher } from './hashers';
4
4
  export { janus } from './janus';
@@ -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, HeaderRecord, IssuedCode, IssuedToken, Janus, LoginOf, MagicLinkApi, PasswordApi, RecoveredSignIn, RecoveryCodesIssued, RequestLike, RequiredStringKeys, ResetPasswordApi, SecondFactorApi, SecondFactorEnrolment, SecondFactorRequired, Session, SharedApi, SignedIn, SignInCodeApi, SignInResult, StepUpApi, StepUpByApp, StepUpByEmail, TypeApi, TypesOf, User, UserBase, UserOf, UserRef, UserTypeApi, VerifyEmailApi, WriteOptions, } from './types';
11
+ export type { Authenticated, Checked, EmailOf, HeaderRecord, IssuedCode, IssuedToken, Janus, LoginOf, MagicLinkApi, PasswordApi, 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,UAAU,EACV,WAAW,EACX,eAAe,EACf,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,cAAc,GACd,MAAM,UAAU,CAAC;AAClB,YAAY,EACX,SAAS,EACT,iBAAiB,EACjB,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACX,WAAW,EACX,IAAI,EACJ,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,SAAS,EACT,UAAU,EACV,SAAS,GACT,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,YAAY,EACZ,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,QAAQ,EACR,aAAa,EACb,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,eAAe,EACf,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,cAAc,GACd,MAAM,UAAU,CAAC;AAClB,YAAY,EACX,SAAS,EACT,iBAAiB,EACjB,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACX,WAAW,EACX,IAAI,EACJ,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,SAAS,EACT,UAAU,EACV,SAAS,GACT,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,YAAY,EACZ,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,QAAQ,EACR,aAAa,EACb,aAAa,EACb,YAAY,EACZ,SAAS,EACT,WAAW,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,OAAO,EACP,WAAW,EACX,cAAc,EACd,YAAY,GACZ,MAAM,SAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"magic-link.d.ts","sourceRoot":"","sources":["../../src/auth/magic-link.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,WAAW,CAAC;AAE/D,OAAO,EACN,KAAK,MAAM,EAIX,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAe,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,MAAM,GACZ,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAgB3D"}
1
+ {"version":3,"file":"magic-link.d.ts","sourceRoot":"","sources":["../../src/auth/magic-link.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,WAAW,CAAC;AAG/D,OAAO,EACN,KAAK,MAAM,EAIX,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAe,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,MAAM,GACZ,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAY3D"}
@@ -2,7 +2,7 @@ import type { At } from '../at';
2
2
  import type { ResolvedType } from '../config';
3
3
  import type { AnyUser, Context } from '../context';
4
4
  import type { UserRecord } from '../port/types';
5
- import type { SecondFactorRequired, SignedIn } from '../types';
5
+ import type { SecondFactorRequired, SignedIn, SignInOptions } from '../types';
6
6
  /**
7
7
  * The challenge `signIn` answers instead of a session, and the code that
8
8
  * redeems it. **Only the challenge's hash is stored**, like a session
@@ -10,6 +10,6 @@ import type { SecondFactorRequired, SignedIn } from '../types';
10
10
  */
11
11
  export declare function challengeFlows(context: Context, type: ResolvedType, at: At): {
12
12
  issue(record: UserRecord, where: string): Promise<SecondFactorRequired>;
13
- confirm(challenge: string, code: string): Promise<SignedIn<AnyUser>>;
13
+ confirm(challenge: string, code: string, options?: SignInOptions): Promise<SignedIn<AnyUser>>;
14
14
  };
15
15
  //# sourceMappingURL=challenge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"challenge.d.ts","sourceRoot":"","sources":["../../../src/auth/second-factor/challenge.ts"],"names":[],"mappings":"AAAA,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;AAEnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAI/D;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE;kBAGhE,UAAU,SACX,MAAM,GACX,OAAO,CAAC,oBAAoB,CAAC;uBAqBP,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;EAI3E"}
1
+ {"version":3,"file":"challenge.d.ts","sourceRoot":"","sources":["../../../src/auth/second-factor/challenge.ts"],"names":[],"mappings":"AAAA,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;AAGnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAI9E;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE;kBAGhE,UAAU,SACX,MAAM,GACX,OAAO,CAAC,oBAAoB,CAAC;uBAsBpB,MAAM,QACX,MAAM,YACF,aAAa,GACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;EAI9B"}
@@ -1,6 +1,7 @@
1
1
  import type { At } from '../at';
2
2
  import type { ResolvedType } from '../config';
3
3
  import type { AnyUser, Context } from '../context';
4
+ import type { DeviceHint } from '../devices';
4
5
  import type { UserRecord } from '../port/types';
5
6
  import type { SignInResult } from '../types';
6
7
  /**
@@ -13,15 +14,16 @@ export declare function secondFactorFlows(context: Context, type: ResolvedType,
13
14
  activate(user: import("..").UserRef, code: string, options?: import("..").WriteOptions): Promise<import("..").RecoveryCodesIssued<AnyUser>>;
14
15
  regenerateRecoveryCodes(user: import("..").UserRef, code: string, options?: import("..").WriteOptions): Promise<import("..").RecoveryCodesIssued<AnyUser>>;
15
16
  disable(user: import("..").UserRef, options?: import("..").WriteOptions): Promise<AnyUser>;
16
- confirm(challenge: string, code: string): Promise<import("..").SignedIn<AnyUser>>;
17
- recover(challenge: string, code: string): Promise<import("..").RecoveredSignIn<AnyUser>>;
17
+ confirm(challenge: string, code: string, options?: import("..").SignInOptions): Promise<import("..").SignedIn<AnyUser>>;
18
+ recover(challenge: string, code: string, options?: import("..").SignInOptions): Promise<import("..").RecoveredSignIn<AnyUser>>;
18
19
  recoveryCodesLeft(user: import("..").UserRef): Promise<number | null>;
19
20
  };
20
21
  /**
21
22
  * What a sign-in answers once the user proved who they are — by
22
23
  * password or by an e-mail code: a session, or a challenge when their
23
- * second factor is active. The first factor alone opens nothing then.
24
+ * second factor is active. The first factor alone opens nothing then,
25
+ * and the device is not carried: its confirmation is given it again.
24
26
  */
25
- finish: (record: UserRecord, where: string) => Promise<SignInResult<AnyUser>>;
27
+ finish: (record: UserRecord, where: string, device: DeviceHint) => Promise<SignInResult<AnyUser>>;
26
28
  };
27
29
  //# sourceMappingURL=flows.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../../../src/auth/second-factor/flows.ts"],"names":[],"mappings":"AAAA,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,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,UAAU,CAAC;AAO9D;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE;;;;;;;;;;IAmBL;;;;OAIG;qBAEM,UAAU,SACX,MAAM,KACX,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;EAKlC"}
1
+ {"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../../../src/auth/second-factor/flows.ts"],"names":[],"mappings":"AAAA,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,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,UAAU,CAAC;AAO9D;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE;;;;;;;;;;IAmBL;;;;;OAKG;qBAEM,UAAU,SACX,MAAM,UACL,UAAU,KAChB,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;EAKlC"}
@@ -1,7 +1,7 @@
1
1
  import type { At } from '../at';
2
2
  import type { ResolvedType } from '../config';
3
3
  import type { AnyUser, Context } from '../context';
4
- import type { RecoveredSignIn } from '../types';
4
+ import type { RecoveredSignIn, SignInOptions } from '../types';
5
5
  /**
6
6
  * Redeems `signIn`'s challenge with a recovery code instead of an app's
7
7
  * code: for a user whose phone is gone.
@@ -12,5 +12,9 @@ import type { RecoveredSignIn } from '../types';
12
12
  * two sign-ins spending the same code at once, the second write is
13
13
  * `VERSION_CONFLICT`, and that sign-in opens nothing.
14
14
  */
15
- export declare function recoverWithCode(context: Context, type: ResolvedType, challenge: string, code: string, at: At): Promise<RecoveredSignIn<AnyUser>>;
15
+ export declare function recoverWithCode(context: Context, type: ResolvedType, given: {
16
+ readonly challenge: string;
17
+ readonly code: string;
18
+ readonly options: SignInOptions | undefined;
19
+ }, at: At): Promise<RecoveredSignIn<AnyUser>>;
16
20
  //# sourceMappingURL=recovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../../src/auth/second-factor/recovery.ts"],"names":[],"mappings":"AAAA,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;AAInD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAIhD;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACpC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,EAAE,GACJ,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAoCnC"}
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../../src/auth/second-factor/recovery.ts"],"names":[],"mappings":"AAAA,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;AAKnD,OAAO,KAAK,EAAE,eAAe,EAAY,aAAa,EAAE,MAAM,UAAU,CAAC;AAIzE;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACpC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE;IACN,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAAC;CAC5C,EACD,EAAE,EAAE,EAAE,GACJ,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAwCnC"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import type { ResolvedType } from '../config';
6
6
  import { type AnyUser, type Context } from '../context';
7
+ import { type DeviceHint } from '../devices';
7
8
  import type { SessionRecord, UserRecord } from '../port/types';
8
9
  import type { Session, SignedIn } from '../types';
9
10
  /** A session as application code sees it: everything but the token's hash. */
@@ -11,6 +12,11 @@ export declare function toSession(record: SessionRecord): Session;
11
12
  /**
12
13
  * Opens a session for a user who just proved who they are. The token is in
13
14
  * the answer and nowhere else: the store holds its hash.
15
+ *
16
+ * `device` is what the sign-in was told of the device, and the answer says
17
+ * whether it is new to the user; `signUp` mints a first token and is never
18
+ * new. Reporting a new device is the caller's, once its sign-in is complete
19
+ * (`reportNewDevice`).
14
20
  */
15
- export declare function openSession(context: Context, type: ResolvedType, user: UserRecord): Promise<SignedIn<AnyUser>>;
21
+ export declare function openSession(context: Context, type: ResolvedType, user: UserRecord, device: DeviceHint, signUp?: boolean): Promise<SignedIn<AnyUser>>;
16
22
  //# sourceMappingURL=open-session.d.ts.map