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
signup
funciton im hoping to recive a user object that is composed by username, password and type.Then we hash the password in the
hashPassword
function, 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
createToken
function, that uses crypto.randomBytes() to get a base64 string. We also add this attribute to the user object.Finally we use
createUser
to... 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
signin
function i hope to get as a parameter an user object composed by username and password.Using sql queries in the
findUser
function we can get the user that is stored (if it exists).Then with the
checkPassword
function we can verify if there is a match between the password stored and the one the user is trying. This is posible thanks tobcrypt.compare
function.After that we use again the
createToken
function to tokenize the current session, and then we useupdateUserToken
to 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)