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
This commit is contained in:
@@ -3224,4 +3224,166 @@ describe('Parse.User testing', () => {
|
|||||||
.then(done)
|
.then(done)
|
||||||
.catch(done.fail);
|
.catch(done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can login with email', (done) => {
|
||||||
|
const user = new Parse.User();
|
||||||
|
user.save({
|
||||||
|
username: 'yolo',
|
||||||
|
password: 'yolopass',
|
||||||
|
email: 'yo@lo.com'
|
||||||
|
}).then(() => {
|
||||||
|
const options = {
|
||||||
|
url: `http://localhost:8378/1/login`,
|
||||||
|
headers: {
|
||||||
|
'X-Parse-Application-Id': Parse.applicationId,
|
||||||
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
|
},
|
||||||
|
json: { email: "yo@lo.com", password: 'yolopass'}
|
||||||
|
}
|
||||||
|
return rp.get(options);
|
||||||
|
}).then(done).catch(done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cannot login with email and invalid password', (done) => {
|
||||||
|
const user = new Parse.User();
|
||||||
|
user.save({
|
||||||
|
username: 'yolo',
|
||||||
|
password: 'yolopass',
|
||||||
|
email: 'yo@lo.com'
|
||||||
|
}).then(() => {
|
||||||
|
const options = {
|
||||||
|
url: `http://localhost:8378/1/login`,
|
||||||
|
headers: {
|
||||||
|
'X-Parse-Application-Id': Parse.applicationId,
|
||||||
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
|
},
|
||||||
|
json: { email: "yo@lo.com", password: 'yolopass2'}
|
||||||
|
}
|
||||||
|
return rp.get(options);
|
||||||
|
}).then(done.fail).catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can login with email through query string', (done) => {
|
||||||
|
const user = new Parse.User();
|
||||||
|
user.save({
|
||||||
|
username: 'yolo',
|
||||||
|
password: 'yolopass',
|
||||||
|
email: 'yo@lo.com'
|
||||||
|
}).then(() => {
|
||||||
|
const options = {
|
||||||
|
url: `http://localhost:8378/1/login?email=yo@lo.com&password=yolopass`,
|
||||||
|
headers: {
|
||||||
|
'X-Parse-Application-Id': Parse.applicationId,
|
||||||
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return rp.get(options);
|
||||||
|
}).then(done).catch(done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can login when both email and username are passed', (done) => {
|
||||||
|
const user = new Parse.User();
|
||||||
|
user.save({
|
||||||
|
username: 'yolo',
|
||||||
|
password: 'yolopass',
|
||||||
|
email: 'yo@lo.com'
|
||||||
|
}).then(() => {
|
||||||
|
const options = {
|
||||||
|
url: `http://localhost:8378/1/login?email=yo@lo.com&username=yolo&password=yolopass`,
|
||||||
|
headers: {
|
||||||
|
'X-Parse-Application-Id': Parse.applicationId,
|
||||||
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return rp.get(options);
|
||||||
|
}).then(done).catch(done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails to login when username doesn't match email", (done) => {
|
||||||
|
const user = new Parse.User();
|
||||||
|
user.save({
|
||||||
|
username: 'yolo',
|
||||||
|
password: 'yolopass',
|
||||||
|
email: 'yo@lo.com'
|
||||||
|
}).then(() => {
|
||||||
|
const options = {
|
||||||
|
url: `http://localhost:8378/1/login?email=yo@lo.com&username=yolo2&password=yolopass`,
|
||||||
|
headers: {
|
||||||
|
'X-Parse-Application-Id': Parse.applicationId,
|
||||||
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
|
},
|
||||||
|
json: true,
|
||||||
|
}
|
||||||
|
return rp.get(options);
|
||||||
|
}).then(done.fail).catch((err) => {
|
||||||
|
expect(err.response.body.error).toEqual('Invalid username/password.');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails to login when email doesn't match username", (done) => {
|
||||||
|
const user = new Parse.User();
|
||||||
|
user.save({
|
||||||
|
username: 'yolo',
|
||||||
|
password: 'yolopass',
|
||||||
|
email: 'yo@lo.com'
|
||||||
|
}).then(() => {
|
||||||
|
const options = {
|
||||||
|
url: `http://localhost:8378/1/login?email=yo@lo2.com&username=yolo&password=yolopass`,
|
||||||
|
headers: {
|
||||||
|
'X-Parse-Application-Id': Parse.applicationId,
|
||||||
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
|
},
|
||||||
|
json: true,
|
||||||
|
}
|
||||||
|
return rp.get(options);
|
||||||
|
}).then(done.fail).catch((err) => {
|
||||||
|
expect(err.response.body.error).toEqual('Invalid username/password.');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails to login when email and username are not provided', (done) => {
|
||||||
|
const user = new Parse.User();
|
||||||
|
user.save({
|
||||||
|
username: 'yolo',
|
||||||
|
password: 'yolopass',
|
||||||
|
email: 'yo@lo.com'
|
||||||
|
}).then(() => {
|
||||||
|
const options = {
|
||||||
|
url: `http://localhost:8378/1/login?password=yolopass`,
|
||||||
|
headers: {
|
||||||
|
'X-Parse-Application-Id': Parse.applicationId,
|
||||||
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
|
},
|
||||||
|
json: true,
|
||||||
|
}
|
||||||
|
return rp.get(options);
|
||||||
|
}).then(done.fail).catch((err) => {
|
||||||
|
expect(err.response.body.error).toEqual('username/email is required.');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails to login when password is not provided', (done) => {
|
||||||
|
const user = new Parse.User();
|
||||||
|
user.save({
|
||||||
|
username: 'yolo',
|
||||||
|
password: 'yolopass',
|
||||||
|
email: 'yo@lo.com'
|
||||||
|
}).then(() => {
|
||||||
|
const options = {
|
||||||
|
url: `http://localhost:8378/1/login?username=yolo`,
|
||||||
|
headers: {
|
||||||
|
'X-Parse-Application-Id': Parse.applicationId,
|
||||||
|
'X-Parse-REST-API-Key': 'rest',
|
||||||
|
},
|
||||||
|
json: true,
|
||||||
|
}
|
||||||
|
return rp.get(options);
|
||||||
|
}).then(done.fail).catch((err) => {
|
||||||
|
expect(err.response.body.error).toEqual('password is required.');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -51,25 +51,33 @@ export class UsersRouter extends ClassesRouter {
|
|||||||
|
|
||||||
handleLogIn(req) {
|
handleLogIn(req) {
|
||||||
// Use query parameters instead if provided in url
|
// Use query parameters instead if provided in url
|
||||||
if (!req.body.username && req.query.username) {
|
let payload = req.body;
|
||||||
req.body = req.query;
|
if (!payload.username && req.query.username || !payload.email && req.query.email) {
|
||||||
|
payload = req.query;
|
||||||
}
|
}
|
||||||
|
const {
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
// TODO: use the right error codes / descriptions.
|
// TODO: use the right error codes / descriptions.
|
||||||
if (!req.body.username) {
|
if (!username && !email) {
|
||||||
throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'username is required.');
|
throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'username/email is required.');
|
||||||
}
|
}
|
||||||
if (!req.body.password) {
|
if (!password) {
|
||||||
throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'password is required.');
|
throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'password is required.');
|
||||||
}
|
}
|
||||||
if (typeof req.body.username !== 'string' || typeof req.body.password !== 'string') {
|
if (typeof password !== 'string'
|
||||||
|
|| email && typeof email !== 'string'
|
||||||
|
|| username && typeof username !== 'string') {
|
||||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
|
||||||
}
|
}
|
||||||
|
|
||||||
let user;
|
let user;
|
||||||
let isValidPassword = false;
|
let isValidPassword = false;
|
||||||
|
const query = Object.assign({}, username ? { username } : {}, email ? { email } : {});
|
||||||
return req.config.database.find('_User', { username: req.body.username })
|
return req.config.database.find('_User', query)
|
||||||
.then((results) => {
|
.then((results) => {
|
||||||
if (!results.length) {
|
if (!results.length) {
|
||||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
|
||||||
@@ -79,7 +87,7 @@ export class UsersRouter extends ClassesRouter {
|
|||||||
if (req.config.verifyUserEmails && req.config.preventLoginWithUnverifiedEmail && !user.emailVerified) {
|
if (req.config.verifyUserEmails && req.config.preventLoginWithUnverifiedEmail && !user.emailVerified) {
|
||||||
throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.');
|
throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.');
|
||||||
}
|
}
|
||||||
return passwordCrypto.compare(req.body.password, user.password);
|
return passwordCrypto.compare(password, user.password);
|
||||||
})
|
})
|
||||||
.then((correct) => {
|
.then((correct) => {
|
||||||
isValidPassword = correct;
|
isValidPassword = correct;
|
||||||
|
|||||||
Reference in New Issue
Block a user