So you just got a little primer on security from Meeka, I'm going to reiterate some of the things she mentioned, mostly that security is hard.
I was actually at a node.js meetup last year where someone gave a talk about security and they said when the internet first came around, there was a list of like, 9 or 10 security issues that needed to be addressed that we needed to find a permanent solution for, and as of 2015 not a single one of them had been crossed off the list.
So we are all notorious for sucking as security. But there are some precautions we can take with our apps to at least tryyyyy to make them more secure. As app developers, our main concerns with security are going to be (1) user authentication and (2) transmitting application data in a verifiable and trusted manner.
One of the standards we have available to us that help with both of these features are JSON Web Tokens. JWTs for short, apparently some people pronounce them Jots. But essentially they look like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
It kind of looks like an API key you might use when youre accessing an open API service, right? So its just a string of plain text that looks like a bunch of gobbly gook.
We can send this along with any of our requests by using it as a HEADER, QUERY STRING, or REQUEST BODY.
What are headers? What are query strings? What are request bodies?
Ok so why would we send a JWT along with our requests? JWTs are going to verify a couple of things about the request, like who its coming from, what time they requested it, and what information theyre trying to send.
Locking down all of this information and encoding it in a JWT is going to ensure that none of our requests are being tampered with before they hit the server.
We'll get into the underlying structure of a JWT in a minute, and how all of this information actually gets put into that long gobbly gook string, but let's do some pretend hacking first. I downloaded a program called Charles which gives you some advanced network debugging tools. It's actually a pretty useful tool but I'm going to use it to mimic what a hacker might do.
Ok let's take a quick pomodoro and then we'll talk about the underlying structure of a JWT.
Ok, so we already mentioned that JWTs verify a couple different pieces of information. JWTs are just an encoded string and when we decode them, we get 3 distinct parts:
- a Header object - JSON OBject tells us the type of token we have (in our case, a JWT), and the hashing algorithm used to encode the final JWT string.
- a Payload object - this is where a lot of the main information is stored. this is a super bare-bones example of a payload, but normally this will include information like the expiration time of the token, the application that issued it, and any contextual information we want to pass along so for example we might want to verify that our authenticated user is an admin, which will grant them more permissions in our application. Not necessarily the same as your request body, but it is really the only part of a JWT that can vary as much as it does
- and a Signature - this is a super key piece of the JWT. The signature is what's used to verify the sender of the token and to ensure that the message hasn't been changed anywhere along the way, so this is what's going to prevent that scenario that we demoed on dictionary.com - the signature would no longer match if the data had been changed in any way on its way to the server. the signature is a hashed value made up of the header, the payload, and some secret, like when you get an API Secret from 3rd party vendors.
So all of this is still a little confusing. Don't worry if you didn't take away 100% of that. Like most web APIs and specs, they usually aren't super user-friendly and everybody rushes to build something better on top of it. Today we're going to use Auth0 which is a service that gives us user authentication using JWTs, similar to Firebase, it will abstract away all of the encoding and signing and all of that nonsense. Then we're going to leverage our Auth0 credentials to secure an endpoint.
Let's take an extended pomodoro, if you want to follow along, before we get started again go ahead and clone this repo, sign yourself up for Auth0 and create a client for the app. Enable google authentication, select Single Page App with react as your framework, and add /login to the list of allowed callback urls in your settings.
[Show them my Auth0] [Show them my .env file where we'll store our info] [Add shit to the main/routes.js file]
So now we want to not allow unauthenticated users to see this home page and we want to redirect them to the login page
[Require auth for home route]
Now we need to wire up our login button
[Wire up login button]
Ok so that's an example of securing a client-side route, next we'll secure a server-side endpoint.
POMODORO
[Add shit to server side, restart server] [Try posting a new message] [See the 401 in the network tab] [Update the POST request with headers]