@adminforth/email-invite 1.2.2 → 1.2.4

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/build.log CHANGED
@@ -7,5 +7,5 @@ custom/
7
7
  custom/SetPassword.vue
8
8
  custom/tsconfig.json
9
9
 
10
- sent 14,973 bytes received 58 bytes 30,062.00 bytes/sec
10
+ sent 14,976 bytes received 58 bytes 30,068.00 bytes/sec
11
11
  total size is 14,761 speedup is 0.98
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export default class EmailInvitePlugin extends AdminForthPlugin {
10
10
  modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource): Promise<void>;
11
11
  validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource): void;
12
12
  instanceUniqueRepresentation(pluginOptions: any): string;
13
+ passwordHashDigest(passwordHash: string | null | undefined): string;
13
14
  handleUserCreation({ resource, adminUser, record, adminforth, extra }: {
14
15
  resource: AdminForthResource;
15
16
  adminUser: AdminUser;
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import AdminForth, { AdminForthPlugin, suggestIfTypo, Filters, afLogger } from "adminforth";
11
11
  import { z } from "zod";
12
+ import { createHash, randomUUID } from "crypto";
12
13
  const setPasswordBodySchema = z.object({
13
14
  token: z.string(),
14
15
  password: z.string(),
@@ -119,6 +120,9 @@ export default class EmailInvitePlugin extends AdminForthPlugin {
119
120
  instanceUniqueRepresentation(pluginOptions) {
120
121
  return `single`;
121
122
  }
123
+ passwordHashDigest(passwordHash) {
124
+ return createHash('sha256').update(passwordHash || '').digest('hex').slice(0, 16);
125
+ }
122
126
  handleUserCreation(_a) {
123
127
  return __awaiter(this, arguments, void 0, function* ({ resource, adminUser, record, adminforth, extra }) {
124
128
  try {
@@ -127,7 +131,7 @@ export default class EmailInvitePlugin extends AdminForthPlugin {
127
131
  delete record.password;
128
132
  }
129
133
  const passwordHashFieldName = adminforth.config.auth.passwordHashField;
130
- record[passwordHashFieldName] = yield AdminForth.Utils.generatePasswordHash('TEMP_INVITE_PLACEHOLDER_' + Date.now());
134
+ record[passwordHashFieldName] = yield AdminForth.Utils.generatePasswordHash('TEMP_INVITE_PLACEHOLDER_' + randomUUID());
131
135
  if (this.options.emailConfirmedField && this.emailConfirmedField) {
132
136
  record[this.emailConfirmedField.name] = false;
133
137
  }
@@ -144,15 +148,19 @@ export default class EmailInvitePlugin extends AdminForthPlugin {
144
148
  var _b, _c;
145
149
  try {
146
150
  const email = record[this.options.emailField];
151
+ const newRecordId = record[this.authResource.columns.find(col => col.primaryKey).name];
147
152
  if (!email || !/\S+@\S+\.\S+/.test(email)) {
148
153
  console.warn('Email invite plugin: Invalid or missing email address');
149
154
  return { ok: true }; // Don't fail user creation for email issues
150
155
  }
151
156
  const brandName = adminforth.config.customization.brandName || 'Admin Panel';
152
- const inviteToken = adminforth.auth.issueJWT({ email, recordId, inviteEmail: true }, 'inviteUser', '7d' // 7 days validity
157
+ const inviteToken = adminforth.auth.issueJWT({
158
+ email,
159
+ recordId: newRecordId,
160
+ inviteEmail: true,
161
+ ph: this.passwordHashDigest(record[this.adminforth.config.auth.passwordHashField])
162
+ }, 'inviteUser', '7d' // 7 days validity
153
163
  );
154
- console.log('Sending invite email to:', email);
155
- console.log('Generated JWT token payload:', { email, recordId, inviteEmail: true });
156
164
  const host = ((_b = extra === null || extra === void 0 ? void 0 : extra.headers) === null || _b === void 0 ? void 0 : _b.host) || 'localhost';
157
165
  const protocol = ((_c = extra === null || extra === void 0 ? void 0 : extra.headers) === null || _c === void 0 ? void 0 : _c['x-forwarded-proto']) || 'http';
158
166
  const inviteUrl = `${protocol}://${host}/set-password?token=${inviteToken}`;
@@ -219,6 +227,9 @@ export default class EmailInvitePlugin extends AdminForthPlugin {
219
227
  if (recordId) {
220
228
  userRecord = yield this.adminforth.resource(this.authResource.resourceId).get(Filters.EQ(this.authResource.columns.find(c => c.primaryKey).name, recordId));
221
229
  }
230
+ if (decoded.ph !== this.passwordHashDigest(userRecord[this.adminforth.config.auth.passwordHashField])) {
231
+ return { error: 'Token has already been used', ok: false };
232
+ }
222
233
  if (!userRecord && email) {
223
234
  const records = yield this.adminforth.resource(this.authResource.resourceId).list(Filters.EQ(this.options.emailField, email), 1);
224
235
  userRecord = records.length > 0 ? records[0] : null;
@@ -226,7 +237,7 @@ export default class EmailInvitePlugin extends AdminForthPlugin {
226
237
  if (!userRecord) {
227
238
  return { error: 'User not found', ok: false };
228
239
  }
229
- if (userRecord[this.options.emailConfirmedField]) {
240
+ if (this.options.emailConfirmedField && userRecord[this.options.emailConfirmedField]) {
230
241
  return { error: 'Password already set. Invitation link cannot be reused.', ok: false };
231
242
  }
232
243
  const userEmail = userRecord[this.options.emailField];
package/index.ts CHANGED
@@ -2,6 +2,7 @@ import AdminForth, { AdminForthPlugin, suggestIfTypo, Filters, afLogger } from "
2
2
  import type { IAdminForth, IHttpServer, AdminForthResourcePages, AdminForthResourceColumn, AdminForthDataTypes, AdminForthResource, AdminUser, HttpExtra } from "adminforth";
3
3
  import type { PluginOptions } from './types.js';
4
4
  import { z } from "zod";
5
+ import { createHash, randomUUID } from "crypto";
5
6
 
6
7
  const setPasswordBodySchema = z.object({
7
8
  token: z.string(),
@@ -137,6 +138,11 @@ export default class EmailInvitePlugin extends AdminForthPlugin {
137
138
  return `single`;
138
139
  }
139
140
 
141
+ passwordHashDigest(passwordHash: string | null | undefined): string {
142
+ return createHash('sha256').update(passwordHash || '').digest('hex').slice(0, 16);
143
+ }
144
+
145
+
140
146
  async handleUserCreation({ resource, adminUser, record, adminforth, extra }: {
141
147
  resource: AdminForthResource;
142
148
  adminUser: AdminUser;
@@ -151,7 +157,7 @@ export default class EmailInvitePlugin extends AdminForthPlugin {
151
157
  }
152
158
 
153
159
  const passwordHashFieldName = adminforth.config.auth!.passwordHashField;
154
- record[passwordHashFieldName] = await AdminForth.Utils.generatePasswordHash('TEMP_INVITE_PLACEHOLDER_' + Date.now());
160
+ record[passwordHashFieldName] = await AdminForth.Utils.generatePasswordHash('TEMP_INVITE_PLACEHOLDER_' + randomUUID());
155
161
 
156
162
  if (this.options.emailConfirmedField && this.emailConfirmedField) {
157
163
  record[this.emailConfirmedField.name] = false;
@@ -174,6 +180,7 @@ export default class EmailInvitePlugin extends AdminForthPlugin {
174
180
  }): Promise<{ok: boolean, error?: string}> {
175
181
  try {
176
182
  const email = record[this.options.emailField];
183
+ const newRecordId = record[this.authResource.columns.find(col => col.primaryKey)!.name];
177
184
 
178
185
  if (!email || !/\S+@\S+\.\S+/.test(email)) {
179
186
  console.warn('Email invite plugin: Invalid or missing email address');
@@ -181,16 +188,18 @@ export default class EmailInvitePlugin extends AdminForthPlugin {
181
188
  }
182
189
 
183
190
  const brandName = adminforth.config.customization.brandName || 'Admin Panel';
184
-
191
+
185
192
  const inviteToken = adminforth.auth.issueJWT(
186
- { email, recordId, inviteEmail: true },
193
+ {
194
+ email,
195
+ recordId: newRecordId,
196
+ inviteEmail: true,
197
+ ph: this.passwordHashDigest(record[this.adminforth.config.auth.passwordHashField])
198
+ },
187
199
  'inviteUser',
188
200
  '7d' // 7 days validity
189
201
  );
190
202
 
191
- console.log('Sending invite email to:', email);
192
- console.log('Generated JWT token payload:', { email, recordId, inviteEmail: true });
193
-
194
203
  const host = extra?.headers?.host || 'localhost';
195
204
  const protocol = extra?.headers?.['x-forwarded-proto'] || 'http';
196
205
  const inviteUrl = `${protocol}://${host}/set-password?token=${inviteToken}`;
@@ -270,6 +279,10 @@ export default class EmailInvitePlugin extends AdminForthPlugin {
270
279
  userRecord = await this.adminforth.resource(this.authResource.resourceId).get(Filters.EQ(this.authResource.columns.find(c => c.primaryKey).name, recordId));
271
280
  }
272
281
 
282
+ if (decoded.ph !== this.passwordHashDigest(userRecord[this.adminforth.config.auth.passwordHashField])) {
283
+ return { error: 'Token has already been used', ok: false };
284
+ }
285
+
273
286
  if (!userRecord && email) {
274
287
  const records = await this.adminforth.resource(this.authResource.resourceId).list(
275
288
  Filters.EQ(this.options.emailField, email),
@@ -281,7 +294,7 @@ export default class EmailInvitePlugin extends AdminForthPlugin {
281
294
  return { error: 'User not found', ok: false };
282
295
  }
283
296
 
284
- if ( userRecord[this.options.emailConfirmedField] ) {
297
+ if ( this.options.emailConfirmedField && userRecord[this.options.emailConfirmedField] ) {
285
298
  return { error: 'Password already set. Invitation link cannot be reused.', ok: false };
286
299
  }
287
300
  const userEmail = userRecord[this.options.emailField];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/email-invite",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",