Skip to content

Commit d5b4757

Browse files
authored
Merge 18b920d into 0ce3692
2 parents 0ce3692 + 18b920d commit d5b4757

File tree

6 files changed

+88
-74
lines changed

6 files changed

+88
-74
lines changed

spec/ValidationAndPasswordsReset.spec.js

+51-50
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
242242
});
243243
});
244244

245-
it('allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true', done => {
246-
const user = new Parse.User();
245+
it('allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true', async () => {
247246
let sendEmailOptions;
248247
const emailAdapter = {
249248
sendVerificationEmail: options => {
@@ -252,59 +251,32 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
252251
sendPasswordResetEmail: () => Promise.resolve(),
253252
sendMail: () => {},
254253
};
255-
reconfigureServer({
254+
await reconfigureServer({
256255
appName: 'emailing app',
257256
verifyUserEmails: true,
258257
preventLoginWithUnverifiedEmail: true,
259258
emailAdapter: emailAdapter,
260259
publicServerURL: 'http://localhost:8378/1',
261-
})
262-
.then(() => {
263-
user.setPassword('other-password');
264-
user.setUsername('user');
265-
user.set('email', '[email protected]');
266-
return user.signUp();
267-
})
268-
.then(() => {
269-
expect(sendEmailOptions).not.toBeUndefined();
270-
request({
271-
url: sendEmailOptions.link,
272-
followRedirects: false,
273-
}).then(response => {
274-
expect(response.status).toEqual(302);
275-
expect(response.text).toEqual(
276-
'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html?username=user'
277-
);
278-
user
279-
.fetch({ useMasterKey: true })
280-
.then(
281-
() => {
282-
expect(user.get('emailVerified')).toEqual(true);
283-
284-
Parse.User.logIn('user', 'other-password').then(
285-
user => {
286-
expect(typeof user).toBe('object');
287-
expect(user.get('emailVerified')).toBe(true);
288-
done();
289-
},
290-
() => {
291-
fail('login should have succeeded');
292-
done();
293-
}
294-
);
295-
},
296-
err => {
297-
jfail(err);
298-
fail('this should not fail');
299-
done();
300-
}
301-
)
302-
.catch(err => {
303-
jfail(err);
304-
done();
305-
});
306-
});
307-
});
260+
});
261+
let user = new Parse.User();
262+
user.setPassword('other-password');
263+
user.setUsername('user');
264+
user.set('email', '[email protected]');
265+
await user.signUp();
266+
expect(sendEmailOptions).not.toBeUndefined();
267+
const response = await request({
268+
url: sendEmailOptions.link,
269+
followRedirects: false,
270+
});
271+
expect(response.status).toEqual(302);
272+
expect(response.text).toEqual(
273+
'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html?username=user'
274+
);
275+
user = await new Parse.Query(Parse.User).first({ useMasterKey: true });
276+
expect(user.get('emailVerified')).toEqual(true);
277+
user = await Parse.User.logIn('user', 'other-password');
278+
expect(typeof user).toBe('object');
279+
expect(user.get('emailVerified')).toBe(true);
308280
});
309281

310282
it('allows user to login if email is not verified but preventLoginWithUnverifiedEmail is set to false', done => {
@@ -345,6 +317,35 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
345317
});
346318
});
347319

320+
it('does not allow signup with preventSignupWithUnverified', async () => {
321+
let sendEmailOptions;
322+
const emailAdapter = {
323+
sendVerificationEmail: options => {
324+
sendEmailOptions = options;
325+
},
326+
sendPasswordResetEmail: () => Promise.resolve(),
327+
sendMail: () => {},
328+
};
329+
await reconfigureServer({
330+
appName: 'test',
331+
publicServerURL: 'http://localhost:1337/1',
332+
verifyUserEmails: true,
333+
preventLoginWithUnverifiedEmail: true,
334+
preventSignupWithUnverifiedEmail: true,
335+
emailAdapter,
336+
});
337+
const newUser = new Parse.User();
338+
newUser.setPassword('asdf');
339+
newUser.setUsername('zxcv');
340+
newUser.set('email', '[email protected]');
341+
await expectAsync(newUser.signUp()).toBeRejectedWith(
342+
new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.')
343+
);
344+
const user = await new Parse.Query(Parse.User).first({ useMasterKey: true });
345+
expect(user).toBeDefined();
346+
expect(sendEmailOptions).toBeDefined();
347+
});
348+
348349
it('fails if you include an emailAdapter, set a publicServerURL, but have no appName and send a password reset email', done => {
349350
reconfigureServer({
350351
appName: undefined,

spec/VerifyUserPassword.spec.js

+17-23
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,9 @@ describe('Verify User Password', () => {
353353
done();
354354
});
355355
});
356-
it('fails to verify password when preventLoginWithUnverifiedEmail is set to true REST API', done => {
357-
reconfigureServer({
356+
357+
it('fails to verify password when preventLoginWithUnverifiedEmail is set to true REST API', async () => {
358+
await reconfigureServer({
358359
publicServerURL: 'http://localhost:8378/',
359360
appName: 'emailVerify',
360361
verifyUserEmails: true,
@@ -364,28 +365,21 @@ describe('Verify User Password', () => {
364365
apiKey: 'k',
365366
domain: 'd',
366367
}),
367-
})
368-
.then(() => {
369-
const user = new Parse.User();
370-
return user.save({
371-
username: 'unverified-user',
372-
password: 'mypass',
373-
374-
});
375-
})
376-
.then(() => {
377-
return verifyPassword('[email protected]', 'mypass', true);
378-
})
379-
.then(res => {
380-
expect(res.status).toBe(400);
381-
expect(res.text).toMatch('{"code":205,"error":"User email is not verified."}');
382-
done();
383-
})
384-
.catch(err => {
385-
fail(err);
386-
done();
387-
});
368+
});
369+
const user = new Parse.User();
370+
await user.save({
371+
username: 'unverified-user',
372+
password: 'mypass',
373+
374+
});
375+
const res = await verifyPassword('[email protected]', 'mypass', true);
376+
expect(res.status).toBe(400);
377+
expect(res.data).toEqual({
378+
code: Parse.Error.EMAIL_NOT_FOUND,
379+
error: 'User email is not verified.',
380+
});
388381
});
382+
389383
it('verify password lock account if failed verify password attempts are above threshold', done => {
390384
reconfigureServer({
391385
appName: 'lockout threshold',

src/Options/Definitions.js

+7
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,13 @@ module.exports.ParseServerOptions = {
412412
action: parsers.booleanParser,
413413
default: false,
414414
},
415+
preventSignupWithUnverifiedEmail: {
416+
env: 'PARSE_SERVER_PREVENT_SIGNUP_WITH_UNVERIFIED_EMAIL',
417+
help:
418+
"If set to `true` it prevents a user from signing up if the email has not yet been verified and email verification is required. In that case the server responds to the sign-up with HTTP status 400 and a Parse Error 205 `EMAIL_NOT_FOUND`. If set to `false` the server responds with HTTP status 200, and client SDKs return an unauthenticated Parse User without session token. In that case subsequent requests fail until the user's email address is verified.<br><br>Default is `false`.<br>Requires option `verifyUserEmails: true`.",
419+
action: parsers.booleanParser,
420+
default: false,
421+
},
415422
protectedFields: {
416423
env: 'PARSE_SERVER_PROTECTED_FIELDS',
417424
help: 'Protected fields that should be treated with extra security when fetching details.',

src/Options/docs.js

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Options/index.js

+7
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,13 @@ export interface ParseServerOptions {
165165
Requires option `verifyUserEmails: true`.
166166
:DEFAULT: false */
167167
preventLoginWithUnverifiedEmail: ?boolean;
168+
/* If set to `true` it prevents a user from signing up if the email has not yet been verified and email verification is required. In that case the server responds to the sign-up with HTTP status 400 and a Parse Error 205 `EMAIL_NOT_FOUND`. If set to `false` the server responds with HTTP status 200, and client SDKs return an unauthenticated Parse User without session token. In that case subsequent requests fail until the user's email address is verified.
169+
<br><br>
170+
Default is `false`.
171+
<br>
172+
Requires option `verifyUserEmails: true`.
173+
:DEFAULT: false */
174+
preventSignupWithUnverifiedEmail: ?boolean;
168175
/* Set the validity duration of the email verification token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.
169176
<br><br>
170177
For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

src/RestWrite.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ RestWrite.prototype.execute = function () {
160160
this.response.response.authDataResponse = this.authDataResponse;
161161
}
162162
}
163+
if (this.storage.rejectSignup && this.config.preventSignupWithUnverifiedEmail) {
164+
throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.');
165+
}
163166
return this.response;
164167
});
165168
};
@@ -879,7 +882,8 @@ RestWrite.prototype.createSessionTokenIfNeeded = function () {
879882
this.config.verifyUserEmails
880883
) {
881884
// verification is on
882-
return; // do not create the session token in that case!
885+
this.storage.rejectSignup = true;
886+
return;
883887
}
884888
return this.createSessionToken();
885889
};

0 commit comments

Comments
 (0)