@adminforth/open-signup 1.0.2 → 1.0.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/custom/SignupPage.vue +55 -47
- package/dist/custom/SignupPage.vue +55 -47
- package/dist/index.js +58 -86
- package/index.ts +69 -97
- package/package.json +1 -1
- package/types.ts +4 -13
- package/dist/custom/ResetPassword.vue +0 -295
- package/dist/custom/ResetPasswordUnderLogin.vue +0 -13
package/dist/index.js
CHANGED
|
@@ -8,7 +8,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import AdminForth, { AdminForthPlugin, Filters, suggestIfTypo, AdminForthDataTypes } from "adminforth";
|
|
11
|
-
import { SendEmailCommand, SESClient } from '@aws-sdk/client-ses';
|
|
12
11
|
export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
13
12
|
constructor(options) {
|
|
14
13
|
super(options, import.meta.url);
|
|
@@ -23,9 +22,15 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
23
22
|
if (!this.options.emailField) {
|
|
24
23
|
throw new Error(`emailField is required and should be a name of field in auth resource`);
|
|
25
24
|
}
|
|
25
|
+
// find field with name resourceConfig.emailField in adminforth.auth.usersResourceId and show error if it doesn't exist
|
|
26
|
+
const authResource = adminforth.config.resources.find(r => r.resourceId === adminforth.config.auth.usersResourceId);
|
|
27
|
+
if (!authResource) {
|
|
28
|
+
throw new Error(`Resource with id config.auth.usersResourceId=${adminforth.config.auth.usersResourceId} not found`);
|
|
29
|
+
}
|
|
30
|
+
this.authResource = authResource;
|
|
26
31
|
if (this.options.confirmEmails) {
|
|
27
|
-
if (!this.options.confirmEmails.
|
|
28
|
-
throw new Error(`confirmEmails.
|
|
32
|
+
if (!this.options.confirmEmails.adapter) {
|
|
33
|
+
throw new Error(`confirmEmails.adapter is required and should be a name of field in auth resource`);
|
|
29
34
|
}
|
|
30
35
|
if (!this.options.confirmEmails.emailConfirmedField) {
|
|
31
36
|
throw new Error(`confirmEmails.emailConfirmedField is required and should be a name of field in auth resource`);
|
|
@@ -38,16 +43,7 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
38
43
|
`);
|
|
39
44
|
}
|
|
40
45
|
this.emailConfirmedField = emailConfirmedField;
|
|
41
|
-
if (this.emailConfirmedField.type !== AdminForthDataTypes.BOOLEAN) {
|
|
42
|
-
throw new Error(`Field ${this.emailConfirmedField.name} must be of type boolean`);
|
|
43
|
-
}
|
|
44
46
|
}
|
|
45
|
-
// find field with name resourceConfig.emailField in adminforth.auth.usersResourceId and show error if it doesn't exist
|
|
46
|
-
const authResource = adminforth.config.resources.find(r => r.resourceId === adminforth.config.auth.usersResourceId);
|
|
47
|
-
if (!authResource) {
|
|
48
|
-
throw new Error(`Resource with id config.auth.usersResourceId=${adminforth.config.auth.usersResourceId} not found`);
|
|
49
|
-
}
|
|
50
|
-
this.authResource = authResource;
|
|
51
47
|
const emailField = authResource.columns.find(f => f.name === this.options.emailField);
|
|
52
48
|
if (!emailField) {
|
|
53
49
|
const similar = suggestIfTypo(authResource.columns.map(f => f.name), this.options.emailField);
|
|
@@ -81,7 +77,8 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
81
77
|
minLength: passwordField.minLength,
|
|
82
78
|
maxLength: passwordField.maxLength,
|
|
83
79
|
validation: passwordField.validation
|
|
84
|
-
}
|
|
80
|
+
},
|
|
81
|
+
requestEmailConfirmation: !!this.options.confirmEmails
|
|
85
82
|
}
|
|
86
83
|
}
|
|
87
84
|
});
|
|
@@ -90,24 +87,23 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
90
87
|
if (!adminforth.config.auth.beforeLoginConfirmation) {
|
|
91
88
|
adminforth.config.auth.beforeLoginConfirmation = [];
|
|
92
89
|
}
|
|
93
|
-
// unshift because if e.g. 2fa set it's hook, this one should be first
|
|
94
|
-
adminforth.config.auth.beforeLoginConfirmation.unshift((_a) => __awaiter(this, [_a], void 0, function* ({ adminUser }) {
|
|
95
|
-
if (!adminUser.dbUser[this.emailConfirmedField.name]) {
|
|
96
|
-
return { body: { allowedLogin: false }, error: 'You need to confirm your email to be able to login' };
|
|
97
|
-
}
|
|
98
|
-
}));
|
|
99
90
|
}
|
|
100
91
|
});
|
|
101
92
|
}
|
|
102
93
|
validateConfigAfterDiscover(adminforth, resourceConfig) {
|
|
103
|
-
|
|
94
|
+
if (this.options.confirmEmails) {
|
|
95
|
+
const emailConfirmedColumn = this.resourceConfig.columns.find(f => f.name === this.options.confirmEmails.emailConfirmedField);
|
|
96
|
+
if (emailConfirmedColumn.type !== AdminForthDataTypes.BOOLEAN) {
|
|
97
|
+
throw new Error(`Field ${this.emailConfirmedField.name} must be of type boolean`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
104
100
|
}
|
|
105
101
|
instanceUniqueRepresentation(pluginOptions) {
|
|
106
102
|
// optional method to return unique string representation of plugin instance.
|
|
107
103
|
// Needed if plugin can have multiple instances on one resource
|
|
108
104
|
return `single`;
|
|
109
105
|
}
|
|
110
|
-
doLogin(email, response) {
|
|
106
|
+
doLogin(email, response, extra) {
|
|
111
107
|
return __awaiter(this, void 0, void 0, function* () {
|
|
112
108
|
const username = email;
|
|
113
109
|
const userRecord = yield this.adminforth.resource(this.authResource.resourceId).get(Filters.EQ(this.emailField.name, email));
|
|
@@ -117,7 +113,7 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
117
113
|
username,
|
|
118
114
|
};
|
|
119
115
|
const toReturn = { allowedLogin: true, error: '' };
|
|
120
|
-
yield this.adminforth.restApi.processLoginCallbacks(adminUser, toReturn, response);
|
|
116
|
+
yield this.adminforth.restApi.processLoginCallbacks(adminUser, toReturn, response, extra);
|
|
121
117
|
if (toReturn.allowedLogin) {
|
|
122
118
|
this.adminforth.auth.setAuthCookie({
|
|
123
119
|
response,
|
|
@@ -131,84 +127,79 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
131
127
|
setupEndpoints(server) {
|
|
132
128
|
server.endpoint({
|
|
133
129
|
method: 'POST',
|
|
134
|
-
path: `/plugin/${this.pluginInstanceId}/
|
|
130
|
+
path: `/plugin/${this.pluginInstanceId}/complete-verified-signup`,
|
|
135
131
|
noAuth: true,
|
|
136
|
-
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, response }) {
|
|
137
|
-
const { token } = body;
|
|
138
|
-
const { email } = yield this.adminforth.auth.verify(token, 'tempVerifyEmailToken');
|
|
132
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, response, headers, query, cookies }) {
|
|
133
|
+
const { token, password } = body;
|
|
134
|
+
const { email } = yield this.adminforth.auth.verify(token, 'tempVerifyEmailToken', false);
|
|
139
135
|
if (!email) {
|
|
140
136
|
return { error: 'Invalid token', ok: false };
|
|
141
137
|
}
|
|
138
|
+
if (!password) {
|
|
139
|
+
return { error: 'Password is required', ok: false };
|
|
140
|
+
}
|
|
142
141
|
const userRecord = yield this.adminforth.resource(this.authResource.resourceId).get(Filters.EQ(this.emailField.name, email));
|
|
143
142
|
if (!userRecord) {
|
|
144
143
|
return { error: 'User not found', ok: false };
|
|
145
144
|
}
|
|
145
|
+
if (userRecord[this.options.confirmEmails.emailConfirmedField]) {
|
|
146
|
+
return { error: 'Email already confirmed', ok: false };
|
|
147
|
+
}
|
|
146
148
|
yield this.adminforth.resource(this.authResource.resourceId).update(userRecord[this.authResource.columns.find((col) => col.primaryKey).name], {
|
|
147
|
-
[this.options.confirmEmails.emailConfirmedField]: true
|
|
149
|
+
[this.options.confirmEmails.emailConfirmedField]: true,
|
|
150
|
+
[this.options.passwordHashField]: yield AdminForth.Utils.generatePasswordHash(password),
|
|
148
151
|
});
|
|
149
|
-
return yield this.doLogin(email, response);
|
|
152
|
+
return yield this.doLogin(email, response, { body, headers, query, cookies });
|
|
150
153
|
})
|
|
151
154
|
});
|
|
152
155
|
server.endpoint({
|
|
153
156
|
method: 'POST',
|
|
154
157
|
path: `/plugin/${this.pluginInstanceId}/signup`,
|
|
155
158
|
noAuth: true,
|
|
156
|
-
handler: (_b) => __awaiter(this, [_b], void 0, function* ({ body, response }) {
|
|
159
|
+
handler: (_b) => __awaiter(this, [_b], void 0, function* ({ body, response, headers, query, cookies }) {
|
|
157
160
|
const { email, url, password } = body;
|
|
158
161
|
// validate email
|
|
159
162
|
if (this.emailField.validation) {
|
|
160
163
|
for (const { regExp, message } of this.emailField.validation) {
|
|
161
|
-
if (!new RegExp(regExp).test(
|
|
164
|
+
if (!new RegExp(regExp).test(email)) {
|
|
162
165
|
return { error: message, ok: false };
|
|
163
166
|
}
|
|
164
167
|
}
|
|
165
168
|
}
|
|
166
169
|
// validate password
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
170
|
+
if (!this.options.confirmEmails) {
|
|
171
|
+
if (password.length < this.passwordField.minLength) {
|
|
172
|
+
return { error: `Password must be at least ${this.passwordField.minLength} characters long`, ok: false };
|
|
173
|
+
}
|
|
174
|
+
if (password.length > this.passwordField.maxLength) {
|
|
175
|
+
return { error: `Password must be at most ${this.passwordField.maxLength} characters long`, ok: false };
|
|
176
|
+
}
|
|
177
|
+
if (this.passwordField.validation) {
|
|
178
|
+
for (const { regExp, message } of this.passwordField.validation) {
|
|
179
|
+
if (!new RegExp(regExp).test(password)) {
|
|
180
|
+
return { error: message, ok: false };
|
|
181
|
+
}
|
|
177
182
|
}
|
|
178
183
|
}
|
|
179
184
|
}
|
|
180
185
|
// first check again if email already exists
|
|
181
|
-
const
|
|
182
|
-
if (
|
|
186
|
+
const existingUser = yield this.adminforth.resource(this.authResource.resourceId).get(Filters.EQ(this.emailField.name, email));
|
|
187
|
+
if ((!this.options.confirmEmails && existingUser) || (this.options.confirmEmails && (existingUser === null || existingUser === void 0 ? void 0 : existingUser[this.emailConfirmedField.name]))) {
|
|
183
188
|
return { error: 'Email already exists', ok: false };
|
|
184
189
|
}
|
|
185
190
|
// create user
|
|
186
|
-
|
|
191
|
+
if (!existingUser) {
|
|
192
|
+
const created = yield this.adminforth.resource(this.authResource.resourceId).create(Object.assign(Object.assign(Object.assign({}, (this.options.defaultFieldValues || {})), (this.options.confirmEmails ? { [this.options.confirmEmails.emailConfirmedField]: false } : {})), { [this.emailField.name]: email, [this.options.passwordHashField]: password ? yield AdminForth.Utils.generatePasswordHash(password) : '' }));
|
|
193
|
+
}
|
|
187
194
|
if (!this.options.confirmEmails) {
|
|
188
|
-
const resp = yield this.doLogin(email, response);
|
|
195
|
+
const resp = yield this.doLogin(email, response, { body, headers, query, cookies });
|
|
189
196
|
return resp;
|
|
190
197
|
}
|
|
191
198
|
// send confirmation email
|
|
192
199
|
const brandName = this.adminforth.config.customization.brandName;
|
|
193
200
|
const verifyToken = this.adminforth.auth.issueJWT({ email, issuer: brandName }, 'tempVerifyEmailToken', '2h');
|
|
194
|
-
// send email with AWS SES this.options.providerOptions.AWS_SES
|
|
195
|
-
const ses = new SESClient({
|
|
196
|
-
region: this.options.confirmEmails.providerOptions.AWS_SES.region,
|
|
197
|
-
credentials: {
|
|
198
|
-
accessKeyId: this.options.confirmEmails.providerOptions.AWS_SES.accessKeyId,
|
|
199
|
-
secretAccessKey: this.options.confirmEmails.providerOptions.AWS_SES.secretAccessKey
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
201
|
process.env.HEAVY_DEBUG && console.log('🐛Sending reset tok to', verifyToken);
|
|
203
|
-
const
|
|
204
|
-
Destination: {
|
|
205
|
-
ToAddresses: [email]
|
|
206
|
-
},
|
|
207
|
-
Message: {
|
|
208
|
-
Body: {
|
|
209
|
-
Text: {
|
|
210
|
-
Charset: "UTF-8",
|
|
211
|
-
Data: `
|
|
202
|
+
const emailText = `
|
|
212
203
|
Dear user,
|
|
213
204
|
Welcome to ${brandName}!
|
|
214
205
|
|
|
@@ -222,11 +213,8 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
222
213
|
Thanks,
|
|
223
214
|
The ${brandName} Team
|
|
224
215
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
Html: {
|
|
228
|
-
Charset: "UTF-8",
|
|
229
|
-
Data: `
|
|
216
|
+
`;
|
|
217
|
+
const emailHtml = `
|
|
230
218
|
<html>
|
|
231
219
|
<head></head>
|
|
232
220
|
<body>
|
|
@@ -242,26 +230,10 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
242
230
|
</html>
|
|
243
231
|
|
|
244
232
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
Charset: "UTF-8",
|
|
250
|
-
Data: `Signup request at ${brandName}`
|
|
251
|
-
}
|
|
252
|
-
},
|
|
253
|
-
Source: `${this.options.confirmEmails.sendFrom}`
|
|
254
|
-
});
|
|
255
|
-
try {
|
|
256
|
-
const sRes = yield ses.send(emailCommand);
|
|
257
|
-
}
|
|
258
|
-
catch (e) {
|
|
259
|
-
console.error('Error sending email', e);
|
|
260
|
-
if (process.env.NODE_ENV === 'development') {
|
|
261
|
-
return { error: 'Some thing went wrong, please check the console', ok: false };
|
|
262
|
-
}
|
|
263
|
-
return { error: 'Something went wrong, please contact support', ok: false };
|
|
264
|
-
}
|
|
233
|
+
`;
|
|
234
|
+
const emailSubject = `Signup request at ${brandName}`;
|
|
235
|
+
// send email with AWS SES this.options.providerOptions.AWS_SES
|
|
236
|
+
this.options.confirmEmails.adapter.sendEmail(this.options.confirmEmails.sendFrom, email, emailText, emailHtml, emailSubject);
|
|
265
237
|
return { ok: true };
|
|
266
238
|
})
|
|
267
239
|
});
|
package/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import AdminForth, { AdminForthPlugin, Filters, suggestIfTypo, AdminForthDataTypes } from "adminforth";
|
|
2
|
-
import type { IAdminForth, IHttpServer, AdminForthComponentDeclaration, AdminForthResourceColumn, AdminForthResource, BeforeLoginConfirmationFunction } from "adminforth";
|
|
2
|
+
import type { IAdminForth, IHttpServer, AdminForthComponentDeclaration, AdminForthResourceColumn, AdminForthResource, BeforeLoginConfirmationFunction, HttpExtra } from "adminforth";
|
|
3
3
|
import type { PluginOptions } from './types.js';
|
|
4
|
-
import { SendEmailCommand, SESClient } from '@aws-sdk/client-ses';
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
@@ -25,9 +24,16 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
25
24
|
throw new Error(`emailField is required and should be a name of field in auth resource`);
|
|
26
25
|
}
|
|
27
26
|
|
|
27
|
+
// find field with name resourceConfig.emailField in adminforth.auth.usersResourceId and show error if it doesn't exist
|
|
28
|
+
const authResource = adminforth.config.resources.find(r => r.resourceId === adminforth.config.auth.usersResourceId);
|
|
29
|
+
if (!authResource) {
|
|
30
|
+
throw new Error(`Resource with id config.auth.usersResourceId=${adminforth.config.auth.usersResourceId} not found`);
|
|
31
|
+
}
|
|
32
|
+
this.authResource = authResource;
|
|
33
|
+
|
|
28
34
|
if (this.options.confirmEmails) {
|
|
29
|
-
if (!this.options.confirmEmails.
|
|
30
|
-
throw new Error(`confirmEmails.
|
|
35
|
+
if (!this.options.confirmEmails.adapter) {
|
|
36
|
+
throw new Error(`confirmEmails.adapter is required and should be a name of field in auth resource`);
|
|
31
37
|
}
|
|
32
38
|
if (!this.options.confirmEmails.emailConfirmedField) {
|
|
33
39
|
throw new Error(`confirmEmails.emailConfirmedField is required and should be a name of field in auth resource`);
|
|
@@ -40,18 +46,8 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
40
46
|
`);
|
|
41
47
|
}
|
|
42
48
|
this.emailConfirmedField = emailConfirmedField;
|
|
43
|
-
if (this.emailConfirmedField.type !== AdminForthDataTypes.BOOLEAN) {
|
|
44
|
-
throw new Error(`Field ${this.emailConfirmedField.name} must be of type boolean`);
|
|
45
|
-
}
|
|
46
49
|
}
|
|
47
50
|
|
|
48
|
-
// find field with name resourceConfig.emailField in adminforth.auth.usersResourceId and show error if it doesn't exist
|
|
49
|
-
const authResource = adminforth.config.resources.find(r => r.resourceId === adminforth.config.auth.usersResourceId);
|
|
50
|
-
if (!authResource) {
|
|
51
|
-
throw new Error(`Resource with id config.auth.usersResourceId=${adminforth.config.auth.usersResourceId} not found`);
|
|
52
|
-
}
|
|
53
|
-
this.authResource = authResource;
|
|
54
|
-
|
|
55
51
|
const emailField = authResource.columns.find(f => f.name === this.options.emailField);
|
|
56
52
|
if (!emailField) {
|
|
57
53
|
const similar = suggestIfTypo(authResource.columns.map(f => f.name), this.options.emailField);
|
|
@@ -89,7 +85,8 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
89
85
|
minLength: passwordField.minLength,
|
|
90
86
|
maxLength: passwordField.maxLength,
|
|
91
87
|
validation: passwordField.validation
|
|
92
|
-
}
|
|
88
|
+
},
|
|
89
|
+
requestEmailConfirmation: !!this.options.confirmEmails
|
|
93
90
|
}
|
|
94
91
|
}
|
|
95
92
|
});
|
|
@@ -99,19 +96,16 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
99
96
|
if (!adminforth.config.auth.beforeLoginConfirmation) {
|
|
100
97
|
adminforth.config.auth.beforeLoginConfirmation = [];
|
|
101
98
|
}
|
|
102
|
-
// unshift because if e.g. 2fa set it's hook, this one should be first
|
|
103
|
-
(adminforth.config.auth.beforeLoginConfirmation as BeforeLoginConfirmationFunction[]).unshift(
|
|
104
|
-
async ({ adminUser }) => {
|
|
105
|
-
if (!adminUser.dbUser[this.emailConfirmedField.name]) {
|
|
106
|
-
return { body: { allowedLogin: false }, error: 'You need to confirm your email to be able to login' };
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
);
|
|
110
99
|
}
|
|
111
100
|
}
|
|
112
101
|
|
|
113
102
|
validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
|
|
114
|
-
|
|
103
|
+
if(this.options.confirmEmails){
|
|
104
|
+
const emailConfirmedColumn = this.resourceConfig.columns.find(f => f.name === this.options.confirmEmails.emailConfirmedField);
|
|
105
|
+
if (emailConfirmedColumn.type !== AdminForthDataTypes.BOOLEAN) {
|
|
106
|
+
throw new Error(`Field ${this.emailConfirmedField.name} must be of type boolean`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
115
109
|
}
|
|
116
110
|
|
|
117
111
|
instanceUniqueRepresentation(pluginOptions: any) : string {
|
|
@@ -120,7 +114,7 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
120
114
|
return `single`;
|
|
121
115
|
}
|
|
122
116
|
|
|
123
|
-
async doLogin(email: string, response: any): Promise<{ error?: string; allowedLogin: boolean; redirectTo?: string; }> {
|
|
117
|
+
async doLogin(email: string, response: any, extra: HttpExtra): Promise<{ error?: string; allowedLogin: boolean; redirectTo?: string; }> {
|
|
124
118
|
|
|
125
119
|
const username = email;
|
|
126
120
|
const userRecord = await this.adminforth.resource(this.authResource.resourceId).get(Filters.EQ(this.emailField.name, email));
|
|
@@ -131,7 +125,7 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
131
125
|
};
|
|
132
126
|
const toReturn = { allowedLogin: true, error: '' };
|
|
133
127
|
|
|
134
|
-
await this.adminforth.restApi.processLoginCallbacks(adminUser, toReturn, response);
|
|
128
|
+
await this.adminforth.restApi.processLoginCallbacks(adminUser, toReturn, response, extra);
|
|
135
129
|
if (toReturn.allowedLogin) {
|
|
136
130
|
this.adminforth.auth.setAuthCookie({
|
|
137
131
|
response,
|
|
@@ -149,22 +143,32 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
149
143
|
setupEndpoints(server: IHttpServer) {
|
|
150
144
|
server.endpoint({
|
|
151
145
|
method: 'POST',
|
|
152
|
-
path: `/plugin/${this.pluginInstanceId}/
|
|
146
|
+
path: `/plugin/${this.pluginInstanceId}/complete-verified-signup`,
|
|
153
147
|
noAuth: true,
|
|
154
|
-
handler: async ({ body, response }) => {
|
|
155
|
-
const { token } = body;
|
|
156
|
-
const { email } = await this.adminforth.auth.verify(token, 'tempVerifyEmailToken');
|
|
148
|
+
handler: async ({ body, response, headers, query, cookies }) => {
|
|
149
|
+
const { token, password } = body;
|
|
150
|
+
const { email } = await this.adminforth.auth.verify(token, 'tempVerifyEmailToken', false);
|
|
157
151
|
if (!email) {
|
|
158
152
|
return { error: 'Invalid token', ok: false };
|
|
159
153
|
}
|
|
154
|
+
|
|
155
|
+
if(!password) {
|
|
156
|
+
return { error: 'Password is required', ok: false };
|
|
157
|
+
}
|
|
160
158
|
const userRecord = await this.adminforth.resource(this.authResource.resourceId).get(Filters.EQ(this.emailField.name, email));
|
|
161
159
|
if (!userRecord) {
|
|
162
160
|
return { error: 'User not found', ok: false };
|
|
163
161
|
}
|
|
162
|
+
|
|
163
|
+
if (userRecord[this.options.confirmEmails.emailConfirmedField]) {
|
|
164
|
+
return { error: 'Email already confirmed', ok: false };
|
|
165
|
+
}
|
|
166
|
+
|
|
164
167
|
await this.adminforth.resource(this.authResource.resourceId).update(userRecord[this.authResource.columns.find((col) => col.primaryKey).name], {
|
|
165
|
-
[this.options.confirmEmails.emailConfirmedField]: true
|
|
168
|
+
[this.options.confirmEmails.emailConfirmedField]: true,
|
|
169
|
+
[this.options.passwordHashField]: await AdminForth.Utils.generatePasswordHash(password),
|
|
166
170
|
});
|
|
167
|
-
return await this.doLogin(email, response);
|
|
171
|
+
return await this.doLogin(email, response, { body, headers, query, cookies });
|
|
168
172
|
}
|
|
169
173
|
});
|
|
170
174
|
|
|
@@ -172,49 +176,53 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
172
176
|
method: 'POST',
|
|
173
177
|
path: `/plugin/${this.pluginInstanceId}/signup`,
|
|
174
178
|
noAuth: true,
|
|
175
|
-
handler: async ({ body, response }) => {
|
|
179
|
+
handler: async ({ body, response, headers, query, cookies }) => {
|
|
176
180
|
const { email, url, password } = body;
|
|
177
181
|
|
|
178
182
|
// validate email
|
|
179
183
|
if (this.emailField.validation) {
|
|
180
184
|
for (const { regExp, message } of this.emailField.validation) {
|
|
181
|
-
if (!new RegExp(regExp).test(
|
|
185
|
+
if (!new RegExp(regExp).test(email)) {
|
|
182
186
|
return { error: message, ok: false };
|
|
183
187
|
}
|
|
184
188
|
}
|
|
185
189
|
}
|
|
186
190
|
|
|
187
191
|
// validate password
|
|
188
|
-
if (
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
192
|
+
if (!this.options.confirmEmails) {
|
|
193
|
+
if (password.length < this.passwordField.minLength) {
|
|
194
|
+
return { error: `Password must be at least ${this.passwordField.minLength} characters long`, ok: false };
|
|
195
|
+
}
|
|
196
|
+
if (password.length > this.passwordField.maxLength) {
|
|
197
|
+
return { error: `Password must be at most ${this.passwordField.maxLength} characters long`, ok: false };
|
|
198
|
+
}
|
|
199
|
+
if (this.passwordField.validation) {
|
|
200
|
+
for (const { regExp, message } of this.passwordField.validation) {
|
|
201
|
+
if (!new RegExp(regExp).test(password)) {
|
|
202
|
+
return { error: message, ok: false };
|
|
203
|
+
}
|
|
198
204
|
}
|
|
199
205
|
}
|
|
200
206
|
}
|
|
201
207
|
|
|
202
208
|
// first check again if email already exists
|
|
203
|
-
const
|
|
204
|
-
if (
|
|
209
|
+
const existingUser = await this.adminforth.resource(this.authResource.resourceId).get(Filters.EQ(this.emailField.name, email));
|
|
210
|
+
if ((!this.options.confirmEmails && existingUser) || (this.options.confirmEmails && existingUser?.[this.emailConfirmedField.name])) {
|
|
205
211
|
return { error: 'Email already exists', ok: false };
|
|
206
212
|
}
|
|
207
213
|
|
|
208
214
|
// create user
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
+
if (!existingUser) {
|
|
216
|
+
const created = await this.adminforth.resource(this.authResource.resourceId).create({
|
|
217
|
+
...(this.options.defaultFieldValues || {}),
|
|
218
|
+
...(this.options.confirmEmails ? { [this.options.confirmEmails.emailConfirmedField]: false } : {}),
|
|
219
|
+
[this.emailField.name]: email,
|
|
220
|
+
[this.options.passwordHashField]: password ? await AdminForth.Utils.generatePasswordHash(password) : '',
|
|
221
|
+
});
|
|
222
|
+
}
|
|
215
223
|
|
|
216
224
|
if (!this.options.confirmEmails) {
|
|
217
|
-
const resp = await this.doLogin(email, response);
|
|
225
|
+
const resp = await this.doLogin(email, response, { body, headers, query, cookies });
|
|
218
226
|
return resp;
|
|
219
227
|
}
|
|
220
228
|
|
|
@@ -223,27 +231,8 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
223
231
|
const brandName = this.adminforth.config.customization.brandName;
|
|
224
232
|
|
|
225
233
|
const verifyToken = this.adminforth.auth.issueJWT({email, issuer: brandName }, 'tempVerifyEmailToken', '2h');
|
|
226
|
-
|
|
227
|
-
// send email with AWS SES this.options.providerOptions.AWS_SES
|
|
228
|
-
const ses = new SESClient({
|
|
229
|
-
region: this.options.confirmEmails.providerOptions.AWS_SES.region,
|
|
230
|
-
credentials: {
|
|
231
|
-
accessKeyId: this.options.confirmEmails.providerOptions.AWS_SES.accessKeyId,
|
|
232
|
-
secretAccessKey: this.options.confirmEmails.providerOptions.AWS_SES.secretAccessKey
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
|
|
236
234
|
process.env.HEAVY_DEBUG && console.log('🐛Sending reset tok to', verifyToken);
|
|
237
|
-
|
|
238
|
-
const emailCommand = new SendEmailCommand({
|
|
239
|
-
Destination: {
|
|
240
|
-
ToAddresses: [email]
|
|
241
|
-
},
|
|
242
|
-
Message: {
|
|
243
|
-
Body: {
|
|
244
|
-
Text: {
|
|
245
|
-
Charset: "UTF-8",
|
|
246
|
-
Data: `
|
|
235
|
+
const emailText = `
|
|
247
236
|
Dear user,
|
|
248
237
|
Welcome to ${brandName}!
|
|
249
238
|
|
|
@@ -257,11 +246,9 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
257
246
|
Thanks,
|
|
258
247
|
The ${brandName} Team
|
|
259
248
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
Charset: "UTF-8",
|
|
264
|
-
Data: `
|
|
249
|
+
`;
|
|
250
|
+
|
|
251
|
+
const emailHtml = `
|
|
265
252
|
<html>
|
|
266
253
|
<head></head>
|
|
267
254
|
<body>
|
|
@@ -277,26 +264,11 @@ export default class OpenSignupPlugin extends AdminForthPlugin {
|
|
|
277
264
|
</html>
|
|
278
265
|
|
|
279
266
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
},
|
|
283
|
-
Subject: {
|
|
284
|
-
Charset: "UTF-8",
|
|
285
|
-
Data: `Signup request at ${brandName}`
|
|
286
|
-
}
|
|
287
|
-
},
|
|
288
|
-
Source: `${this.options.confirmEmails.sendFrom}`
|
|
289
|
-
});
|
|
267
|
+
`;
|
|
268
|
+
const emailSubject = `Signup request at ${brandName}`;
|
|
290
269
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
} catch (e) {
|
|
294
|
-
console.error('Error sending email', e);
|
|
295
|
-
if (process.env.NODE_ENV === 'development') {
|
|
296
|
-
return { error: 'Some thing went wrong, please check the console', ok: false };
|
|
297
|
-
}
|
|
298
|
-
return { error: 'Something went wrong, please contact support', ok: false };
|
|
299
|
-
}
|
|
270
|
+
// send email with AWS SES this.options.providerOptions.AWS_SES
|
|
271
|
+
this.options.confirmEmails.adapter.sendEmail(this.options.confirmEmails.sendFrom, email, emailText, emailHtml, emailSubject);
|
|
300
272
|
|
|
301
273
|
return { ok: true };
|
|
302
274
|
}
|
package/package.json
CHANGED
package/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { EmailAdapter } from 'adminforth';
|
|
2
|
+
|
|
1
3
|
export interface PluginOptions {
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -40,11 +42,6 @@ export interface PluginOptions {
|
|
|
40
42
|
*/
|
|
41
43
|
emailConfirmedField: string;
|
|
42
44
|
|
|
43
|
-
/**
|
|
44
|
-
* Required to confirm email
|
|
45
|
-
*/
|
|
46
|
-
emailProvider: 'AWS_SES' ;
|
|
47
|
-
|
|
48
45
|
/**
|
|
49
46
|
* From which email to send password reset emails
|
|
50
47
|
* e.g. no-reply@example.com
|
|
@@ -53,15 +50,9 @@ export interface PluginOptions {
|
|
|
53
50
|
sendFrom: string;
|
|
54
51
|
|
|
55
52
|
/**
|
|
56
|
-
*
|
|
53
|
+
* Adapter to send emails
|
|
57
54
|
*/
|
|
58
|
-
|
|
59
|
-
AWS_SES: {
|
|
60
|
-
region: string;
|
|
61
|
-
accessKeyId: string;
|
|
62
|
-
secretAccessKey: string;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
55
|
+
adapter: EmailAdapter;
|
|
65
56
|
}
|
|
66
57
|
|
|
67
58
|
|