mirror of
https://gitlab.com/Alamantus/Readlebee.git
synced 2025-06-03 16:10:06 +02:00
Add login validation without testing
This commit is contained in:
parent
73980aeb93
commit
929d1ca928
3 changed files with 177 additions and 27 deletions
|
@ -28,6 +28,13 @@ export class LoginController extends ViewController {
|
||||||
// or use `onclick=${() => controller.submit()}` to maintain the 'this' of the class instead.
|
// 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 () {
|
clearCreateAccountForm () {
|
||||||
this.state.fieldValues.createEmail = '';
|
this.state.fieldValues.createEmail = '';
|
||||||
this.state.fieldValues.createUsername = '';
|
this.state.fieldValues.createUsername = '';
|
||||||
|
@ -38,6 +45,31 @@ export class LoginController extends ViewController {
|
||||||
this.emit('render');
|
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 () {
|
validateCreateAccount () {
|
||||||
const { __ } = this.i18n;
|
const { __ } = this.i18n;
|
||||||
this.state.createError = '';
|
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 () {
|
createAccount () {
|
||||||
const { __ } = this.i18n;
|
const { __ } = this.i18n;
|
||||||
const {
|
const {
|
||||||
|
|
|
@ -49,6 +49,25 @@ class Account {
|
||||||
return true;
|
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) {
|
static cleanCreateAccountFormData (formData) {
|
||||||
return {
|
return {
|
||||||
email: formData.email.trim(),
|
email: formData.email.trim(),
|
||||||
|
@ -155,6 +174,41 @@ class Account {
|
||||||
return userToConfirm;
|
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 {
|
} else {
|
||||||
const token = fastify.jwt.sign({ id: newUser.id });
|
return reply.send({
|
||||||
const expireTime = fastify.siteConfig.tokenExpireDays * (24 * 60 * 60e3); // The section in parentheses is milliseconds in a day
|
error: false,
|
||||||
|
message: 'api.account_create_success',
|
||||||
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',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -138,7 +127,7 @@ async function routes(fastify, options) {
|
||||||
}).then(email => {
|
}).then(email => {
|
||||||
if (email.err) {
|
if (email.err) {
|
||||||
console.error(email.err);
|
console.error(email.err);
|
||||||
return reply.send({
|
return reply.code(400).send({
|
||||||
error: true,
|
error: true,
|
||||||
message: 'api.account_confirm_email_send_fail',
|
message: 'api.account_confirm_email_send_fail',
|
||||||
});
|
});
|
||||||
|
@ -158,18 +147,23 @@ async function routes(fastify, options) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fastify.get('/api/login', async (request, reply) => {
|
fastify.get('/api/account/login', async (request, reply) => {
|
||||||
reply.view('login.hbs', { text: request.isLoggedInUser ? JSON.stringify(fastify.jwt.decode(request.cookies.token)) : 'you are NOT logged in' });
|
const formDataIsValid = Account.loginDataIsValid(request.body);
|
||||||
});
|
if (formDataIsValid !== true) {
|
||||||
|
return reply.code(400).send(formDataIsValid);
|
||||||
fastify.post('/api/login-validate', async (request, reply) => {
|
|
||||||
if (typeof request.body.email === "undefined" || typeof request.body.password === "undefined") {
|
|
||||||
reply.redirect('/login', 400);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
const expireTime = fastify.siteConfig.tokenExpireDays * (24 * 60 * 60e3); // The section in parentheses is milliseconds in a day
|
||||||
reply
|
|
||||||
|
return reply
|
||||||
.setCookie('token', token, {
|
.setCookie('token', token, {
|
||||||
path: '/',
|
path: '/',
|
||||||
expires: new Date(Date.now() + expireTime),
|
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
|
httpOnly: true, // Prevents JavaScript on the front end from grabbing it
|
||||||
sameSite: true, // Prevents the cookie from being used outside of this site
|
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) => {
|
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…
Add table
Reference in a new issue