Skip to content

Commit 681016d

Browse files
Add Email Verification and Verify Password (#1144)
* Add missing methods from user routes * Remove unsafe current user * Refactor verifyPassword * Add static method test for verifyPassword * fix conflicts Co-authored-by: Diamond Lewis <[email protected]>
1 parent bc5bbd1 commit 681016d

File tree

5 files changed

+184
-0
lines changed

5 files changed

+184
-0
lines changed

integration/test/ParseUserTest.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,34 @@ describe('Parse User', () => {
936936
expect(user.get('authData').facebook.id).toBe('test');
937937
});
938938

939+
it('can verify user password via static method', async () => {
940+
await Parse.User.signUp('asd123', 'xyz123');
941+
const res = await Parse.User.verifyPassword('asd123', 'xyz123');
942+
expect(typeof res).toBe('object');
943+
expect(res.username).toBe('asd123');
944+
945+
try {
946+
await Parse.User.verifyPassword('asd123', 'wrong password');
947+
} catch (error) {
948+
expect(error.code).toBe(101);
949+
expect(error.message).toBe('Invalid username/password.');
950+
}
951+
});
952+
953+
it('can verify user password via instance method', async () => {
954+
const user = await Parse.User.signUp('asd123', 'xyz123');
955+
const res = await user.verifyPassword('xyz123');
956+
expect(typeof res).toBe('object');
957+
expect(res.username).toBe('asd123');
958+
959+
try {
960+
await user.verifyPassword('wrong password');
961+
} catch (error) {
962+
expect(error.code).toBe(101);
963+
expect(error.message).toBe('Invalid username/password.');
964+
}
965+
});
966+
939967
it('can encrypt user', async () => {
940968
Parse.User.enableUnsafeCurrentUser();
941969
Parse.enableEncryptedUser();

src/CoreManager.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ type UserController = {
132132
upgradeToRevocableSession: (user: ParseUser, options: RequestOptions) => Promise;
133133
linkWith: (user: ParseUser, authData: AuthData) => Promise;
134134
removeUserFromDisk: () => Promise;
135+
verifyPassword: (username: string, password: string, options: RequestOptions) => Promise;
136+
requestEmailVerification: (email: string, options: RequestOptions) => Promise;
135137
};
136138
type HooksController = {
137139
get: (type: string, functionName?: string, triggerName?: string) => Promise;
@@ -417,6 +419,8 @@ module.exports = {
417419
'me',
418420
'requestPasswordReset',
419421
'upgradeToRevocableSession',
422+
'requestEmailVerification',
423+
'verifyPassword',
420424
'linkWith',
421425
], controller);
422426
config['UserController'] = controller;

src/ParseUser.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,20 @@ class ParseUser extends ParseObject {
506506
});
507507
}
508508

509+
/**
510+
* Verify whether a given password is the password of the current user.
511+
*
512+
* @param {String} password A password to be verified
513+
* @param {Object} options
514+
* @return {Promise} A promise that is fulfilled with a user
515+
* when the password is correct.
516+
*/
517+
verifyPassword(password: string, options?: RequestOptions): Promise<ParseUser> {
518+
const username = this.getUsername() || '';
519+
520+
return ParseUser.verifyPassword(username, password, options);
521+
}
522+
509523
static readOnlyAttributes() {
510524
return ['sessionToken'];
511525
}
@@ -761,6 +775,69 @@ class ParseUser extends ParseObject {
761775
);
762776
}
763777

778+
/**
779+
* Request an email verification.
780+
*
781+
* <p>Calls options.success or options.error on completion.</p>
782+
*
783+
* @param {String} email The email address associated with the user that
784+
* forgot their password.
785+
* @param {Object} options
786+
* @static
787+
* @returns {Promise}
788+
*/
789+
static requestEmailVerification(email: string, options?: RequestOptions) {
790+
options = options || {};
791+
792+
const requestOptions = {};
793+
if (options.hasOwnProperty('useMasterKey')) {
794+
requestOptions.useMasterKey = options.useMasterKey;
795+
}
796+
797+
const controller = CoreManager.getUserController();
798+
return controller.requestEmailVerification(email, requestOptions);
799+
}
800+
801+
/**
802+
* Verify whether a given password is the password of the current user.
803+
*
804+
* @param {String} username A username to be used for identificaiton
805+
* @param {String} password A password to be verified
806+
* @param {Object} options
807+
* @static
808+
* @returns {Promise} A promise that is fulfilled with a user
809+
* when the password is correct.
810+
*/
811+
static verifyPassword(username: string, password: string, options?: RequestOptions) {
812+
if (typeof username !== 'string') {
813+
return Promise.reject(
814+
new ParseError(
815+
ParseError.OTHER_CAUSE,
816+
'Username must be a string.'
817+
)
818+
);
819+
}
820+
821+
if (typeof password !== 'string') {
822+
return Promise.reject(
823+
new ParseError(
824+
ParseError.OTHER_CAUSE,
825+
'Password must be a string.'
826+
)
827+
);
828+
}
829+
830+
options = options || {};
831+
832+
const verificationOption = {};
833+
if (options.hasOwnProperty('useMasterKey')) {
834+
verificationOption.useMasterKey = options.useMasterKey;
835+
}
836+
837+
const controller = CoreManager.getUserController();
838+
return controller.verifyPassword(username, password, verificationOption);
839+
}
840+
764841
/**
765842
* Allow someone to define a custom User class without className
766843
* being rewritten to _User. The default behavior is to rewrite
@@ -1160,6 +1237,26 @@ const DefaultController = {
11601237
}
11611238
return user;
11621239
});
1240+
},
1241+
1242+
verifyPassword(username: string, password: string, options: RequestOptions) {
1243+
const RESTController = CoreManager.getRESTController();
1244+
return RESTController.request(
1245+
'GET',
1246+
'verifyPassword',
1247+
{ username, password },
1248+
options
1249+
);
1250+
},
1251+
1252+
requestEmailVerification(email: string, options: RequestOptions) {
1253+
const RESTController = CoreManager.getRESTController();
1254+
return RESTController.request(
1255+
'POST',
1256+
'verificationEmailRequest',
1257+
{ email: email },
1258+
options
1259+
);
11631260
}
11641261
};
11651262

src/__tests__/ParseUser-test.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,4 +1201,55 @@ describe('ParseUser', () => {
12011201
expect(user.isCurrent()).toBe(false);
12021202
expect(user.existed()).toBe(true);
12031203
});
1204+
1205+
it('can verify user password', async () => {
1206+
ParseUser.enableUnsafeCurrentUser();
1207+
ParseUser._clearCache();
1208+
CoreManager.setRESTController({
1209+
request() {
1210+
return Promise.resolve({
1211+
objectId: 'uid2',
1212+
username: 'username',
1213+
sessionToken: '123abc'
1214+
}, 200);
1215+
},
1216+
ajax() {}
1217+
});
1218+
const user = await ParseUser.verifyPassword('username', 'password');
1219+
expect(user.objectId).toBe('uid2');
1220+
expect(user.username).toBe('username');
1221+
1222+
CoreManager.setRESTController({
1223+
request() {
1224+
const parseError = new ParseError(
1225+
ParseError.OBJECT_NOT_FOUND,
1226+
'Invalid username/password.'
1227+
);
1228+
return Promise.reject(parseError);
1229+
},
1230+
ajax() {}
1231+
});
1232+
1233+
try {
1234+
await ParseUser.verifyPassword('username','wrong password');
1235+
} catch(error) {
1236+
expect(error.code).toBe(101);
1237+
expect(error.message).toBe('Invalid username/password.');
1238+
}
1239+
});
1240+
1241+
it('can send an email verification request', () => {
1242+
CoreManager.setRESTController({
1243+
request(method, path, body) {
1244+
expect(method).toBe('POST');
1245+
expect(path).toBe("verificationEmailRequest");
1246+
expect(body).toEqual({ email: "[email protected]" });
1247+
1248+
return Promise.resolve({}, 200);
1249+
},
1250+
ajax() {}
1251+
});
1252+
1253+
ParseUser.requestEmailVerification("[email protected]");
1254+
});
12041255
});

src/__tests__/RESTController-test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ describe('RESTController', () => {
287287
requestPasswordReset() {},
288288
upgradeToRevocableSession() {},
289289
linkWith() {},
290+
requestEmailVerification() {},
291+
verifyPassword() {},
290292
});
291293

292294
const xhr = {
@@ -323,6 +325,8 @@ describe('RESTController', () => {
323325
requestPasswordReset() {},
324326
upgradeToRevocableSession() {},
325327
linkWith() {},
328+
requestEmailVerification() {},
329+
verifyPassword() {},
326330
});
327331

328332
const xhr = {

0 commit comments

Comments
 (0)