@absolutejs/auth 0.65.3 → 0.65.5

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.
@@ -69,9 +69,10 @@ export type CredentialsConfig<UserType> = {
69
69
  }) => void | Promise<void>;
70
70
  passwordPolicy?: PasswordPolicy;
71
71
  registerRoute?: RouteString;
72
- /** When true, registration creates the account but NOT a session, and login is
73
- * rejected until the email is verified. Default false = auto-login on register and
74
- * verification acts as a soft, later gate. */
72
+ /** When true, registration stores only a pending credential and does NOT call
73
+ * `onCreateCredentialUser` until a verification token is accepted. No user
74
+ * account or session exists before email ownership is proven. Default false
75
+ * creates the user immediately and auto-logs them in. */
75
76
  requireEmailVerification?: boolean;
76
77
  /** When true, `POST /register` returns 409 "Email is already registered" for a
77
78
  * known email. Default false = enumeration-safe: return the same generic
@@ -1,6 +1,6 @@
1
1
  import { Elysia } from 'elysia';
2
2
  import { type CredentialsConfig } from './config';
3
- export declare const credentialsEmailVerification: <UserType>({ credentialStore, onEmailVerified, onSendEmail, verificationTokenDurationMs, verifyEmailRoute }: CredentialsConfig<UserType>) => Elysia<"", {
3
+ export declare const credentialsEmailVerification: <UserType>({ credentialStore, getUserByEmail, onCreateCredentialUser, onEmailVerified, onRegistrationSuccess, onSendEmail, requireEmailVerification, verificationTokenDurationMs, verifyEmailRoute }: CredentialsConfig<UserType>) => Elysia<"", {
4
4
  decorator: {};
5
5
  store: {};
6
6
  derive: {};
@@ -25,19 +25,7 @@ export declare const credentialsEmailVerification: <UserType>({ credentialStore,
25
25
  query: unknown;
26
26
  headers: unknown;
27
27
  response: {
28
- 400: "Invalid or expired verification token";
29
- 200: {
30
- readonly status: "email_verified";
31
- };
32
- 422: {
33
- type: "validation";
34
- on: string;
35
- summary?: string;
36
- message?: string;
37
- found?: unknown;
38
- property?: string;
39
- expected?: string;
40
- };
28
+ [x: number]: {};
41
29
  };
42
30
  };
43
31
  };
@@ -79,6 +79,21 @@ export declare const credentialsTable: import("drizzle-orm/pg-core").PgTableWith
79
79
  identity: undefined;
80
80
  generated: undefined;
81
81
  }>;
82
+ registration_data: import("drizzle-orm/pg-core").PgBuildColumn<"auth_credentials", import("drizzle-orm/pg-core").Set$Type<import("drizzle-orm/pg-core").PgJsonbBuilder, Record<string, unknown>>, {
83
+ name: string;
84
+ tableName: "auth_credentials";
85
+ dataType: "object json";
86
+ data: Record<string, unknown>;
87
+ driverParam: unknown;
88
+ notNull: false;
89
+ hasDefault: false;
90
+ isPrimaryKey: false;
91
+ isAutoincrement: false;
92
+ hasRuntimeDefault: false;
93
+ enumValues: undefined;
94
+ identity: undefined;
95
+ generated: undefined;
96
+ }>;
82
97
  status: import("drizzle-orm/pg-core").PgBuildColumn<"auth_credentials", import("drizzle-orm/pg-core").SetHasDefault<import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgVarcharBuilder<[string, ...string[]]>>>, {
83
98
  name: string;
84
99
  tableName: "auth_credentials";
@@ -43,19 +43,7 @@ export declare const credentialRoutes: <UserType>(config: CredentialRouteProps<U
43
43
  query: unknown;
44
44
  headers: unknown;
45
45
  response: {
46
- 400: "Invalid or expired verification token";
47
- 200: {
48
- readonly status: "email_verified";
49
- };
50
- 422: {
51
- type: "validation";
52
- on: string;
53
- summary?: string;
54
- message?: string;
55
- found?: unknown;
56
- property?: string;
57
- expected?: string;
58
- };
46
+ [x: number]: {};
59
47
  };
60
48
  };
61
49
  };
@@ -6,6 +6,12 @@ export type CredentialRecord = {
6
6
  emailVerified: boolean;
7
7
  organizationId?: OrganizationId;
8
8
  passwordHash: string;
9
+ /**
10
+ * Signup fields held until email ownership is proven. This is deliberately
11
+ * stored with the credential rather than in the consumer's user table so
12
+ * `requireEmailVerification` does not create an impersonatable account.
13
+ */
14
+ registrationData?: Record<string, unknown>;
9
15
  status: CredentialStatus;
10
16
  updatedAt: number;
11
17
  userId?: string;
package/dist/index.d.ts CHANGED
@@ -760,19 +760,7 @@ export declare const createAuthApplications: <UserType>(configuration: AuthConfi
760
760
  query: unknown;
761
761
  headers: unknown;
762
762
  response: {
763
- 400: "Invalid or expired verification token";
764
- 200: {
765
- readonly status: "email_verified";
766
- };
767
- 422: {
768
- type: "validation";
769
- on: string;
770
- summary?: string;
771
- message?: string;
772
- found?: unknown;
773
- property?: string;
774
- expected?: string;
775
- };
763
+ [x: number]: {};
776
764
  };
777
765
  };
778
766
  };
package/dist/index.js CHANGED
@@ -5124,8 +5124,12 @@ var DEFAULT_VERIFICATION_TOKEN_TTL_MS = MILLISECONDS_IN_A_DAY;
5124
5124
  // src/credentials/emailVerification.ts
5125
5125
  var credentialsEmailVerification = ({
5126
5126
  credentialStore,
5127
+ getUserByEmail,
5128
+ onCreateCredentialUser,
5127
5129
  onEmailVerified,
5130
+ onRegistrationSuccess,
5128
5131
  onSendEmail,
5132
+ requireEmailVerification = false,
5129
5133
  verificationTokenDurationMs = DEFAULT_VERIFICATION_TOKEN_TTL_MS,
5130
5134
  verifyEmailRoute = "/auth/verify-email"
5131
5135
  }) => new Elysia10().post(verifyEmailRoute, async ({ body: { token }, status }) => {
@@ -5136,7 +5140,36 @@ var credentialsEmailVerification = ({
5136
5140
  if (consumed.expiresAt < Date.now()) {
5137
5141
  return status("Bad Request", "Invalid or expired verification token");
5138
5142
  }
5143
+ const credential = await credentialStore.getCredentialByEmail(consumed.email);
5144
+ if (!credential) {
5145
+ return status("Bad Request", "Invalid or expired verification token");
5146
+ }
5147
+ let user = await getUserByEmail(consumed.email);
5148
+ if (requireEmailVerification && !user && credential.registrationData !== undefined) {
5149
+ const created = await onCreateCredentialUser({
5150
+ ...credential.registrationData,
5151
+ email: consumed.email
5152
+ });
5153
+ if (created instanceof Response || isStatusResponse(created)) {
5154
+ return created;
5155
+ }
5156
+ user = created;
5157
+ }
5139
5158
  await credentialStore.setEmailVerified(consumed.email);
5159
+ if (credential.registrationData !== undefined) {
5160
+ await credentialStore.saveCredential({
5161
+ ...credential,
5162
+ emailVerified: true,
5163
+ registrationData: undefined,
5164
+ updatedAt: Date.now()
5165
+ });
5166
+ }
5167
+ if (user && credential.registrationData !== undefined) {
5168
+ await onRegistrationSuccess?.({
5169
+ email: consumed.email,
5170
+ user
5171
+ });
5172
+ }
5140
5173
  await onEmailVerified?.({ email: consumed.email });
5141
5174
  return status("OK", { status: "email_verified" });
5142
5175
  }, { body: t7.Object({ token: t7.String() }) }).post(`${verifyEmailRoute}/request`, async ({ body: { email }, status }) => {
@@ -5510,7 +5543,7 @@ var credentialsPasswordReset = ({
5510
5543
  }) => new Elysia12().post(`${resetPasswordRoute}/request`, async ({ body: { email }, status }) => {
5511
5544
  const normalizedEmail = email.trim().toLowerCase();
5512
5545
  const credential = await credentialStore.getCredentialByEmail(normalizedEmail);
5513
- if (credential && credential.status === "active") {
5546
+ if (credential && credential.status === "active" && credential.registrationData === undefined) {
5514
5547
  const token = generateSecureToken();
5515
5548
  const expiresAt = Date.now() + resetTokenDurationMs;
5516
5549
  await credentialStore.saveResetToken({
@@ -5620,12 +5653,16 @@ var credentialsRegister = ({
5620
5653
  status: "verification_required"
5621
5654
  });
5622
5655
  }
5623
- const created = await onCreateCredentialUser({
5624
- ...extraFields,
5625
- email: normalizedEmail
5626
- });
5627
- if (created instanceof Response || isStatusResponse(created)) {
5628
- return created;
5656
+ let created;
5657
+ if (!requireEmailVerification) {
5658
+ const result = await onCreateCredentialUser({
5659
+ ...extraFields,
5660
+ email: normalizedEmail
5661
+ });
5662
+ if (result instanceof Response || isStatusResponse(result)) {
5663
+ return result;
5664
+ }
5665
+ created = result;
5629
5666
  }
5630
5667
  const now = Date.now();
5631
5668
  await credentialStore.saveCredential({
@@ -5633,6 +5670,7 @@ var credentialsRegister = ({
5633
5670
  email: normalizedEmail,
5634
5671
  emailVerified: false,
5635
5672
  passwordHash: await hashPassword(password),
5673
+ registrationData: requireEmailVerification ? extraFields : undefined,
5636
5674
  status: "active",
5637
5675
  updatedAt: now
5638
5676
  });
@@ -5649,15 +5687,18 @@ var credentialsRegister = ({
5649
5687
  token,
5650
5688
  type: "verify_email"
5651
5689
  });
5652
- await onRegistrationSuccess?.({
5653
- email: normalizedEmail,
5654
- user: created
5655
- });
5656
5690
  if (requireEmailVerification) {
5657
5691
  return status("Created", {
5658
5692
  status: "verification_required"
5659
5693
  });
5660
5694
  }
5695
+ if (created === undefined) {
5696
+ throw new Error("Credential user was not created");
5697
+ }
5698
+ await onRegistrationSuccess?.({
5699
+ email: normalizedEmail,
5700
+ user: created
5701
+ });
5661
5702
  const userSessionId = await promoteToSession({
5662
5703
  authSessionStore,
5663
5704
  cookie: user_session_id,
@@ -31044,8 +31085,8 @@ var annotateFailureMetadata = (metadata, report, now) => ({
31044
31085
  ...metadata ?? {},
31045
31086
  lastCredentialFailureAt: now,
31046
31087
  lastCredentialFailureCode: report.code,
31047
- lastCredentialFailureMessage: report.message,
31048
- lastCredentialFailureRetryAt: report.retryAt
31088
+ ...report.message === undefined ? {} : { lastCredentialFailureMessage: report.message },
31089
+ ...report.retryAt === undefined ? {} : { lastCredentialFailureRetryAt: report.retryAt }
31049
31090
  });
31050
31091
  var sortNewestFirst = (items) => [...items].sort((left, right) => right.updatedAt - left.updatedAt);
31051
31092
  var resolveGrantFailureStatus = (grant, report) => {
@@ -32010,7 +32051,8 @@ var validateEmailDeliverability = async (email, options) => {
32010
32051
  };
32011
32052
  // src/credentials/inMemoryCredentialStore.ts
32012
32053
  var cloneCredential = (value) => ({
32013
- ...value
32054
+ ...value,
32055
+ registrationData: value.registrationData === undefined ? undefined : structuredClone(value.registrationData)
32014
32056
  });
32015
32057
  var cloneToken = (value) => ({ ...value });
32016
32058
  var consumeToken = (tokens, tokenHash) => {
@@ -32065,6 +32107,7 @@ var credentialsTable = pgTable("auth_credentials", {
32065
32107
  email_verified: boolean("email_verified").notNull().default(false),
32066
32108
  organization_id: varchar("organization_id", { length: ID_LENGTH2 }),
32067
32109
  password_hash: text("password_hash").notNull(),
32110
+ registration_data: jsonb("registration_data").$type(),
32068
32111
  status: varchar("status", { length: STATUS_LENGTH }).notNull().default("active"),
32069
32112
  updated_at_ms: bigint("updated_at_ms", { mode: "number" }).notNull(),
32070
32113
  user_id: varchar("user_id", { length: ID_LENGTH2 })
@@ -32086,6 +32129,7 @@ var toCredentialRecord = (row) => ({
32086
32129
  emailVerified: row.email_verified,
32087
32130
  organizationId: row.organization_id ?? undefined,
32088
32131
  passwordHash: row.password_hash,
32132
+ registrationData: row.registration_data ?? undefined,
32089
32133
  status: isCredentialStatus(row.status) ? row.status : "active",
32090
32134
  updatedAt: row.updated_at_ms,
32091
32135
  userId: row.user_id ?? undefined
@@ -32127,6 +32171,7 @@ var createPostgresCredentialStore = (db) => ({
32127
32171
  email_verified: credential.emailVerified,
32128
32172
  organization_id: credential.organizationId ?? null,
32129
32173
  password_hash: credential.passwordHash,
32174
+ registration_data: credential.registrationData ?? null,
32130
32175
  status: credential.status,
32131
32176
  updated_at_ms: credential.updatedAt,
32132
32177
  user_id: credential.userId ?? null
@@ -36771,6 +36816,10 @@ var mfaSmsColumnsMigration = {
36771
36816
  ].join(`
36772
36817
  `)
36773
36818
  };
36819
+ var credentialDeferredUserMigration = {
36820
+ id: "0002_deferred_user_creation",
36821
+ sql: 'ALTER TABLE "auth_credentials" ADD COLUMN IF NOT EXISTS "registration_data" jsonb;'
36822
+ };
36774
36823
  var mfaTotpLockoutMigration = {
36775
36824
  id: "0003_totp_lockout",
36776
36825
  sql: 'ALTER TABLE "auth_mfa_enrollments" ADD COLUMN IF NOT EXISTS "totp_failed_attempts" smallint NOT NULL DEFAULT 0;'
@@ -36826,11 +36875,17 @@ var blockMigrations = {
36826
36875
  apiKeysTable
36827
36876
  ]),
36828
36877
  audit: initMigration("audit", [auditEventsTable]),
36829
- credentials: initMigration("credentials", [
36830
- credentialsTable,
36831
- credentialResetTokensTable,
36832
- credentialVerificationTokensTable
36833
- ]),
36878
+ credentials: {
36879
+ block: "credentials",
36880
+ migrations: [
36881
+ ...initMigration("credentials", [
36882
+ credentialsTable,
36883
+ credentialResetTokensTable,
36884
+ credentialVerificationTokensTable
36885
+ ]).migrations,
36886
+ credentialDeferredUserMigration
36887
+ ]
36888
+ },
36834
36889
  fga: initMigration("fga", [warrantsTable]),
36835
36890
  linkedProviders: initMigration("linkedProviders", [
36836
36891
  linkedProviderBindingsTable,
@@ -38181,5 +38236,5 @@ export {
38181
38236
  AGENT_CLAIM_GRANT_TYPE
38182
38237
  };
38183
38238
 
38184
- //# debugId=EEB323A04F3FC17564756E2164756E21
38239
+ //# debugId=BB9E2E153512DF2164756E2164756E21
38185
38240
  //# sourceMappingURL=index.js.map