Skip to content

Commit 72888bc

Browse files
authored
Adds ability to login with email when specifying it (#4276)
* Adds ability to login with email when specifying it * Adds tests for corner cases * nits
1 parent 6685932 commit 72888bc

File tree

2 files changed

+179
-9
lines changed

2 files changed

+179
-9
lines changed

spec/ParseUser.spec.js

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3224,4 +3224,166 @@ describe('Parse.User testing', () => {
32243224
.then(done)
32253225
.catch(done.fail);
32263226
});
3227+
3228+
it('can login with email', (done) => {
3229+
const user = new Parse.User();
3230+
user.save({
3231+
username: 'yolo',
3232+
password: 'yolopass',
3233+
3234+
}).then(() => {
3235+
const options = {
3236+
url: `http://localhost:8378/1/login`,
3237+
headers: {
3238+
'X-Parse-Application-Id': Parse.applicationId,
3239+
'X-Parse-REST-API-Key': 'rest',
3240+
},
3241+
json: { email: "[email protected]", password: 'yolopass'}
3242+
}
3243+
return rp.get(options);
3244+
}).then(done).catch(done.fail);
3245+
});
3246+
3247+
it('cannot login with email and invalid password', (done) => {
3248+
const user = new Parse.User();
3249+
user.save({
3250+
username: 'yolo',
3251+
password: 'yolopass',
3252+
3253+
}).then(() => {
3254+
const options = {
3255+
url: `http://localhost:8378/1/login`,
3256+
headers: {
3257+
'X-Parse-Application-Id': Parse.applicationId,
3258+
'X-Parse-REST-API-Key': 'rest',
3259+
},
3260+
json: { email: "[email protected]", password: 'yolopass2'}
3261+
}
3262+
return rp.get(options);
3263+
}).then(done.fail).catch(done);
3264+
});
3265+
3266+
it('can login with email through query string', (done) => {
3267+
const user = new Parse.User();
3268+
user.save({
3269+
username: 'yolo',
3270+
password: 'yolopass',
3271+
3272+
}).then(() => {
3273+
const options = {
3274+
url: `http://localhost:8378/1/[email protected]&password=yolopass`,
3275+
headers: {
3276+
'X-Parse-Application-Id': Parse.applicationId,
3277+
'X-Parse-REST-API-Key': 'rest',
3278+
},
3279+
}
3280+
return rp.get(options);
3281+
}).then(done).catch(done.fail);
3282+
});
3283+
3284+
it('can login when both email and username are passed', (done) => {
3285+
const user = new Parse.User();
3286+
user.save({
3287+
username: 'yolo',
3288+
password: 'yolopass',
3289+
3290+
}).then(() => {
3291+
const options = {
3292+
url: `http://localhost:8378/1/[email protected]&username=yolo&password=yolopass`,
3293+
headers: {
3294+
'X-Parse-Application-Id': Parse.applicationId,
3295+
'X-Parse-REST-API-Key': 'rest',
3296+
},
3297+
}
3298+
return rp.get(options);
3299+
}).then(done).catch(done.fail);
3300+
});
3301+
3302+
it("fails to login when username doesn't match email", (done) => {
3303+
const user = new Parse.User();
3304+
user.save({
3305+
username: 'yolo',
3306+
password: 'yolopass',
3307+
3308+
}).then(() => {
3309+
const options = {
3310+
url: `http://localhost:8378/1/[email protected]&username=yolo2&password=yolopass`,
3311+
headers: {
3312+
'X-Parse-Application-Id': Parse.applicationId,
3313+
'X-Parse-REST-API-Key': 'rest',
3314+
},
3315+
json: true,
3316+
}
3317+
return rp.get(options);
3318+
}).then(done.fail).catch((err) => {
3319+
expect(err.response.body.error).toEqual('Invalid username/password.');
3320+
done();
3321+
});
3322+
});
3323+
3324+
it("fails to login when email doesn't match username", (done) => {
3325+
const user = new Parse.User();
3326+
user.save({
3327+
username: 'yolo',
3328+
password: 'yolopass',
3329+
3330+
}).then(() => {
3331+
const options = {
3332+
url: `http://localhost:8378/1/[email protected]&username=yolo&password=yolopass`,
3333+
headers: {
3334+
'X-Parse-Application-Id': Parse.applicationId,
3335+
'X-Parse-REST-API-Key': 'rest',
3336+
},
3337+
json: true,
3338+
}
3339+
return rp.get(options);
3340+
}).then(done.fail).catch((err) => {
3341+
expect(err.response.body.error).toEqual('Invalid username/password.');
3342+
done();
3343+
});
3344+
});
3345+
3346+
it('fails to login when email and username are not provided', (done) => {
3347+
const user = new Parse.User();
3348+
user.save({
3349+
username: 'yolo',
3350+
password: 'yolopass',
3351+
3352+
}).then(() => {
3353+
const options = {
3354+
url: `http://localhost:8378/1/login?password=yolopass`,
3355+
headers: {
3356+
'X-Parse-Application-Id': Parse.applicationId,
3357+
'X-Parse-REST-API-Key': 'rest',
3358+
},
3359+
json: true,
3360+
}
3361+
return rp.get(options);
3362+
}).then(done.fail).catch((err) => {
3363+
expect(err.response.body.error).toEqual('username/email is required.');
3364+
done();
3365+
});
3366+
});
3367+
3368+
it('fails to login when password is not provided', (done) => {
3369+
const user = new Parse.User();
3370+
user.save({
3371+
username: 'yolo',
3372+
password: 'yolopass',
3373+
3374+
}).then(() => {
3375+
const options = {
3376+
url: `http://localhost:8378/1/login?username=yolo`,
3377+
headers: {
3378+
'X-Parse-Application-Id': Parse.applicationId,
3379+
'X-Parse-REST-API-Key': 'rest',
3380+
},
3381+
json: true,
3382+
}
3383+
return rp.get(options);
3384+
}).then(done.fail).catch((err) => {
3385+
expect(err.response.body.error).toEqual('password is required.');
3386+
done();
3387+
});
3388+
});
32273389
});

src/Routers/UsersRouter.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,25 +51,33 @@ export class UsersRouter extends ClassesRouter {
5151

5252
handleLogIn(req) {
5353
// Use query parameters instead if provided in url
54-
if (!req.body.username && req.query.username) {
55-
req.body = req.query;
54+
let payload = req.body;
55+
if (!payload.username && req.query.username || !payload.email && req.query.email) {
56+
payload = req.query;
5657
}
58+
const {
59+
username,
60+
email,
61+
password,
62+
} = payload;
5763

5864
// TODO: use the right error codes / descriptions.
59-
if (!req.body.username) {
60-
throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'username is required.');
65+
if (!username && !email) {
66+
throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'username/email is required.');
6167
}
62-
if (!req.body.password) {
68+
if (!password) {
6369
throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'password is required.');
6470
}
65-
if (typeof req.body.username !== 'string' || typeof req.body.password !== 'string') {
71+
if (typeof password !== 'string'
72+
|| email && typeof email !== 'string'
73+
|| username && typeof username !== 'string') {
6674
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
6775
}
6876

6977
let user;
7078
let isValidPassword = false;
71-
72-
return req.config.database.find('_User', { username: req.body.username })
79+
const query = Object.assign({}, username ? { username } : {}, email ? { email } : {});
80+
return req.config.database.find('_User', query)
7381
.then((results) => {
7482
if (!results.length) {
7583
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
@@ -79,7 +87,7 @@ export class UsersRouter extends ClassesRouter {
7987
if (req.config.verifyUserEmails && req.config.preventLoginWithUnverifiedEmail && !user.emailVerified) {
8088
throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.');
8189
}
82-
return passwordCrypto.compare(req.body.password, user.password);
90+
return passwordCrypto.compare(password, user.password);
8391
})
8492
.then((correct) => {
8593
isValidPassword = correct;

0 commit comments

Comments
 (0)