|
78 | 78 | */
|
79 | 79 |
|
80 | 80 | import { TOTP, Secret } from 'otpauth';
|
| 81 | +import crypto from 'crypto'; |
81 | 82 | import { randomString } from '../../cryptoUtils';
|
82 | 83 | import AuthAdapter from './AuthAdapter';
|
83 | 84 | class MFAAdapter extends AuthAdapter {
|
@@ -149,17 +150,32 @@ class MFAAdapter extends AuthAdapter {
|
149 | 150 | if (this.email && email) {
|
150 | 151 | if (token === 'request') {
|
151 | 152 | const { token: sendToken, expiry } = await this.sendEmail(email);
|
152 |
| - auth.mfa.token = sendToken; |
153 |
| - auth.mfa.expiry = expiry; |
154 |
| - req.object.set('authData', auth); |
155 |
| - await req.object.save(null, { useMasterKey: true }); |
156 |
| - throw 'Please enter the token'; |
| 153 | + const auth = req.original.get('authData') || {}; |
| 154 | + auth.mfa = { |
| 155 | + token: sendToken, |
| 156 | + email: email, |
| 157 | + expiry: expiry |
| 158 | + }; |
| 159 | + |
| 160 | + // Use direct database access to avoid validation |
| 161 | + const query = new Parse.Query(Parse.User); |
| 162 | + const user = await query.get(req.object.id, { useMasterKey: true }); |
| 163 | + user.set('authData', auth); |
| 164 | + |
| 165 | + // Skip validation and hooks |
| 166 | + await user.save(null, { |
| 167 | + useMasterKey: true, |
| 168 | + context: { skipValidation: true }, |
| 169 | + validateSave: false |
| 170 | + }); |
| 171 | + |
| 172 | + throw new Parse.Error(209,'Please enter the token'); |
157 | 173 | }
|
158 | 174 | if (!saved || token !== saved) {
|
159 | 175 | throw 'Invalid MFA token 1';
|
160 | 176 | }
|
161 | 177 | if (new Date() > expiry) {
|
162 |
| - throw 'Invalid MFA token 2'; |
| 178 | + throw 'Expired MFA token'; |
163 | 179 | }
|
164 | 180 | delete auth.mfa.token;
|
165 | 181 | delete auth.mfa.expiry;
|
@@ -283,10 +299,12 @@ class MFAAdapter extends AuthAdapter {
|
283 | 299 |
|
284 | 300 | async setupEmailOTP(email) {
|
285 | 301 | const { token, expiry } = await this.sendEmail(email);
|
| 302 | + const emailHash = this.md5Hash(email); |
286 | 303 | return {
|
287 | 304 | save: {
|
288 | 305 | pending: {
|
289 |
| - [email]: { |
| 306 | + // encode the email md5 has |
| 307 | + [emailHash]: { |
290 | 308 | token,
|
291 | 309 | expiry,
|
292 | 310 | },
|
@@ -344,17 +362,18 @@ class MFAAdapter extends AuthAdapter {
|
344 | 362 |
|
345 | 363 | async confirmEmailOTP(inputData, authData) {
|
346 | 364 | const { email, token } = inputData;
|
347 |
| - if (!authData.pending?.[email]) { |
| 365 | + const emailHash = this.md5Hash(email); |
| 366 | + if (!authData.pending?.[emailHash]) { |
348 | 367 | throw 'This email is not pending';
|
349 | 368 | }
|
350 |
| - const pendingData = authData.pending[email]; |
| 369 | + const pendingData = authData.pending[emailHash]; |
351 | 370 | if (token !== pendingData.token) {
|
352 | 371 | throw 'Invalid MFA token';
|
353 | 372 | }
|
354 | 373 | if (new Date() > pendingData.expiry) {
|
355 | 374 | throw 'Invalid MFA token';
|
356 | 375 | }
|
357 |
| - delete authData.pending[email]; |
| 376 | + delete authData.pending[emailHash]; |
358 | 377 | authData.email = email;
|
359 | 378 | return {
|
360 | 379 | save: authData,
|
@@ -384,5 +403,8 @@ class MFAAdapter extends AuthAdapter {
|
384 | 403 | save: { secret, recovery },
|
385 | 404 | };
|
386 | 405 | }
|
| 406 | + md5Hash(str) { |
| 407 | + return crypto.createHash('md5').update(str).digest('hex'); |
| 408 | + } |
387 | 409 | }
|
388 | 410 | export default new MFAAdapter();
|
0 commit comments