@nxgt/janus 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -321,6 +321,7 @@ await auth.authenticate(request); // { user, session, token, re
321
321
  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
+ await auth.verifyEmail.proveFirst(user); // privileged: FirstProof — { user, proved }; drops a squatter's password and factor
324
325
  await auth.resetPassword.request(email); // … | null
325
326
  await auth.resetPassword.confirm(token, newPassword);
326
327
  await auth.signInCode.request(email); // { code, challenge, email, expiresAt, user } | null
@@ -783,6 +784,7 @@ has a route for each flow, with Hono.
783
784
  const invitation = await invitations.redeem(body.invitationToken); // yours: one use, expiring, hashed at rest
784
785
  if (invitation === null) return new Response(null, { status: 404 });
785
786
 
787
+ // The invitation reached the inbox: call verifyEmail.proveFirst before this — see below.
786
788
  const result = await auth.signInProven(invitation.userId, { device: deviceCookie ?? null });
787
789
  if (result.status === 'secondFactor') return askForCode(result.challenge); // switch on status, as after signIn
788
790
  // result: { status: 'signedIn', user, session, token, newDevice, deviceToken }
@@ -810,7 +812,8 @@ with no password, code or link. Every user type has it.
810
812
  `magicLink.request` on the invitee's address cannot make it answer 429 —
811
813
  and links or codes already in the person's inbox still work. It proves
812
814
  no e-mail either: `emailVerified` stays as it was, and nothing is dropped
813
- as on a first proof by link.
815
+ as on a first proof by link — call `verifyEmail.proveFirst` before it
816
+ when your proof reached the inbox, as an invitation does.
814
817
 
815
818
  [Sessions](docs/guide/sessions.md#signing-in-on-your-own-proof--signinproven)
816
819
  has the invitation route in full, and what to verify before the call.
@@ -825,6 +828,7 @@ if (invitation === null) return new Response(null, { status: 404 });
825
828
  const member =
826
829
  (await auth.member.findByEmail(invitation.email)) ?? // User | null — counts nothing, sends nothing
827
830
  (await auth.member.create({ email: invitation.email, name: '' }));
831
+ await auth.member.verifyEmail.proveFirst(member); // the invitation reached the inbox: before signInProven
828
832
  const result = await auth.member.signInProven(member, { device: deviceCookie ?? null });
829
833
  ```
830
834
 
@@ -847,6 +851,43 @@ password and reset flows on it.
847
851
  `STORE_FAILED`, never `null`.
848
852
  - **Server-side only**: it tells whether an account exists. See *Traps*.
849
853
 
854
+ ### Proving an e-mail on your own proof — `verifyEmail.proveFirst`
855
+
856
+ ```ts
857
+ // Accepting an invitation: the token YOUR server mailed to the address proves the inbox.
858
+ const user = (await auth.findByEmail(invitation.email)) ?? (await auth.create({ email: invitation.email, name }));
859
+ const { proved } = await auth.verifyEmail.proveFirst(user); // 1. first: drops a squatter's password, factor, sessions
860
+ await auth.setPassword(user, body.password); // 2. then the invitee's own password, if the type has one
861
+ const result = await auth.signInProven(user); // 3. last: proveFirst revokes every session
862
+ ```
863
+
864
+ Answers `FirstProof<User>` — `{ user, proved }`, exported. Applies **the
865
+ first proof of an e-mail** that `magicLink.confirm` and
866
+ `signInCode.confirm` apply, when the proof is the application's — an
867
+ invitation token it mailed to the address and the person followed. On every
868
+ type with an e-mail; `signInProven` alone proves nothing.
869
+
870
+ - **On an e-mail never proved, in one write**: marks it verified and drops
871
+ the password and the second factor — active or waiting, with its recovery
872
+ codes. It revokes every session and spends the reset links and
873
+ second-factor challenges, before the write and after it, and sends
874
+ `user.emailVerified`, then `user.passwordChanged` when a password was
875
+ dropped and `user.secondFactorDisabled` when an active factor was.
876
+ Answers `{ user, proved: true }`.
877
+ - **On an e-mail already proved, nothing**: no write, no event, no session
878
+ revoked — `{ user, proved: false }`.
879
+ - **Sends nothing, counts nothing**: no e-mail, no token, no mail-throttle
880
+ or sign-in-throttle count.
881
+ - **Refused as `signInProven` refuses**: an inactive user is
882
+ `USER_INACTIVE`; a malformed id, an unknown one or one of another type is
883
+ `NOT_FOUND`. A user written between its read and its write is
884
+ `VERSION_CONFLICT`, with nothing proved; an outage is `STORE_FAILED`.
885
+ - **Privileged, and the order matters**: see *Traps*.
886
+
887
+ [E-mail flows](docs/guide/email-flows.md#proving-an-e-mail-on-your-own-proof--provefirst)
888
+ has the details; [sessions](docs/guide/sessions.md#accepting-an-invitation)
889
+ has the invitation route in full.
890
+
850
891
  ### Step-up — `stepUp` and `assertFresh`
851
892
 
852
893
  ```ts
@@ -959,12 +1000,12 @@ await auth.signUp({ email, password }); // the listener has the event before thi
959
1000
  | `type` | Sent by |
960
1001
  | --- | --- |
961
1002
  | `user.created` | `create`, `signUp` |
962
- | `user.emailVerified` | `verifyEmail.confirm`; `resetPassword.confirm`, `magicLink.confirm` and `signInCode.confirm`, whose link or code proves the e-mail too — never for an e-mail already verified |
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 |
963
1004
  | `user.passwordReset` | `resetPassword.confirm` |
964
- | `user.passwordChanged` | `changePassword`, `setPassword`; `signInCode.confirm` and `magicLink.confirm` when their first proof of the e-mail dropped a password — never a reset, which is `user.passwordReset` alone |
1005
+ | `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 |
965
1006
  | `user.emailChanged` | `update`, when it changed the e-mail — carrying `formerEmail`, the address before (`null` for none) |
966
1007
  | `user.secondFactorEnabled` | `secondFactor.activate`, once the factor is active — not `enroll`, which leaves it waiting |
967
- | `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 |
1008
+ | `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 |
968
1009
  | `user.recoveryCodesRegenerated` | `secondFactor.regenerateRecoveryCodes` — not `activate`, whose codes come with `user.secondFactorEnabled` |
969
1010
  | `user.recoveryCodeUsed` | `secondFactor.recover`, once the recovery code is spent: a sign-in without the user's phone |
970
1011
  | `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 |
@@ -1259,6 +1300,21 @@ id from that proof's record. For an invitation, it replaces
1259
1300
  five anonymous requests for the invitee's address would make the
1260
1301
  acceptance answer 429.
1261
1302
 
1303
+ **Call `verifyEmail.proveFirst` first: it drops the password and revokes
1304
+ every session.** On an e-mail never proved, it does what a first sign-in by
1305
+ link does, so a password set before it is erased — the invitee's own,
1306
+ chosen on your acceptance form, included — and a session opened before it
1307
+ is revoked. Accept an invitation in this order: `findByEmail` (or
1308
+ `create`), then `verifyEmail.proveFirst`, then `setPassword` on a type with
1309
+ a password, then `signInProven`. Called after `setPassword`, the person
1310
+ signs in once and then finds `signIn` answering `CREDENTIALS_INVALID`
1311
+ (`reason: 'noPassword'`). **It is privileged, like `signInProven`**: it
1312
+ checks no credential, so call it only right after your server verified
1313
+ its own proof of the inbox — an invitation token it mailed to that
1314
+ address — on the user that proof names, whose e-mail is the address the
1315
+ proof reached; never with an id a request supplied, or anybody can wipe
1316
+ anybody's password and second factor and sign them out.
1317
+
1262
1318
  **`findByEmail` is an account-existence oracle — never expose it to an
1263
1319
  anonymous client.** It answers whether somebody of the type holds an
1264
1320
  address, at once, with no throttle: a route that calls it on an address a
@@ -1561,7 +1617,7 @@ that sends one, since it is awaited: queue the event and return.
1561
1617
 
1562
1618
  ## Type safety, counted
1563
1619
 
1564
- **One hundred and sixty-six plausible mistakes, one hundred and sixty-six refused at compile time — and
1620
+ **One hundred and sixty-nine plausible mistakes, one hundred and sixty-nine refused at compile time — and
1565
1621
  two gaps, named.**
1566
1622
 
1567
1623
  The lists are typechecked and never run, with one `@ts-expect-error` per
@@ -1569,10 +1625,10 @@ mistake beside the shapes that must keep compiling. One is a single file:
1569
1625
  `test/types/refusals.ts` (fifteen, on the shared vocabulary). The other three
1570
1626
  are folders with one file per behaviour: `test/types/port/` (twenty-five, on
1571
1627
  the identity stores' port, from the point of view of the person implementing
1572
- it), `test/types/auth/` (seventy-eight, on `janus()`, from the point of view of
1628
+ it), `test/types/auth/` (eighty-one, on `janus()`, from the point of view of
1573
1629
  the application — twelve of them on the second factor, three on sign-in codes,
1574
1630
  five on user events, four on step-ups, six on the sign-in throttle, five on the mail throttle, four on
1575
- 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) and `test/types/permissions/` (forty-eight, on the
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
1576
1632
  permission model and the questions asked of it). The rule comes from
1577
1633
  `nxgt-data`, and so does the reason to distrust the claim without the files:
1578
1634
  when it was last measured on `@nxgt/mongo`, *seven of twelve plausible
@@ -12,6 +12,6 @@ export { type Context, createContext } from './create-context';
12
12
  export { emailOf, holderOfEmail, loginsOf } from './logins';
13
13
  export { checkPassword, passwordMatches, passwordRule, rehashed, requireHasher, } from './password';
14
14
  export { findRecord, getRecord, writeUser } from './records';
15
- export { type AnyUser, idOf, toUser } from './user';
15
+ export { type AnyUser, idOf, refId, toUser } from './user';
16
16
  export { validateFields } from './validate-fields';
17
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/context/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,EACN,aAAa,EACb,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,aAAa,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,KAAK,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/context/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,EACN,aAAa,EACb,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,aAAa,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,KAAK,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
@@ -11,4 +11,11 @@ export type AnyUser = User<string, Record<string, unknown>>;
11
11
  export declare function toUser(record: UserRecord): AnyUser;
12
12
  /** The id a {@link UserRef} names. */
13
13
  export declare const idOf: (user: UserRef) => string;
14
+ /**
15
+ * The id a JavaScript caller's reference names, or `''` — malformed, and
16
+ * so `NOT_FOUND` without reaching the store — for anything that names none:
17
+ * what the privileged calls read, `signInProven` and
18
+ * `verifyEmail.proveFirst`, which must never mistake a bad value for a user.
19
+ */
20
+ export declare function refId(user: UserRef): string;
14
21
  //# sourceMappingURL=user.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/auth/context/user.ts"],"names":[],"mappings":"AAAA,qEAAqE;AAErE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAE9C,kEAAkE;AAClE,MAAM,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE5D;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAalD;AAED,sCAAsC;AACtC,eAAO,MAAM,IAAI,GAAI,MAAM,OAAO,KAAG,MACK,CAAC"}
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/auth/context/user.ts"],"names":[],"mappings":"AAAA,qEAAqE;AAErE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAE9C,kEAAkE;AAClE,MAAM,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE5D;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAalD;AAED,sCAAsC;AACtC,eAAO,MAAM,IAAI,GAAI,MAAM,OAAO,KAAG,MACK,CAAC;AAE3C;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAK3C"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Proving an e-mail on the application's own proof of the inbox — an
3
+ * invitation token it issued to the address — with the ending a first
4
+ * sign-in by link or code gives an e-mail never proved (`../first-proof`).
5
+ */
6
+ import type { At } from '../at';
7
+ import type { ResolvedType } from '../config';
8
+ import { type AnyUser, type Context } from '../context';
9
+ import type { VerifyEmailApi } from '../types';
10
+ /**
11
+ * `verifyEmail.proveFirst` for one user type: the user read by id —
12
+ * `NOT_FOUND` for nobody, as `signInProven` — refused when inactive, then,
13
+ * when the e-mail was never proved, `proveFirstEmail` exactly as
14
+ * `magicLink.confirm` and `signInCode.confirm` run it, decided the same way:
15
+ * on the record read, `emailVerifiedAt === null`. An e-mail already proved
16
+ * is answered as it is, with nothing written.
17
+ *
18
+ * No throttle is counted and no count restarted, no e-mail sent and no
19
+ * token issued; the only tokens spent are the ones `proveFirstEmail`
20
+ * spends, what the password opened.
21
+ */
22
+ export declare function proveFirstFlow(context: Context, type: ResolvedType, at: At): VerifyEmailApi<AnyUser>['verifyEmail']['proveFirst'];
23
+ //# sourceMappingURL=prove-first.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prove-first.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/prove-first.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAIZ,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,cAAc,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,CAgBtD"}
@@ -1,11 +1,15 @@
1
1
  /**
2
2
  * Verifying an e-mail: send a token to it, then redeem the token to mark the
3
- * address verified.
3
+ * address verified — or prove it on the application's own proof
4
+ * (`prove-first.ts`).
4
5
  */
5
6
  import type { At } from '../at';
6
7
  import type { ResolvedType } from '../config';
7
8
  import { type AnyUser, type Context } from '../context';
8
9
  import type { VerifyEmailApi } from '../types';
9
- /** `verifyEmail` of one user type: `send` a token to its e-mail, then `confirm` it. */
10
+ /**
11
+ * `verifyEmail` of one user type: `send` a token to its e-mail, then
12
+ * `confirm` it; or `proveFirst`, on the application's own proof.
13
+ */
10
14
  export declare function verifyEmailFlows(context: Context, type: ResolvedType, at: At): VerifyEmailApi<AnyUser>['verifyEmail'];
11
15
  //# sourceMappingURL=verify-email.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"verify-email.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/verify-email.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAKZ,MAAM,YAAY,CAAC;AAIpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG/C,uFAAuF;AACvF,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,cAAc,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAwCxC"}
1
+ {"version":3,"file":"verify-email.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/verify-email.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAKZ,MAAM,YAAY,CAAC;AAIpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAI/C;;;GAGG;AACH,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,cAAc,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CA0CxC"}
@@ -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, 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, 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,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,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 +1 @@
1
- {"version":3,"file":"proven-sign-in.d.ts","sourceRoot":"","sources":["../../src/auth/proven-sign-in.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,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,EAAa,MAAM,WAAW,CAAC;AAElE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAW,MAAM,SAAS,CAAC;AAEtE;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,MAAM,GACZ,eAAe,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAiBjD"}
1
+ {"version":3,"file":"proven-sign-in.d.ts","sourceRoot":"","sources":["../../src/auth/proven-sign-in.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,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,EAAoB,MAAM,WAAW,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE7D;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,MAAM,GACZ,eAAe,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAiBjD"}
@@ -91,8 +91,51 @@ export interface VerifyEmailApi<U> {
91
91
  * e-mail the user has since changed is `TOKEN_STALE`.
92
92
  */
93
93
  confirm(token: string): Promise<U>;
94
+ /**
95
+ * Applies the first proof of the user's e-mail **the application made
96
+ * itself** — an invitation token it issued to the address and the
97
+ * person followed: exactly what `magicLink.confirm` and
98
+ * `signInCode.confirm` do to an e-mail never proved, with no link and
99
+ * no code. In one write it marks the e-mail verified and drops the
100
+ * password and the second factor — active or waiting, with its
101
+ * recovery codes — and it revokes every session and spends the reset
102
+ * links and second-factor challenges, before the write and after it.
103
+ * Sends `user.emailVerified`, then `user.passwordChanged` when a
104
+ * password was dropped and `user.secondFactorDisabled` when an active
105
+ * factor was. Answers `{ user, proved: true }`.
106
+ *
107
+ * An e-mail already proved changes nothing and sends nothing:
108
+ * `{ user, proved: false }`.
109
+ *
110
+ * **Privileged: janus checks no credential.** Call it only after your
111
+ * server verified its own proof of the inbox, with the user from that
112
+ * proof's record — never with an id a request supplied: whoever
113
+ * chooses it drops anybody's password and second factor.
114
+ *
115
+ * **Call it before `setPassword` and before `signInProven`**: it drops
116
+ * the password and revokes every session, so a password set or a
117
+ * session opened before it is gone after it.
118
+ *
119
+ * Counts nothing in the mail or the sign-in throttle, sends no e-mail
120
+ * and issues no token. An inactive user is `USER_INACTIVE`; a
121
+ * malformed id, an unknown one or one of another type is `NOT_FOUND`;
122
+ * a user written since it was read is `VERSION_CONFLICT`, and nothing
123
+ * is proved.
124
+ */
125
+ proveFirst(user: UserRef): Promise<FirstProof<U>>;
94
126
  };
95
127
  }
128
+ /** What `verifyEmail.proveFirst` answers: the user, and whether this call proved the e-mail. */
129
+ export interface FirstProof<U> {
130
+ /** The user after the call: `emailVerified` is `true` either way. */
131
+ readonly user: U;
132
+ /**
133
+ * `true` when the e-mail had never been proved and this call proved it,
134
+ * dropping the password and the second factor and revoking every
135
+ * session; `false` when it was already proved, and nothing changed.
136
+ */
137
+ readonly proved: boolean;
138
+ }
96
139
  /** What a user type with an e-mail and a password answers besides. */
97
140
  export interface ResetPasswordApi<U> {
98
141
  readonly resetPassword: {
@@ -1 +1 @@
1
- {"version":3,"file":"email-flows.d.ts","sourceRoot":"","sources":["../../../src/auth/types/email-flows.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAgB,MAAM,WAAW,CAAC;AACvE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,0EAA0E;AAC1E,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CACzB;AAED,oFAAoF;AACpF,MAAM,WAAW,UAAU,CAAC,CAAC;IAC5B,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uCAAuC;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;CACjB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,UAAU,EAAE;QACpB;;;;;;;;;;;;WAYG;QACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACtD;;;;;;WAMG;QACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QACjD;;;;;;;;;;;;WAYG;QACH,OAAO,CACN,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,CAAC;KACnB,CAAC;CACF;AAED,uDAAuD;AACvD,MAAM,WAAW,cAAc,CAAC,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE;QACrB;;;;;WAKG;QACH,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QAC1C;;;WAGG;QACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACnC,CAAC;CACF;AAED,sEAAsE;AACtE,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE;QACvB;;;;;;;;WAQG;QACH,OAAO,CACN,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,CAAC,WAAW,GAAG;YAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;SAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QACxD;;;;;WAKG;QACH,OAAO,CACN,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,CAAC,WAAW,GAAG;YAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;SAAE,CAAC,CAAC,CAAC;QAChE;;;;;;WAMG;QACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACrD,CAAC;CACF"}
1
+ {"version":3,"file":"email-flows.d.ts","sourceRoot":"","sources":["../../../src/auth/types/email-flows.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@
8
8
  * dragging a type parameter through every internal function.
9
9
  */
10
10
  export type { Checked } from './checks';
11
- export type { IssuedCode, IssuedToken, ResetPasswordApi, SignInCodeApi, VerifyEmailApi, } from './email-flows';
11
+ export type { FirstProof, IssuedCode, IssuedToken, ResetPasswordApi, SignInCodeApi, VerifyEmailApi, } from './email-flows';
12
12
  export type { FindByEmailApi } from './find-by-email';
13
13
  export type { EmailOf, LoginOf, RequiredStringKeys, TypesOf, UserOf, } from './inference';
14
14
  export type { Janus, SharedApi, TypeApi } from './janus';
@@ -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,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,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"}
package/dist/index.js CHANGED
@@ -612,6 +612,12 @@ function toUser(record) {
612
612
  };
613
613
  }
614
614
  var idOf = (user) => typeof user === "string" ? user : user.id;
615
+ function refId(user) {
616
+ if (typeof user === "string")
617
+ return user;
618
+ const id = typeof user === "object" && user !== null ? user.id : undefined;
619
+ return typeof id === "string" ? id : "";
620
+ }
615
621
 
616
622
  // src/auth/context/records.ts
617
623
  var notFound = (where, id, type) => new NotFoundError2(`${where}: no ${type ?? "user"} has this id`, type === null ? { userId: id, operation: where } : { userId: id, userType: type, operation: where });
@@ -1391,39 +1397,6 @@ async function confirmReset(context, type, secret, password, where) {
1391
1397
  return toUser(written);
1392
1398
  }
1393
1399
 
1394
- // src/auth/email-flows/verify-email.ts
1395
- function verifyEmailFlows(context, type, at) {
1396
- return {
1397
- async send(user) {
1398
- const where = at("verifyEmail.send");
1399
- const record = await getRecord(context, idOf(user), type.name, where);
1400
- await countMailByUser(context, type, "verifyEmail", record.id, where);
1401
- return issueEmailToken(context, type, "verifyEmail", record, where);
1402
- },
1403
- async confirm(secret) {
1404
- const where = at("verifyEmail.confirm");
1405
- const { token, user } = await redeemEmailToken(context, type, "verifyEmail", secret, where);
1406
- let newlyVerified = false;
1407
- const written = await writeUser(context, user.id, type, undefined, where, (record, now) => {
1408
- refuseStale(type, record, token, where, "token");
1409
- newlyVerified = record.emailVerifiedAt === null;
1410
- return { emailVerifiedAt: now };
1411
- });
1412
- if (newlyVerified) {
1413
- await emit(context, "user.emailVerified", written, written.updatedAt);
1414
- }
1415
- return toUser(written);
1416
- }
1417
- };
1418
- }
1419
-
1420
- // src/auth/email-flows/flows.ts
1421
- function emailFlows(context, type, at) {
1422
- return {
1423
- verifyEmail: verifyEmailFlows(context, type, at),
1424
- resetPassword: resetPasswordFlows(context, type, at)
1425
- };
1426
- }
1427
1400
  // src/auth/totp.ts
1428
1401
  import { createHmac as createHmac2, randomBytes as randomBytes5 } from "node:crypto";
1429
1402
  var TOTP = { digits: 6, stepMs: 30000, drift: 1 };
@@ -1558,6 +1531,59 @@ async function endWhatTheSquatterOpened(context, userId) {
1558
1531
  await endWhatThePasswordOpened(context, userId);
1559
1532
  }
1560
1533
 
1534
+ // src/auth/email-flows/prove-first.ts
1535
+ function proveFirstFlow(context, type, at) {
1536
+ return async (user) => {
1537
+ const where = at("verifyEmail.proveFirst");
1538
+ const record = await getRecord(context, refId(user), type.name, where);
1539
+ if (!record.active) {
1540
+ throw new UserInactiveError2(`${where}: the user is inactive`, {
1541
+ userId: record.id,
1542
+ userType: type.name
1543
+ });
1544
+ }
1545
+ if (record.emailVerifiedAt !== null) {
1546
+ return { user: toUser(record), proved: false };
1547
+ }
1548
+ const proved = await proveFirstEmail(context, type, record, where);
1549
+ return { user: toUser(proved), proved: true };
1550
+ };
1551
+ }
1552
+
1553
+ // src/auth/email-flows/verify-email.ts
1554
+ function verifyEmailFlows(context, type, at) {
1555
+ return {
1556
+ async send(user) {
1557
+ const where = at("verifyEmail.send");
1558
+ const record = await getRecord(context, idOf(user), type.name, where);
1559
+ await countMailByUser(context, type, "verifyEmail", record.id, where);
1560
+ return issueEmailToken(context, type, "verifyEmail", record, where);
1561
+ },
1562
+ async confirm(secret) {
1563
+ const where = at("verifyEmail.confirm");
1564
+ const { token, user } = await redeemEmailToken(context, type, "verifyEmail", secret, where);
1565
+ let newlyVerified = false;
1566
+ const written = await writeUser(context, user.id, type, undefined, where, (record, now) => {
1567
+ refuseStale(type, record, token, where, "token");
1568
+ newlyVerified = record.emailVerifiedAt === null;
1569
+ return { emailVerifiedAt: now };
1570
+ });
1571
+ if (newlyVerified) {
1572
+ await emit(context, "user.emailVerified", written, written.updatedAt);
1573
+ }
1574
+ return toUser(written);
1575
+ },
1576
+ proveFirst: proveFirstFlow(context, type, at)
1577
+ };
1578
+ }
1579
+
1580
+ // src/auth/email-flows/flows.ts
1581
+ function emailFlows(context, type, at) {
1582
+ return {
1583
+ verifyEmail: verifyEmailFlows(context, type, at),
1584
+ resetPassword: resetPasswordFlows(context, type, at)
1585
+ };
1586
+ }
1561
1587
  // src/auth/email-sign-in.ts
1562
1588
  async function signInHolder(context, type, email) {
1563
1589
  const record = await holderOfEmail(context, type, email);
@@ -1636,7 +1662,7 @@ function provenSignInFlows(context, type, at, finish) {
1636
1662
  async signInProven(user, options) {
1637
1663
  const where = at("signInProven");
1638
1664
  const device = deviceHint(context, options, where);
1639
- const record = await getRecord(context, idOf2(user), type.name, where);
1665
+ const record = await getRecord(context, refId(user), type.name, where);
1640
1666
  if (!record.active) {
1641
1667
  throw new UserInactiveError2(`${where}: the user is inactive`, {
1642
1668
  userId: record.id,
@@ -1649,12 +1675,6 @@ function provenSignInFlows(context, type, at, finish) {
1649
1675
  }
1650
1676
  };
1651
1677
  }
1652
- function idOf2(user) {
1653
- if (typeof user === "string")
1654
- return user;
1655
- const id = typeof user === "object" && user !== null ? user.id : undefined;
1656
- return typeof id === "string" ? id : "";
1657
- }
1658
1678
 
1659
1679
  // src/auth/sign-in-attempts.ts
1660
1680
  var PURPOSE3 = "janus/sign-in/attempts/v1";
@@ -2628,5 +2648,5 @@ export {
2628
2648
  systemClock
2629
2649
  };
2630
2650
 
2631
- //# debugId=8864B5F285AA35A664756E2164756E21
2651
+ //# debugId=D104C8FB67042A0864756E2164756E21
2632
2652
  //# sourceMappingURL=index.js.map