💻 Protecting Your Jamstack Applications - A Few Concepts You Need to Know
Presented by: James Quick
Originally aired on May 25, 2023 @ 8:00 AM - 8:30 AM EDT
Cloudflare's Full Stack Week continues with a segment on Protecting your Jamstack! In the last couple of years, the Jamstack has drastically changed the way that we build websites. With these changes happening so quickly, are yourself... Are you caught up on best practices for protecting your Jamstack applications?
In this talk, we'll explore authentication and authorization and how those two have changed with the evolution of the Jamstack. We'll also discuss how to track and validate users client-side and server-side using cookies, sessions, and JSON Web Tokens. Join this talk to make sure you're not behind the times when it comes to modern authentication in the Jamstack.
Visit the Full Stack Week Hub for every exciting announcement and CFTV episode — and check back all week for more!
English
Full Stack Week
Transcript (Beta)
What What is up, everyone?
Welcome to the developer speaker series. And thank you for thank you to Cloudflare for having me to talk about protecting your apps in the Jamstack, something that I am particularly really excited about.
So first off, who am I?
My name is James Q. Quick. I'm a developer, speaker and teacher. I've been doing some combination of those three things professionally for about 10 years now, which is kind of fun.
Also kind of hard to believe and recently just had a job change.
So today is actually my very first day as a staff developer advocate at PlanetScale.
I was previously at all zero. You'll see a little bit of all zero in this presentation today.
And that was a big inspiration for a lot of the pieces of this talk.
So we'll talk about through throughout this talk. But today is my first day as a developer advocate at PlanetScale.
I also get to represent a couple of other really cool companies in Twilio, Cloudinary and Hashnode, and they each have different names for their programs.
But it's basically kind of an advocacy group for each one of these cool companies.
So you'll see maybe an appearance by a couple of those in this talk as well.
So all of that said, basically, what I do is I teach developers through content.
That's kind of a tagline that I have started to adopt.
And again, this like all the things I do really stem from a place of loving to teach, loving to help people.
And specifically in this case today to help people understand how to protect their applications in the JAMstack.
So a couple of expectations. This is not a security deep dive. I will reference some security concepts, some authentication flows.
I won't go into detail about those.
So I will kind of give you the opportunity to go and do some extra research or go and watch some extra videos for extra background if you want them.
But I'm really just going to be focusing this on app builders, just kind of your average developer who's building an app or a SaaS or whatever it is that you're working on.
This is not geared towards security professionals, but people that are actually building applications.
A quick heads up. This talk is a little bit biased towards two things.
React is the framework that I use the most for building web applications.
And then Auth0, as I said, I used to work for Auth0. And that's a we'll show a couple of snippets in here of code that are using Auth0 SDKs with React.
But I will try to make sure to call out that those things, although the snippets are specific to these two things, they don't have to be and they should apply to other frameworks and tools that you might use.
So setting a little bit of context here, the first question is, how do we even auth?
How did we do this before?
And really quickly, auth can mean two different things, authentication and authorization.
And we'll talk a little bit about the difference between those two here in a minute.
But how do we even auth? Well, there's the traditional server auth with session and cookies.
And if you think about a PHP application or Ruby on Rails application, your traditional server side rendered content.
What happens when you do auth is that you have a browser, you go to a login page, you enter in your email and your password, and you send this login request to the application server.
The application server will then take your email or username and password.
It will go and look it up against the database.
It will then get a response back. And then based on that, it will determine whether or not the user should be logged in and or return back to the actual calling application.
But the thing that happens behind the scenes is with that return, there's a cookie that gets attached to that request or in that response.
And that just shows that the user is now logged in. And that cookie could be a JSON web token, which we'll talk about in a minute.
It could be just the unique identifier for the session.
It could be a few different things.
But there's something in that cookie that allows the server to then track that logged in user.
What then happens is with subsequent requests from the browser, from that client application, if you make a request to an API endpoint called my stuff or whatever it is to your back end server, what happens is that cookie gets sent along with that request.
And then if that cookie is, for example, a session ID that represents an actual session, it can go and look that ID up in the session table and the database, then respond back to say, yes, the user is good, and then allow the user to have access to whatever it is that they're trying to do.
So all in all, this is kind of the way that authentication happened primarily for a long time and still is very popular.
But this is this idea of the server side rendered content using cookie and sessions to be able to track the user.
Now, there's a couple of things that we're going to mention, we won't go into details about this, but these are two different workflows or flows for authentication and authorization.
So OAuth2 is specifically for authorization. OpenID Connect is for authentication.
And the difference between those two is authentication is just proving that you are who you are.
Authorization is do you have access to do the thing that you're trying to do or what do you have access to do?
So who you are versus what you have access to do.
Now, these are a couple of the terms that I'll kind of gloss over here there.
It's not important for us to go into the details. Just know that these are two pretty common flows to achieve authentication and authorization.
Now, the output of these two are a couple of different types of tokens.
So from going through the OAuth2 flow, you can get back an access token. An access token allows an application access to a resource.
OAuth, OpenID Connect, excuse me.
The output of that is what gets returned is an ID token, which keeps information about the logged in user.
So the ID token is just information about the user.
The access token is this token of some sort that can be sent to an application that proves access or lack thereof to a given resource.
We'll talk more about JWTs here in a minute, but access tokens can be in the format of a JWT, a JSON Web Token.
ID tokens are in the format of a JWT, JSON Web Token. So with that said, what actually are JSON Web Tokens?
JWTs are JSON Web Tokens, and they typically have three parts.
You've got the first part of this token before the first period is the header that keeps information about the algorithm and the type of token this is, in this case, the JWT.
The next part between the first and second period is the body, and this is where you can kind of keep whatever information you want in here.
Sub is a standard claim for a unique identifier for a user.
You might have a name property. You might have email. You might have an issued at timestamp.
You might have an expiration timestamp. There's a lot of things that you can add in here.
And the last part of this is the verify signature. And what happens is you take the base 64 year old encoded version of the header at a dot, then the base URL, base 64 year old encoded version of the payload.
You combine those together and then you sign them with some sort of secret.
And this is the key to how JWTs works.
This means that as I receive a JWT, if I'm a back end server of some sort, I can then validate that that token came from an appropriate place and was signed with the appropriate key.
Now, if you ever want to see what an actual JWT looks like, a JSON Web Token, you can take that actual token.
It's just a string and you can copy and paste it into a website called JWT IO.
You can actually see what those different pieces are.
So it's a little bit of the the authentication authorization context, but I really want to make this applicable again to people who are building applications specifically in the JAMstack.
But JAMstack is still a relatively vague thing.
The interesting thing about the JAMstack is that there's really no great definition for what the JAMstack is.
Lots of people talk about the JAMstack.
It's one of those buzzwords that we hear all the time, but there's really not a great definition for exactly what it is.
But I guess you can start with the acronym.
JAMstack, the J-A-M stands for JavaScript, APIs and Markup.
And if you think about these things, JavaScript, APIs, Markup, none of these things are really new.
We've had these things for a long time. You've got JavaScript where you can do vanilla JavaScript or you can use a framework like React, Angular, Vue, Svelte.
You've got APIs. I've got a couple there. You've got Markup where you're creating HTML or using Markdown or whatever kind of Markup you're using or generating.
Those things have been around for a long time. Nothing inherently about them individually is new.
So to me, the JAMstack is really all about a new way to combine old things.
JavaScript, APIs, Markup, they've been around, but now we're using them and combining them in a different kind of way than we had in the past.
And this also comes with a focus on static content. Now, static content is another one of those very ambiguous terms.
What even is static? How do you define what static is in this context?
Well, we can start with Wikipedia, the source of all truth, as you probably would yourself.
And they go on to define it as a web page delivered to the browser exactly as stored in contrast to a dynamic web app.
And so there's this kind of big, long definition, and I've shortened it a little bit here to be a set of files that can be hosted as is on a CDN.
Well, what all does that actually mean? A CDN is a content delivery network, and this means that a content delivery network can take a set of static files, it can replicate them all over the world.
So if I make a request to this website from the United States, it's going to serve those static files from some sort of server in the United States.
If I'm in Japan and I make that same request, it's not going to call all the way out to the US.
It's going to have whatever closest server or data center nearby to serve that response to me.
So I'm not having to make a request all across the world.
I get the closest files and have them sent directly to me.
And the reason these are static is because they don't change. They're just a set of files that get hosted in that CDN.
Now, if only we had, we knew of a CDN, that that would be a good use case for this.
Well, actually we do, and Cloudflare is one of those for hosting these what I call static applications based on that definition of a set of files.
And the really cool thing about having static sites, just those static files, is that you don't need to create or maintain a server.
If you want to deploy your site to Cloudflare, you connect it to a GitHub repository.
You might have a build command in there, or it might just be a set of static files itself.
But you don't have to create and or maintain and or patch and or update a server yourself.
That stuff is taken care of for you. So what are the benefits of that?
Well, there's actually lots of them. Performance is one. If you think about these are static files, they're not being generated real time.
That means you're able to accept a lot of requests and respond to a lot of requests really quickly because it's just sharing that file with that incoming request.
You're not having to go and interact with a database.
You're not having to do any extra additional logic.
You're just sending back those files. That's really nice.
Now, in addition to that, you get high security. This is, I'm not having to patch a database or excuse me, patch a server, update a server, that sort of thing.
And that's where a lot of hacks actually come from or from servers or platforms that are not updated, not patched, but I don't have to worry about that myself.
Scalability, similar to performance, a lot of requests come in, they just get responded to.
You don't really get overwhelmed. If you have Christmas cookies that you, or holiday themed cookies that you sell at Christmas and your requests spike up, you don't really have to worry about it.
It can handle those requests spikes.
And then lastly, excellent developer experience. I mentioned earlier, if you're deploying a site to Cloudflare, you connect to a GitHub repository, you tell it what command to run.
And that's basically it. You've got your site deployed and up and running in literally just a few minutes.
And there's lots of other aspects of the JAMstack that I really enjoy, but the developer experience of the JAMstack is really, really incredible.
And it's absolutely my favorite aspect of the JAMstack.
Now, we talked a lot about static sites.
The question might then become, do static sites mean or imply static experiences?
Well, to answer that, let's go back to our definition. Remember our definition is a set of files that can be hosted as is on a CDN.
Well, if we go and look at some popular frameworks, things like Angular, React, Vue, Svelte, the thing that all of these have in common is that each one has some sort of build step.
And the output of running that build is a set of static files that you can then host on whatever host provider you want to, including Cloudflare as a pretty good option here.
Now, this means that although these are considered static files, and I would deem them static sites, obviously these are very dynamic because you can do anything dynamically on the front end that you want to with the SPA frameworks like Angular, React, Vue, and Svelte.
So let's transition a little bit and talk about the three forms of content in the JAMstack.
So they are CSR, the acronym, CSR, SSG, and SSR.
So CSR stands for client-side render that your traditional Angular, React, Vue, Svelte that we just looked at.
There's SSG, which is statically generated, which we'll get into in a second.
And then there's SSR, which is server-side rendered.
And it's kind of funny, server -side rendered is kind of the way we did things for a long time, and now we've kind of come full circle to incorporate that into JAMstack.
We'll talk about that in a minute as well. But let's start with client-side render.
Let's start with CSR. Well, we go back to these frameworks, Angular, React, Vue, Svelte, and this is kind of what we're talking about, these front-end frameworks where we're doing everything on the client.
We ship a bunch of JavaScript to the client and everything takes place on the front end.
So in that case, how do we do off-end, which is authentication, and off-Z, which is authorization?
That actually threw me for a loop one time when people mentioned those, so I want to clarify what those acronyms are, but authentication and authorization.
And I'll just do a quick ask or remember, or ask you to remember, that we talked about OpenID Connect and OAuth2.
Those are OpenID Connect is for authentication, OAuth2 is for authorization.
And so we can use those workflows on the front end to handle authentication and our authorization.
Now, the way this works is from that front-end application, you can redirect the user over to the authentication server, authorization server, have the authentication take place on that end, and then redirect the user back.
Now, if you're using some sort of regular web application that can keep a secret, you can do that kind of no problem, but in the front end, you have a little bit of issue because you can't share any secret credentials.
And this is where you add a little fairy dust to this with something called Pixie, which is proof key for code exchange.
Again, not super important, the details behind the scenes of what's going on, that's something you can look into more if you're interested, but basically Pixie allows you to have to implement OAuth2 and OpenID Connect from the front end in the most secure way possible.
So that's one way that you can do your authentication and your authorization.
Now, the next thing is how do we get dynamic data?
We talked about these are like static sites in the sense that we can host them as a static set of files, but we still want interactions, right?
We still wanna have live, real dynamic data. How do we get that?
Well, in the Jamstack, we can fetch it from an API using JavaScript. Did you see what we did there going back to the Jam and Jamstack JavaScript APIs and markup?
We can make API requests to get that content using our JavaScript. And then the question becomes, well, where do we actually get that data?
Well, there's lots of really cool options.
You can use a traditional database, you can use a headless CMS, which are becoming more and more abundant and popular.
You can use a third-party API.
You can get the data from someone else that you don't even have to create the data yourself.
And as this is my first day with PlanetScale, I would be remiss not to mention PlanetScale as a pretty sweet database option that you can use for storing your data also.
Now, regardless of which of these options you use, you'll recognize that the goal of these services is to make it as easy as possible for you, the developer, to build your applications and access that data from that data source, whichever one you choose.
So you may look at an SDK on NPM. This is for sanity.
And inside of this documentation, you start to notice a couple of things, and similar SDKs, you start to notice a few things that you might need.
You might need an API key, a secret key, an app ID, an auth token, and maybe you don't quite know what to do with those.
The key thing here is that all three of these, or all of these except for app ID are actually private credentials that you shouldn't share with anyone.
And unfortunately, the browser is not safe. Your source code is also not safe because there are developer ninjas out there in the world that are waiting for you to expose these secret credentials so that they can take them and use them for nefarious reasons, whatever they want to.
So the key here is never to reveal your secrets.
And a big hint, this is going to be a theme in the rest of what we talk about in this talk.
So if I say you want to interact with these data sources, but you can't give up your secret credentials, what do you actually do?
Well, I'm going to push this off for a little bit and say we'll come back to that in a few minutes.
And let's get into our next type of content in the JAMstack, which is statically generated content.
You'll hear the acronym SSG stands for Static Site Generators.
Now there's a lot of really cool options out there for this.
There's Jekyll, there's Nuxt, which is built on top of Vue.
There's Gatsby and Next, which is built on top of React. There's Eleventy, which is built on top of Vanilla JavaScript.
And there's Hugo, which is built on top of Go.
So there's all sorts of options for statically generating content.
And I guess we should start by really defining what static site generators are, or where does that static content actually come from?
Well, with a static site generator, you access your data, you interact with the data source at build time.
So as you go through this build step, you actually go and grab whatever information you need from the database.
The common example is blog posts.
And you use that to go ahead and pre-create all the templates, all the HTML, all the markdown for those pages.
The interesting thing about this, which is a little inconvenient, is if you add something new to your database, your data source, it won't just automatically show up in your application.
You actually have to do an entire new build for that stuff to show up because we only interact with that data source at build time.
But the benefits of this are we get into the Jamstack stuff that we've talked about.
Speed, security, development experience, ease of use, pretty cool.
Now the alternative to this, a dynamic site, this is something like your Ruby on Rails or server-side rendered content.
You make a request to your data source or you interact with your data source real time.
So I make a request to a website for a blog post.
It sends that request to the server.
The server asks the database for that information. It gets it. It generates the markup, then sends that back to the browser.
So it interacts with that data source in real time.
The cool thing about this is if I add a new blog post, for example, I don't need to do an additional build.
That content is already there because it's going to go and grab it from the database real time when I make that request.
Now, our benefits, other benefits, no extra build is necessary. And this has kind of been the way we've done things for a long time.
This is an established strategy and it works really well.
So the big difference here, the key thing with static site generators is that it gets data at build time and generates markup.
The big implication here is that this means that secret keys are okay because that build step actually runs on a server, a server that can keep a secret, which means you can add your secret key as an environment variable on that host, on that server.
That secret key can be used during that build step solely on the server and never sent and exposed in the browser.
So that's really cool. So that solves a little bit of our problem for data that we want statically.
And then the question becomes, so how do we do auth n, authentication, and auth z, authorization?
Well, the really cool thing, if you look at frameworks like Nuxt and Gridsome and Gatsby and Next, these are Vue and React frameworks, they have this process that you can tap into called rehydration.
And rehydration is JavaScript that runs on the browser after you've already sent this application to the user.
If you send the user the static content, then you have rehydration where you can run JavaScript and you can basically do anything that you could before with your regular SPA frameworks, React, Angular, Vue, Svelte.
So the answer to how do you do authentication authorization is actually the exact same or can be the exact same as what you did before when we talked about just regular SPA frameworks.
So you can get into OAuth2, OpenID Connect, you can get into Pixie.
Again, things for you to maybe look into more after this talk.
Static sites are nice, but we still may want to have some sort of dynamic data.
We want to have comments on our blog post as an example.
How do we get those dynamically? Or in general, how do we get dynamic data with a static site generator or a statically generated site?
Well, we have that rehydration process where we can do anything that we did before, which means we can then fetch data from an API using our JavaScript still in the Jamstack.
And this leads us to this next topic, which is serverless functions. Now serverless functions are really, really cool in this case because you can run code with minimal effort and keep a secret.
I mentioned that keeping a secret is a theme to this talk.
So you can run code with minimal effort and keep a secret. You kind of just write a function and magic happens behind the scenes.
And where can you host your serverless functions?
Well, Cloudflare is just a really good fit for this talk because you can host not only your static applications, but also your serverless functions alongside them inside of Cloudflare.
The benefit of this is that means that we can send access tokens along for the ride.
From the front end, we can send a request to our serverless function and we can attach that access token that we talked about before.
And you configure your serverless function to not only validate that access token, but then use the information inside of it to determine whether or not the user should have access to what they're trying to access to.
So how do we do authorization in this case, authentication and authorization?
We have the user logged in on the front end.
We grab that access token. We send it to the back end with each request and let the back end do its thing to figure out whether or not the user is authenticated and then authorized to do the thing that they're trying to do.
So the last piece of content in the Jamstack is server-side rendered content, SSG, or actually that is not SSG.
This should be SSR. I forgot to update that.
I apologize. But the funny thing about this is that the Jamstack has come full circle because server-side rendered content is the way the web worked for a long time.
It's the standard thing that we talked about that's an established strategy that's been around for years that works really well.
And people in the Jamstack, the Jamstack ecosystem looked at the limitations of statically generated content and they realized, hey, we might also want to have a server-side rendered content be an option for us as well, which then led to the creation of what I call the hybrid Jamstack or frameworks that can do it all.
Going back to the couple of frameworks we mentioned already, Next.js is a framework built on top of React.
Nuxt .js is a framework built on top of Vue.
SvelteKit is a framework built on top of Svelte and they can do it all.
Now this gets really interesting because since they can do all of this, CSR, SSG, SSR, client -side rendered, statically generated, server-side rendered, how do we then do AuthN, authentication, and AuthZ, authorization?
Well, let's take a look at since these are hybrid frameworks and they can do it all, we can kind of combine all the things that we've already talked about.
Remember when we talked about OpenID Connect and OAuth2? Remember when we talked about adding a little fairy dust with Pixie on top of that to do that from the front end from a single page application?
You can do the exact same stuff that you want to.
You can also use OpenID Connect and OAuth2 without Pixie if you're doing that from the server.
You're doing kind of your traditional server-side auth, which means additionally since you have the ability to server-side render content, you can do your traditional server auth with Session and Cookies where a user sends a request to the back end, logs in, get validated, and then they have some sort of piece of information that's tracked inside of a cookie to keep track of that logged in user.
So the short answer is with hybrid JAMstack frameworks, you can do it all.
All the different methods and options you have for authentication and authorization, you can do them here, which is really nice, but also comes with a little downside of now it's a little trickier and you have to make educated decisions about how you handle it and when and where different scenarios make more sense.
So I want to wrap up here with a little bit of an example using the Auth0 SDK for Next.js, Next.js being one of those hybrid frameworks which allows you to do it all.
Cool thing about this is after you install this package, you export one function in a dynamic API route.
Behind the scenes, what this does is it creates an API route for log in, log out, callback route, and a me route.
It does all that for you. And then after that stuff is configured with that one line, now you basically just handle log in by redirecting the user to a log in route in your application.
So see, this is just a link to slash API slash auth slash log in.
So from the front end to handle log in, all we do is send the user to the log in route.
This is where we take advantage of that server side rendering. Now the difficult thing or seemingly difficult thing now is we're handling authentication on the server side by redirecting the user to a server side route and then sending the user over to do authentication and redirecting them back.
But now we also have access to use user hooks in the front end.
Now this is similar to something you would see in a regular react application or regular view application where now you need some sort of way to track the log in user on the front end.
And that's where this user hook comes into play so we can handle authentication on the server.
We can then track the user on the front end with this user hook.
And then two more things we can do. We can protect our individual pages with this little helper function with page auth required that will protect the page itself so we can protect the page before the user even navigates there to say, hey, you can't be here if you're not already logged in.
And in this case, if you're not an admin.
We can do the exact same thing or similar thing on the API routes, the server side API routes with using this hook called with API auth required.
So we're able to handle authentication on the back end, track the user on the front end and the back end, and then gate our pages and API routes with these different hooks that we have access to with the Auth0 Next .js SDK.
Pretty, pretty cool. There are other SDKs that will do similar things.
Next auth is one that people really enjoy as well.
So definitely recommend going and check those out. But any of those that can help make this process as easy as possible is definitely going to be a win.
All right.
Thank you all for hanging out and viewing the talk. I hope that you enjoyed it.
If you want to check out the slides, you can find them on my website at jamesqquick.com slash talks.
There will be a line item there for this talk in particular.
So you can go and grab these specific slides. And lastly, again, just thank you.
If you have any additional questions, you can reach out to me through my website at jamesqquick.com and you can find me on social media on Twitter and TikTok and YouTube and a few other places at jamesqquick also.
So thanks again for checking out the talk.
I appreciate it and I hope you have a good day.