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