Add login validation without testing
This commit is contained in:
parent
73980aeb93
commit
929d1ca928
|
@ -28,6 +28,13 @@ export class LoginController extends ViewController {
|
|||
// or use `onclick=${() => controller.submit()}` to maintain the 'this' of the class instead.
|
||||
}
|
||||
|
||||
clearLoginForm () {
|
||||
this.state.fieldValues.loginEmail = '';
|
||||
this.state.fieldValues.loginPassword = '';
|
||||
|
||||
this.emit('render');
|
||||
}
|
||||
|
||||
clearCreateAccountForm () {
|
||||
this.state.fieldValues.createEmail = '';
|
||||
this.state.fieldValues.createUsername = '';
|
||||
|
@ -38,6 +45,31 @@ export class LoginController extends ViewController {
|
|||
this.emit('render');
|
||||
}
|
||||
|
||||
validateLogin () {
|
||||
const { __ } = this.i18n;
|
||||
this.state.createError = '';
|
||||
this.state.isChecking = true;
|
||||
|
||||
this.emit('render', () => {
|
||||
const {
|
||||
loginEmail,
|
||||
loginPassword
|
||||
} = this.state.fieldValues;
|
||||
|
||||
if ([
|
||||
loginEmail,
|
||||
loginPassword,
|
||||
].includes('')) {
|
||||
this.state.createError = __('login.create_required_field_blank');
|
||||
this.state.isChecking = false;
|
||||
this.emit('render');
|
||||
return;
|
||||
}
|
||||
|
||||
this.logIn();
|
||||
});
|
||||
}
|
||||
|
||||
validateCreateAccount () {
|
||||
const { __ } = this.i18n;
|
||||
this.state.createError = '';
|
||||
|
@ -74,6 +106,38 @@ export class LoginController extends ViewController {
|
|||
});
|
||||
}
|
||||
|
||||
logIn () {
|
||||
const { __ } = this.i18n;
|
||||
const {
|
||||
loginEmail,
|
||||
loginPassword
|
||||
} = this.state.fieldValues;
|
||||
|
||||
fetch('/api/account/login', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: loginEmail,
|
||||
password: loginPassword,
|
||||
}),
|
||||
}).then(response => response.json())
|
||||
.then(response => {
|
||||
if (response.error !== false) {
|
||||
console.error(response);
|
||||
this.state.loginError = __(response.message);
|
||||
this.state.isChecking = false;
|
||||
this.emit('render');
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.loginMessage = __(response.message);
|
||||
this.state.isChecking = false;
|
||||
this.clearLoginForm();
|
||||
})
|
||||
}
|
||||
|
||||
createAccount () {
|
||||
const { __ } = this.i18n;
|
||||
const {
|
||||
|
|
|
@ -49,6 +49,25 @@ class Account {
|
|||
return true;
|
||||
}
|
||||
|
||||
static loginDataIsValid (loginData) {
|
||||
if (typeof loginData.email === 'undefined'
|
||||
|| typeof loginData.password === 'undefined'
|
||||
|| loginData.password === '') {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_login_required_data_missing',
|
||||
};
|
||||
}
|
||||
if (loginData.email.length < 5 || !/^.+@.+\..+$/.test(loginData.email)) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_login_invalid_email',
|
||||
};
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static cleanCreateAccountFormData (formData) {
|
||||
return {
|
||||
email: formData.email.trim(),
|
||||
|
@ -155,6 +174,41 @@ class Account {
|
|||
return userToConfirm;
|
||||
});
|
||||
}
|
||||
|
||||
async validateLogin (email, password) {
|
||||
const existingUser = await this.model.find({
|
||||
attributes: ['id', 'passwordHash', 'passwordSalt', 'accountConfirm'],
|
||||
where: {
|
||||
email,
|
||||
},
|
||||
});
|
||||
if (existingUser == null) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_login_invalid_email',
|
||||
};
|
||||
}
|
||||
|
||||
if (existingUser.accountConfirm !== null) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_login_not_confirmed',
|
||||
};
|
||||
}
|
||||
|
||||
const passwordIsValid = Account.verifyPassword(existingUser.passwordHash, existingUser.passwordSalt, password);
|
||||
if (!passwordIsValid) {
|
||||
return {
|
||||
error: true,
|
||||
message: 'api.account_login_invalid_password',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
error: false,
|
||||
id: existingUser.id,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -78,21 +78,10 @@ async function routes(fastify, options) {
|
|||
});
|
||||
}
|
||||
} else {
|
||||
const token = fastify.jwt.sign({ id: newUser.id });
|
||||
const expireTime = fastify.siteConfig.tokenExpireDays * (24 * 60 * 60e3); // The section in parentheses is milliseconds in a day
|
||||
|
||||
return reply
|
||||
.setCookie('token', token, {
|
||||
path: '/',
|
||||
expires: new Date(Date.now() + expireTime),
|
||||
maxAge: new Date(Date.now() + expireTime), // Both are set as a "just in case"
|
||||
httpOnly: true, // Prevents JavaScript on the front end from grabbing it
|
||||
sameSite: true, // Prevents the cookie from being used outside of this site
|
||||
})
|
||||
.send({
|
||||
error: false,
|
||||
message: 'api.account_create_success',
|
||||
});
|
||||
return reply.send({
|
||||
error: false,
|
||||
message: 'api.account_create_success',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -138,7 +127,7 @@ async function routes(fastify, options) {
|
|||
}).then(email => {
|
||||
if (email.err) {
|
||||
console.error(email.err);
|
||||
return reply.send({
|
||||
return reply.code(400).send({
|
||||
error: true,
|
||||
message: 'api.account_confirm_email_send_fail',
|
||||
});
|
||||
|
@ -158,18 +147,23 @@ async function routes(fastify, options) {
|
|||
}
|
||||
});
|
||||
|
||||
fastify.get('/api/login', async (request, reply) => {
|
||||
reply.view('login.hbs', { text: request.isLoggedInUser ? JSON.stringify(fastify.jwt.decode(request.cookies.token)) : 'you are NOT logged in' });
|
||||
});
|
||||
|
||||
fastify.post('/api/login-validate', async (request, reply) => {
|
||||
if (typeof request.body.email === "undefined" || typeof request.body.password === "undefined") {
|
||||
reply.redirect('/login', 400);
|
||||
fastify.get('/api/account/login', async (request, reply) => {
|
||||
const formDataIsValid = Account.loginDataIsValid(request.body);
|
||||
if (formDataIsValid !== true) {
|
||||
return reply.code(400).send(formDataIsValid);
|
||||
}
|
||||
|
||||
const token = fastify.jwt.sign({ email: request.body.email, password: request.body.password });
|
||||
const account = new Account(fastify.models.User);
|
||||
const user = account.validateLogin(request.body.email, request.body.password);
|
||||
|
||||
if (user.error !== true) {
|
||||
return reply.code(400).send(user);
|
||||
}
|
||||
|
||||
const token = fastify.jwt.sign({ id: user.id });
|
||||
const expireTime = fastify.siteConfig.tokenExpireDays * (24 * 60 * 60e3); // The section in parentheses is milliseconds in a day
|
||||
reply
|
||||
|
||||
return reply
|
||||
.setCookie('token', token, {
|
||||
path: '/',
|
||||
expires: new Date(Date.now() + expireTime),
|
||||
|
@ -177,11 +171,49 @@ async function routes(fastify, options) {
|
|||
httpOnly: true, // Prevents JavaScript on the front end from grabbing it
|
||||
sameSite: true, // Prevents the cookie from being used outside of this site
|
||||
})
|
||||
.redirect('/', 200);
|
||||
.send({
|
||||
error: false,
|
||||
message: 'api.account_create_success',
|
||||
});
|
||||
});
|
||||
|
||||
fastify.post('/api/account/validate', async (request, reply) => {
|
||||
if (typeof request.cookies.token === "undefined") {
|
||||
return reply.code(400).send({
|
||||
error: true,
|
||||
message: 'api.account_validate_missing_token',
|
||||
});
|
||||
}
|
||||
|
||||
const tokenIsValid = await fastify.jwt.verify(request.cookies.token);
|
||||
if (!tokenIsValid) {
|
||||
return reply.code(400).send({
|
||||
error: true,
|
||||
message: 'api.account_validate_invalid_token',
|
||||
});
|
||||
}
|
||||
|
||||
// Renew the token if valid
|
||||
const expireTime = fastify.siteConfig.tokenExpireDays * (24 * 60 * 60e3); // The section in parentheses is milliseconds in a day
|
||||
return reply
|
||||
.setCookie('token', request.cookies.token, {
|
||||
path: '/',
|
||||
expires: new Date(Date.now() + expireTime),
|
||||
maxAge: new Date(Date.now() + expireTime), // Both are set as a "just in case"
|
||||
httpOnly: true, // Prevents JavaScript on the front end from grabbing it
|
||||
sameSite: true, // Prevents the cookie from being used outside of this site
|
||||
})
|
||||
.send({
|
||||
error: false,
|
||||
message: 'api.account_validate_renewed_token',
|
||||
});
|
||||
});
|
||||
|
||||
fastify.get('/api/logout', async (request, reply) => {
|
||||
reply.clearCookie('token', { path: '/' }).redirect('/?loggedout');
|
||||
return reply.clearCookie('token', { path: '/' }).send({
|
||||
error: false,
|
||||
message: 'api._account_logout_success',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue