Today I will be going back to the NodeJS course as I feel like I have been able to implement everything I’ve learned thus far on my own with the Drawing App.
Below we setup a route to log a user out. This simply authenticates the user using an auth middleware we wrote previously. Once the user is authenticated, it checks the users list of active tokens and removes the one currently saved to the req method, which was saved there during their authentication.
router.post('/users/logout', auth, async (req,res) => {
try {
req.user.tokens = req.user.tokens.filter((token) => {
return token.token !== req.token
})
await req.user.save()
res.send()
} catch (e) {
res.status(500).send()
}
})
To ‘Log Out from All Locations’ we simply remove ALL of the tokens from the token array stored in the User object, rather than the only ‘active’ one. We can do this by setting the user’s tokens to an empty array like so. req.user.tokens = []
Today I pushed my app in development to Heroku for testing and to show a couple of friends. It is now live at jbv-drawing-prompt-app.herokuapp.com
During the process I encountered a couple of CORS issues as well as some database connection issues. It made me glad that I added a console log to the mongodb connection JS file that either alerts ‘Connected To Dabase’ or ‘Could not connect to database’
I got everything to work by doing the following:
Read More...Got back from vacation last night FULLY relaxed and ready to get back into the daily grind. While on the trip I put in an hour or two here and there working through codewars.com exercises and puzzles. Fun stuff.
This morning I started the day off with rehashing what I learned on my last coding day. I also implemented a couple of new features into the site I am working on.
I added an admin panel that shows me what ‘items’ users have submitted. It then shows me if they are approved or unapproved, as well as gives me the ability to delete any item. This is VERY similar to MANY todolist tutorials and I can see why those tutorials are so important. Basically everything on the web is a TODO list type item.
Read More...I spent yesterday and this morning getting deeper with working with MongoDB. I really enjoy using mongoose.js to interface with the mongo database as well. I spent time implementing my own CRUD operations, sending get and post requests to my own express app on the backend.
This morning, after realizing that git version control wasnt working with my database folder for the app I am working on, I migrated my database over to MongoDB Atlas. This is a cloud database that offers a couple free tier options. I like the UI for the site as well as the ability to access the database from any computer I will be working from.
The implementation was extremely simple as well.
I am now going to go through a couple more videos from the NodeJS course. At this time I am 61% complete and I feel like I have learned a pretty large amount of tools.
Read More...So Hurricane Barry is making its way to land. Im posted up at my parents house in case the power back home goes out. I’ve been reading a bit of SICP which apparently is a computer programmer’s/CS Major’s dream book. I’ve also been going through MIT lectures on the book and I find it very interesting.
The language it will be focused on is a dialect of Lisp called Scheme. This all comes recommended to my by a friend who’s has his degree in CS. I think it’s a good reccomendation as the book is going through the basic building blocks of what programs are, of what interpreters are, and what is actually going on behind the scenes when we write programs. I love this type of stuff, taking it all apart is so interesting to me.
Anyway! That being said, I felt it necessary to also go back to the Regex section of Eloquent Javascript. Why? Because in it is a section on writing a parsing function that interprets an INI file using regex statements. Here is the example the book gives.
function parseINI(string) {
// Start with an object to hold the top-level fields
let result = {};
let section = result;
string.split(/\r?\n/).forEach(line => {
let match;
if (match = line.match(/^(\w+)=(.*)$/)) {
section[match[1]] = match[2];
} else if (match = line.match(/^\[(.*)\]$/)) {
section = result[match[1]] = {};
} else if (!/^\s*(;.*)?$/.test(line)) {
throw new Error("Line '" + line + "' is not valid.");
}
});
return result;
}
Since it’s been a couple of days since I last worked through more of the NodeJS course. I’m starting the morning off by reviewing the last hour or so of videos. Running through bcrypt password hashing usage, mongoose model middelware to run before saving anything to the database, and json web tokens.
Every express route we define will fall into either a ‘public’ or ‘private’ category. It will either be accessible to anyone, or sit behind authentication, and users would have to be authed to use it.
Our signup and login route will be the only public routes available to the user. We dont want users deleting OTHER users tasks or updating things of the sort. To do this, we will implement authentication with Json web tokens.
We will be using the jsonwebtoken npm library to do all of this. Heres what a super simple implemenation could look like.
const jwt = require('jsonwebtoken')
const myFunction = async () => {
const token = jwt.sign({_id: 'abcd1234'}, 'secretsignature')
console.log(token)
// eyJhbGciOiJIUzI1NiIsInR5cCI6Ik.... more really long token data
// the token comes back as 3 chunks of data separated by periods.
// the first piece is a base64 json string header with meta info
// the second piece is the payload/body containing the data provided {_id: 'abcd1234'}
// the third piece is the signature
The goal of a json web token isnt to hide the data we provide. The whole point is to create data that is verifiable via the signature.
Whats going on behind the scenes is this. Lets copy the middle part of the token provided to us and ‘decode’ it at base64decode.org - we get this back: {"_id":"abcd1234","iat":1562776753}
The “iat” is the timestamp at which the jwt was created.
We can use the .verify()
that the package gives us to verify a token:
// .verify() takes the token to verify, and the secret string used to create that token as arguments
const data = jwt.verify(token, 'mysecretstring')
console.log(data)
// {"_id":"abcd1234","iat":1562776753}
We can also set expiration by providing a third argument to the .sign()
method like so:
jwt.sign({_id: 'abcd1234'}, 'secretsignature', {expiresIn: '1h'})
This would set the token to expire in one hour, effectively logging the user out after one hour.
We will now use jwt to create a token to send to the user when signing up, or when logging into the site. Each request will send the user jwt tokens. We will write a re-usable function that different handlers can use whenever we need to generate a token.
We will store this on userSchema.methods
as opposed to userSchema.statics
which will give each user instance the function we can use. Static methods are accessible on the Model as a whole, but regular methods are accessible on instances of that model.
Lets first create that method.
userSchema.methods.generateAuthToken = async function () {
const user = this;
// jwt expects a string in the payload object you pass to it, so we use .toString() to do that.
const token = jwt.sign({_id: user._id.toString() },'thisismysecretsentence')
return token;
}
Next, lets change the users/login route to call this function and add that to what we send to the user.
router.post('/users/login', async (req,res) => {
try {
const user = await User.findByCredentials(req.body.email,req.body.password)
const token = await user.generateAuthToken()
res.send({ user, token})
} catch(e) {
res.status(400).send()
}
})
Great! It worked. Now when we send a request to /users/login with the correct body info filled out we get this back. (I am using postman for all of my http requests)
{
"user": {
// ...data here
},
"token": // data here
}
At this point we provide the auth token to the client for them to use on any request that requires it. We are not keeping track of this currently on the server. The server generates it and then sends it back. Right now users cant truly log out while the token exists. We can fix this by tracking tokens we generate for users. It will allow them to log in on multiple devices, with each token tracked to that login/logout.
We will store all of the auth tokens created for a user in the user document. To do this, we only have to add a little code to the User Schema, as well as the .generateAuthToken method we just created.
To the Schema we add:
// this allow us to create an array of objects on the user. Each object will contain a token that will be a string.
tokens: [{
token: {
type: String,
required: true
}
}]
To the .generateAuthToken method we add the following before the return call:
user.tokens = user.tokens.concat({ token });
await user.save();
This will concatenate the current token inside of an object to the current array of objects with tokens. Now our returned json.body looks like this when posting to users/login
{
"user": {
"age": 0,
"_id": "5d1d0f94316a5340744f4d9a",
"name": "Jordan",
"email": // email data
"password": // password data
"__v": 4,
"tokens": [
{
"_id": "5d261de27254e010f8799f8e",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZDFkMGY5NDMxNmE1MzQwNzQ0ZjRkOWEiLCJpYXQiOjE1NjI3NzkxMDZ9.WDxucPGY_7HVyl8fLgmvAC2MUIW9qxkPTPsGwLRtAQM"
}
]
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZDFkMGY5NDMxNmE1MzQwNzQ0ZjRkOWEiLCJpYXQiOjE1NjI3NzkxNzR9.mEkkXpWvND1V0lfsm2TuKoBP5YxArNqA4OlwSKKqGOE"
}
Now we will move onto authenticating users and having them be able to only do certain things depending on their auth. We will do this with express middleware on certain routes. Here is a simple example of something we can use middleware to do (we put this above our routes in our index.js file):
app.use((req,res, next) => {
if (req.method === 'GET') {
res.send('GET requests are currently under construction!')
} else {
next()
}
})
The code above will intercept all http requests and first check if it is a GET method. If so, it will send to the user that GET requests are currently being worked on. All other methods will be run as normal, moving onto the next function by calling next()
.
This type of functionality will be how we use authentication. When creating middleware to use on our site it is normal to store your middleware functions in a folder src/middleware. We will create a file there called auth.js and load it into our routers.
We will use middleware for only certain routes. To add middleware to only certain routes, you pass the function to use as middleware as your second argument. In between the route, and the callback that route will run. DONT FORGET TO CALL NEXT() in the middleware function.
router.get('/users', middelwareFunc, (req,res) => {
// code here...
})
The user will be able to be authenticated by sending the latest auth key they recieved to the server via GET method and headers. For learning purposes we will setup for a user to send a GET request to users/me and be returned their on info. We will accomplish this by adding an Authorization header with ‘Bearer **token’ - We will do this like so:
const jwt = require('jsonwebtoken')
const User = require('../models/user')
const auth = async (req,res,next) => {
try {
const token = req.header('Authorization').replace('Bearer ','')
// if verified decoded should look lik {_id: ....., iat: .....}
const decoded = jwt.verify(token, 'thisismysecretsentence')
// this will look for a user with the ID that also has the token in their array of tokens
const user = await User.findOne({ _id: decoded._id, 'tokens.token': token })
if (!user) {
throw new Error()
}
// store the returned user to the req object so the next part of the express functions will not have to look it up again
req.user = user
next()
} catch (e) {
res.status(401).send({error: 'Please Authenticate.'})
}
}
module.exports = auth
// get user profile
router.get('/users/me', auth , async (req,res) => {
res.send(req.user)
})
Adding this returns the users data to them, if they are currently logged in. We do this by checking if the key the browsers sends to the server is still in the tokens array and active.
After spending the morning working through all of this I then created my own versisons of what we did in the tutorials. I created a login page and saved users to a mongodb database using postman, sending user info in the body of my requests to create multiple users.
I then created a ‘log in’ page and added the functionality for a user to submit their username and password. This sends a fetch request to a custom route I setup using express and returns the user’s data and a new token to them if the user name and password were correct.
Here is some of that code from the front end.
var userName = document.getElementById('username')
var submit = document.getElementById('submit')
let errorText = document.getElementsByClassName('error')[0]
var token;
submit.addEventListener('click', () => {
fetch('http://localhost:3000/login/user', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body:
JSON.stringify({
userName: userName.value,
password: document.getElementById('password').value
})
}).then(async (response) => {
errorText.innerHTML = '';
errorText.classList.remove('error-animate')
try {
let data = await response.json();
let {_id:id, userName:user } = data.user
token = data.token;
console.log(id, user)
//userName.value = ''
//document.getElementById('password').value = ''
} catch (e) {
console.log('Unable to log in')
errorText.innerHTML = 'Unable to log in.';
errorText.classList.add('error-animate')
}
})
})
Working from a great coffee shop in Lafayette today. Starting off the day reading through the asynchronous programming chapter in Eloquent Javascript. I feel fairly confident in what async functions are and how they work, but I want to read through this chapter to solidify any loose thinking in regards to that.
The author has a sense of humor. We will be learning the ins and outs of async by writing networking functions for a fictitious crow colony that has learned how to use javascript and communicate from nest to nest by sending signals to and from each other…. interesting.
One approach to async programming is to make functions that perform a slow action take a callback argument to run when the code is finished working. (We learned about this in the NodeJS course we are currently working through.) As we learned previously, this can get convoluted quickly when wanting to perform more than one async operation. The example below has two callbacks. Any more and this could lead to hard to lots of nesting and hard to read code. IE.
import {bigOak} from "./crow-tech";
bigOak.readStorage("food caches", caches => { // first callback
let firstCache = caches[0];
bigOak.readStorage(firstCache, info => { // second nested callback
console.log(info);
});
});
Spent a couple of hours yesterday and today implementing pagination onto my site and blog. I met with a professional programmer and he had some great suggestions for what my portfolio should showcase, as well as some input for the blog.
1. The blog now shows a max of 10 posts on a single page. Each post also has a tag that tells Jekyll where to put the “Read More…” link.
2. The home page is now my project showcase, and at the bottom of the page, my latest 2 entries show up for anyone who is interested to read it.
3. I uploaded everything to my domain at jordanvidrine.com for a better online presence.
4. I removed my github pages ‘blog’ and that all now resides on the github page for jordanvidrine.github.io
After making edits, I use the bundle exec jekyll build
command to build the site into normal html, css, and javascript files. I then upload that to both my github page, as well as my domain. I really like the consistency of my new site now and am pleased with the blogging functionality I put into it.
Spent the first couple of hours of my day trying to integrate pagination into this blog. It turned out very frustrating and uneventful because github pages does not support the newest jekyll pagination gem. I couldn’t for the life of me get the v1 of their gem to work either. I’ll have to look into that in some free time. Onto my node course!
We will use bcrpyt in our task-manager app (the one I’ve been creating in this course) to save hashed versions of user passwords, as opposed to the literal password string. The following code will use bcrpyt.hash() to do this.
const bcrpyt = require('bcryptjs')
const myFunction = async () => {
const password = 'Red12345!'
// hash() accepts the string to hash, and a number of rounds to run the hash
// algorithm. 8 is the value recommended from the original creator of the
// bcrypt algorithm
const hashedPassword = await bcrypt.hash(password, 8)
console.log(password)
console.log(hashedPassword)
}
myFunction()
I’ve worked with basic promises with the past couple of exercises in the Node.js course. What if we wanted to to multiple tasks, after each step of operating with the Promise returned. Ie. First get a lat,long from a geocoding API, then use that data to get weather info; or mark a task as complete, then get the total number of incomplete tasks left to do.
Here is where Promise Chaining comes into play.
// Here is an example of a basic Promise that will always return the sum after 2 seconds. An error is
// not programmed to take place.
const add = (a, b) => {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve(a+b)
},2000)
})
}
// What if you want to perform more asynchronous actions after receiving the resolved promise? One way
// (a bad way) is to nest more calls to the Promise inside of the returned resolution as such. This
// will QUICKLY get confusing and convoluted so it is not a good idea
add(1,2).then((sum) => {
console.log(sum)
add(sum,5).then((sum2) => {
console.log(sum2)
}).catch((e) => {
console.log(e)
})
}).catch((e) => {
console.log(e)
})
// Promise Chaining
add(1,1).then((sum) => {
console.log(sum)
// return a call to add, this will send the returned value to the next chained .then() method
return add(sum, 2)
}).then((sum2) => {
console.log(sum2)
// this one call to .catch() will apply to any error that happens in the chain
}).catch((e) => {
console.log(e)
})