Securing a Svelte Application with JSON Web Tokens

Securing the application with JSON Web Tokens

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the Fullstack Svelte course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Fullstack Svelte, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Fullstack Svelte

Hello. In this lesson, we're starting a module about security. Up until this point, we have been making great progress, but our backend API is unsecured. We will address security in this module by using JSON web tokens. JWTs, sometimes called JOTS, are an industry standard way of representing identity that can be used to authenticate and authorize an API call. And once you have an actual JWT, the security mechanism is pretty straightforward. You attach the JWT to your API request header, and then the backend API valid ates the JWT prior to handling the request. And the library we were using for our front-end API calls, Axios makes it pretty easy to do that. And then on the backend express makes it pretty easy to validate the JWTs as well. So here's an example of what it kind of looks like. So we've got our Git API lunch week. And then when we call that API, we add an HTTP header with authorization, bearer, and then the token. And so the token is encoded and looks like this. And it's signed by an authority and can't be tampered with. And that's what makes it secured. Now we can create JWTs and sign them ourselves and set up a whole JWT issuing system. But we're going to use Auth0, which is a third party service. And it does a lot of stuff for us like generating the JWTs and managing users. And it can even do emails and things like that. It also has a universal login page that we'll use for our login page. So it kind of just makes everything a little bit easier. With that being said, it's still pretty complicated. So we'll try to go slow and explain everything that we're doing in the next couple of lessons. So to get started, head over to Auth0.com and sign up for a free account. The free quotas are very generous. So you can have a production app and do very well with just the free quotas. And once you get your account created for Auth0, you're going to start by creating an Auth0 application. So we're going to call this school lunch front end. So I've logged into Auth0 here. And this is the dashboard in Auth0. So come over to applications. Just ignore this default app and choose create application. We're going to call it school lunch front end and then pick single page web applications and choose create. And then you can ignore this. They do have lots of quick start tutorials and stuff like that to help you depending on what you're trying to do. So next we're going to create a test user. And this will just be what we use for testing kind of helps us make progress. So under users and roles, choose create user. So left menu users roles create user. So I'm just going to use John Doe, create a password and make sure you remember the password you use because we'll need it. And then choose create. So I've got some of the basics set up here in Auth0. Okay, we need to take a few steps to integrate Auth0 into the front end. Here's an overview of how the process works. For the front end, lunch menu admin pages that need security, we'll do a check to determine whether the user is currently authenticated. If the user is not authenticated, we'll redirect the user to the Auth0 log in page. And when we do that redirect, we provide Auth0 with a callback URL. So then the user logs in and Auth0 uses the callback URL and redirects back to our app. Then when we redirect back, Auth0 provides a special code in the URL. So it kind of looks like something like this. And we'll have a little bit of code that parses that URL. And that receives the user's JWT. And so then the JWT gets stored in local storage in the browser. And this makes it persistent across tabs and sessions. And for example, if you close your browser and reopen it, it's persistent in that case. And so then we can use that JWT for our API calls. Okay, so it's a lot of steps and it's definitely a little bit complicated, but hopefully it'll make sense as we work through the code. So we're going to use a library called Auth0Spa.js. Now I had some issues with different versions of this library. So I know that version 1.12.0 works. So you could try the latest version. But if you have an issue, you might want to try 1.12.0. And the issue I was having was with the spelt compiler. So the issue was showing up in the command line terminal window when I was trying to use this library, the whole front and application wouldn't compile. So let's go ahead and npm install this. So over in the front end, npm install Auth0Spa.js at 1.12.0. Okay, so a lot of these steps up here, we're going to use the admin layout component to accomplish those, because we know that that admin layout component gets accessed every time a user accesses an admin page. So it's a nice spot to put this authentication code in. And another thing we're going to do is add a logout button to that component. And so we can start with this. So copy this code here for the button. Go over to the project and find the admin layout.spelt. And we're going to put that logout button right here. Now this button has a click handler. And on click, it's going to call a logout function. So to get this the app to compile, let's go ahead and at least stub that function out. So we'll say const. Logout equals. And we'll just what to say to do for now. Let's go ahead and start the front end and just have a quick look. Oops. npm run dev. Okay, so here's our logout button that we just added. And this is going to show up on any of our admin pages. Okay, so we're going to use the on mount function in the admin layout to handle some of our authentication code. But there's a couple of places where we're going to need to use auth zero config. So let's go ahead and just create a separate file called auth zero config dot JS, put it in the source directory, and then it'll be in one spot and we can use it in the different places that we needed. So front end source new file. Auth zero config dot JS. And then this is the config that we need. And you're going to need to get this information from the auth zero dashboard. So go over to applications. Pick the school lunch front end. And we're going to need this domain. So I'm going to copy that. And that's going to go right here. And then also right here. And then we're going to need this client ID. So copy that. Paste that in. And then our cache location, as I mentioned earlier, is going to be local storage. And that's where the token will be stored once it's received. Okay, so back to admin layout.self, we're going to import this auth zero config that we just created. And then we 're going to import create auth zero client from the library. And then what we'll do is we'll actually instantiate an instance of the client with our config. Then we'll run our authenticated check right there. And then if the user's not authenticated, we'll run this auth zero dot login with redirect. And this is where we'll send the user over to auth zero for login. Otherwise, we'll just set our user like we were doing before and then toggle initialize to true. So let's paste this code in. So we need those two imports. And then we'll paste this into our on mount. So before we were just setting the user and then setting initialize the true, so we're going to add a little more logic here. So now we'll do the auth zero stuff. And then we'll set the user to true. Or excuse me, initialize the user. Okay, at this point, I think we'll get an error. Let's give that a try and then we'll address that. Okay, so yeah, so now if we try to load our app, we're getting an error that says failed to load resource, the server responded with status 403. So we're trying to hit an auth zero endpoint and we're getting a 403 security error. So the reason we're getting that is this is kind of the way auth zero works. We need to set a loud callback URLs and allowed web origins. That way, some other website can 't use your auth zero config and do malicious activities with it. So back in the auth zero dashboard, we need to open the school lunch front end settings and add these URLs. So school lunch under applications, school lunch, front end settings. And then if you scroll down a little bit, you 've got call back URLs. So go ahead and add localhost 5000. And then localhost 5000 slash callback. Now, in a second, we're going to actually create a route under this callback URL. So just go ahead and add it for now. Then under logout URLs, go ahead and add localhost 5000. And then under web origins, also add localhost 5000. And then the save button is down at the bottom. I'll go ahead and put it in allowed origins as well. Okay, go ahead and hit save changes at the bottom. Now let's try this again. Okay, so what happened there is everything was successful from an auth zero configuration standpoint. And it hit our code right here. Since the user wasn't authenticated, it fell into this block right here. And so we got redirected to auth zero. So you can see here, this is the auth zero login page. And so it's actually running on the auth zero domain. And this is from my testing, I just got some data in here already, because I saved the password, but yours will probably look like this. And so now we can log in as that user. Let me check what what I used here. So I used john do at test.com. So put in the username that you used. And then the password that you use. Now if you forgot the password, or you need to change some information, you can change data about this user over here in auth zero. So for example, under actions, you can change the password there. Okay, so hit the login button. Okay, so once you get the password right, hit the login button. And then you're going to get this authorized app message. Go ahead and hit accept. Now if you don't want the user to have to deal with that message over here in auth zero under apis. Choose the auth zero management API. And down at the bottom access settings, you can turn off allow skipping user consent. Okay, so now coming back here, what happened was auth zero redirected us back to our app. And now we see local host 5,000 404. So the reason we're getting this 404 is because it sent us back to callback. And we don't have that setup yet. So right here we said redirect redirect URI was our window.location.origin/call back. And we don't have that. And let me just show you a little bit about window. location. You never looked at it. So over in the console, you can just run JavaScript here. So if I run window.location, this will tell me what's in that variable. So what we're using is origin. And then path name. So right here, we just use origin, which gives us our server name slash callback. And then right here, we use path name. And this will explain this a little later. This allows us to come back to the page that we were already on. Okay. So we got to the all auth zero login page. Now the auth zero login page is what's called universal login. And you can customize the look and feel of that page as well under universal login. So over here, universal login. This allows you to pick some options and you can even customize it completely with the HTML. So if you click login, this is the HTML. So you can you can actually override that. So you can you have a lot of flexibility to change this and make it look how you want it to look. Okay. So handling the auth zero callback. So we need a way to handle the callback when it comes back to our application. So we're going to create a new top level route slash callback and a new component called callback dot spelled. So what what we're going to do is when auth zero redirects us back. In our callback processing, we'll use auth zero dot handle redirect callback. That'll parse this URL and it 'll finish the authentication process. And that method automatically caches the token into local storage. And then we'll use that later for our API calls. So let's create callback dot spelled under source views admin. Source views admin new file callback dot spelled. And then copy this information in. So we're using again, we're using on mount. We're using create auth zero client . And then we're using our auth zero config that we stored in this file. And so what we're doing here is in the on mount function, we instantiate the auth zero library. Then we check the authentication result using this handle redirect callback. And we'll grab the redirect path out of our app dot state. So that's this piece right here. And that way we can put the user back to the page that they were originally on before they got redirected. And then using that redirect path, we'll push that into the browsers URL and that will perform the redirect. And then while this is taking place, we'll just show a little message that says redirecting, please wait. Okay, so that's the callback handling. Now we need to add it to our routes dot JS. So go ahead and import it . And then we'll add it right here. It doesn't need to be nested. It can kind of stand alone there. Okay. So let's give this a try. Get off of this page. I'm going to go to an incognito window to test this. So if you need a fresh browser with nothing cached, this is a good way to test things. So if I go back to the app in the incognito window, I'm going to get redirected again. So put in the username and password. Quick login. Now this time, you know, you may have missed it. It was pretty quick, but it hit our callback component. I saw the redirecting, please wait message. And then it sent me back to the page that I was trying to get to originally. And now if we just take a quick look over here in dev tools under application, local storage. Now you can see the information that was cached here from op zero. So here's our tokens and some other information about the user that's stored in local storage. Okay. We can work on our logout function next. So back in add admin layout dot spelt, we'll finish this logout function. And this will allow a user to click the logout button and log completely out of auth zero. So we just need to use auth zero dot logout. That'll delete the information from the cache and log the user out. So let's copy this piece over. Add in layout dot spelt in the logout function. So we need that in. And then originally we declared let auth zero inside on mount. So we won't be able to access auth zero directly here. So let's move this declaration up to the top of the file so that it can be accessed by both functions. Okay. So let's give this a try. So click the logout button. And that logs the user out. And if you go look in application local storage, it deletes the information from local storage. And then if the user tries to access an admin page again, it'll hit the authentication flow and redirect back to auth zero. And then just watching local storage, you can see the tokens get cached again here locally. So I'm logged back into the application. Okay, so that's a big chunk of work that we just did on the front end. And so we've got the tokens stored locally and we've implemented the redirect and callback flow. But we actually haven't achieved any security yet. So in order to actually achieve security, we need to secure the backend. So this is kind of a good stopping point. If you need a break, otherwise we'll forge ahead and continue working with the backend. So we're going to use some express middleware to secure the backend routes that need to be protected. So we don't want our create update and delete routes to be public. So let's go ahead and NPM install these tools. So we're going to use dot env. And then we'll use express JWT and J W K S R S A. Copy that over to the command line in the backend directory. So shut that down and run this NPM install. And I'll go ahead and start it up again. NPM run node, mon. Okay. So what we're going to do next is create a dot env file. So we need to store some additional secrets in the backend. And so this is where we're going to have our auth zero information that we don't want to be public and we want to keep it in a secure place and not commit it to source control. So we're going to create a dot env file in the backend project. So right here in the root of the backend project new file dot env. And we're going to store environment variable information in this file. And then we'll use this package dot env to actually apply those when the app starts up. So paste this in. And then we need to go to auth zero dashboard and grab this information. So back in the auth zero dashboard, go to applications, school lunch front end. And then we need our domain. So copy that into the env file for each of these environment variables. Okay. So if you're using Git, now is a good time to add the env file to your git ignore file. So I'll go ahead and do that. If you click over here, this will show you your git changes. And here's the env file so you can right click and choose add to git ignore. And that'll handle that. Okay. And then next we're going to work in the backend. And so the idea is we 're going to create our own middleware function. And the middleware will check inbound JWTs. And it will provide the security. So the middleware will reject the request if it doesn't contain the J WT. And it will return a 401 unauthorized response. Otherwise, if the JWT is good, it will allow the response. So let's create a file called authenticate JWT.js. In the backend project. Okay. And then this will be the contents of that file. And so what this is doing is using a couple of libraries to validate the JWT. And then we're providing some configuration from our dot env file. So we're going to get the J WKS URL. So we set that up right there. And then the Auth0 audience and the Auth0 issuer . Okay. And then we need to update our app.js file. So we're going to do a couple of things here. So right now the dot env file is just sitting there, and it hasn't been applied . So we'll use this package called dot env. And we'll put it right at the top of the file. And that will apply the dot env file and create those environment variables on your system that can then be read in the middleware. So let's start with that. So we need to look in app.js. Put that at the top. And this way, all the environment variables will be set up before anything else is instantiated. Then we're going to import our new authenticate JWT middleware function. So copy that. And then let's just demonstrate real quick with Postman what's going to happen. So we still haven't actually applied it yet. So over in Postman, try to run a get against against the lunch week API. So if I run that, I'm getting a 200. Okay. And I'm getting the JSON back. So once we apply this middleware, we're going to add it right here on our lunch week route. So we just add the middleware right there. And now let's try Postman again. And so this time we get a 401 unauthorized in a message that says no authorization token was found. So the middleware is preventing this request because we haven't provided an authorization token in the headers. And so that's pretty simple on the back end. So now wherever we use this middleware, these routes are now protected. So all the routes inside this lunch week router file are all protected now. Okay, so we're almost done with this lesson, but we still need to make one additional front end change. If we test the front end right now, we'll get an unauthorized message. And that's because we haven't configured Axios to actually send in the JWT with the API calls. So just looking at the front end, if we try to use the app and make this API call, we're going to get a 401 unauthorized. So we don't actually have the JWT set in the request headers. And so our back end security is working and it's kicking these requests out. So what we're going to do to address this is create a file called Axios global. And in that file, we're going to configure an instance of Axios with the JWT. And then we'll export that instance of Axios back out from this file. So it's kind of like a little utility that will then use for our API calls. And inside this file, we're going to use interceptors. And so anytime we use this instance of Axios to make a request, it's going to grab the token from auth zero dot get token silently. And that's going to add a header with authorization bearer. And then the token. And then we'll export that back out. So let's go ahead and create a file called Axios global in the front end project. So front end, make sure it's in the source directory. New file Axios global. And then copy this into it. And so what we're doing here is importing Axios, just the vanilla Axios, we're going to import our auth zero configuration and the auth zero library. And then right here, we're going to create a new instance of Axios. And this is important because this gives us kind of a clean standalone instance. If we don't do this, let's say we just do Axios here, it'll actually cause this change to be global. And then anywhere we use Axios, we're going to get this. And so later on, we'll see in the course, we've got a case where we're going to use Axios for user registration. And that'll be a public endpoint, because the user's not registered yet. And so we don't want all of this stuff in here. And so by using this Axios dot create, it gives us a clean, standalone instance that doesn't kind of pollute any other instances of Axios we might use. Okay, so save that. And then all we have to do from here is in the components where we use Axios, instead of import Axios from Axios, we're going to import from dot dot slash dot slash Axios dash global. And so that's going to just go up two directories to get to our Axios dot global file. And then we don't need to change any of this code down here because we've used the variable name Axios here, and we're still using it here. So it's going to use the one from our new file that we just created. So let's import that there. And then import it here also in lunch menu admin details. Save that. And then go have a look at the front end. And so now you can see we're actually getting some data back. And if you look at the network tab. Now we're getting a 200 Okay. And then looking down in the request headers, this is where the magic 's happening. We've got our authorization bearer, and then the token there in the request headers. And so that's how we make the JWTs work on the front end. So fantastic work. We did a lot in this lesson, we integrated auth zero into the front end. And then we secure the backend API routes with J WT security. And then in the next lesson, we're going to transition the application to a multi tenant app that supports different schools, aka tenants. And so right now we just have a single tenant app, all the data is in one database. And so we're going to change the application architecture in the next lesson, so that multiple schools, let's say a school in Virginia, and a school in California could both use our app. And their data would be separated from one another and kind of isolated. So that's it for this one. Thank you for joining us. And we'll see you next time.