So, in this second part (First part) we are going to configure our backend to allow us sign up and sign in into our chat app!
In our users.js file we have to do a little bit more than before:
Users
./sql/users.js
const bcrypt = require("bcryptjs");
const crypto = require("crypto");
const db = require("../db.js");
bcrypt is a hashing function that we will use to safely store the users passwords.
And crypto provide us cryptographic functionality that we will use to tokenize the user session.
const signup = (user) => {
return hashPassword(user.password)
.then((hashedPassword) => {
delete user.password;
user.password_digested = hashedPassword;
})
.then(() => createToken())
.then((token) => (user.token = token))
.then(() => createUser(user))
.then((user) => {
delete user.password_digested;
return user;
})
.catch((err) => {
console.log(err);
return err;
});
};
const hashPassword = (password) => {
return new Promise((resolve, reject) =>
bcrypt.hash(password, 10, (err, hash) => {
err ? reject(err) : resolve(hash);
})
);
};
const createToken = () => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, data) => {
err ? reject(err) : resolve(data.toString("base64"));
});
});
};
const createUser = (user) => {
return db
.raw(
"INSERT INTO users ( usr, name, password_digested, token, type) VALUES (?, ?, ?, ?, ?) RETURNING usr, name, type, token",
[user.usr, user.name, user.password_digested, user.token, user.type]
)
.then((data) => data.rows[0]);
};
So basically we've create an signup-flow. Lets break down that code a litle.
In the
signupfunciton im hoping to recive a user object that is composed by username, password and type.Then we hash the password in the
hashPasswordfunction, which uses bcrypt.hash() to salt and hash the user password.After that, we now can delete the user password from our records and only care about the hashedPassword. So at this moment we start to create an user object based on the model defined at the migrations models.
Then we create a token for this session with
createTokenfunction, that uses crypto.randomBytes() to get a base64 string. We also add this attribute to the user object.Finally we use
createUserto... well, order some pizza. This function uses the db knex object to insert into the users table that user object we've been composing.
Now we are building the signin-flow:
const signin = (userReq) => {
let user;
return findUser(userReq.usr)
.then((foundUser) => {
user = foundUser;
return checkPassword(userReq.password, foundUser);
})
.then((res) => createToken())
.then((token) => updateUserToken(token, user))
.then(
() => {
delete user.password_digested;
return user;
},
(err) => {
return "User not found, please verify the fields";
}
)
.catch((err) => {
console.log(err);
return "Cannot signin, please get in touch with the admin";
});
};
const findUser = (usr) => {
console.log(usr);
return db
.raw("SELECT * FROM users WHERE usr = ?", [usr])
.then((data) => data.rows[0]);
};
const checkPassword = (reqPassword, foundUser) => {
return new Promise((resolve, reject) =>
bcrypt.compare(
reqPassword,
foundUser.password_digested,
(err, response) => {
if (err) {
reject(err);
} else if (response) {
resolve(response);
} else {
reject(new Error("Verify your password"));
}
}
)
);
};
const updateUserToken = (token, user) => {
return db
.raw("UPDATE users SET token = ? WHERE usr = ? RETURNING usr, token", [
token,
user.usr,
])
.then((data) => data.rows[0]);
};
Lets break it down!
In the
signinfunction i hope to get as a parameter an user object composed by username and password.Using sql queries in the
findUserfunction we can get the user that is stored (if it exists).Then with the
checkPasswordfunction we can verify if there is a match between the password stored and the one the user is trying. This is posible thanks tobcrypt.comparefunction.After that we use again the
createTokenfunction to tokenize the current session, and then we useupdateUserTokento change the token stored.Finally we send a response of the user authenticated but without his hashed password.
Ok so we've created our signin signup flow, now we can access this functions by exporting them:
module.exports = {
signin,
signup,
findUser,
};
In the next part we'll be setting our graphql schemas and subscription to fetch messages and signin and signup usign graphql!
Top comments (0)