From 3fcd2f0bcb10a52ae5a96c01c5c99f9fafe0257e Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sat, 10 May 2025 13:17:25 +0100 Subject: [PATCH 01/20] Write introduction and lesson overview --- nodeJS/authentication/json_web_tokens.md | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 nodeJS/authentication/json_web_tokens.md diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md new file mode 100644 index 00000000000..58355f67ce6 --- /dev/null +++ b/nodeJS/authentication/json_web_tokens.md @@ -0,0 +1,37 @@ +### Introduction + +Previously, we learned about using sessions to persist logins and authenticate users. Session data would be stored server-side and the client issued their session's ID via a cookie. When authenticating, the session store would be checked for a matching session. This kind of authentication is "stateful". + +An alternative approach to authentication, and one that is common with REST APIs, is to use "stateless" authentication with JSON web tokens (JWTs). The main difference between using stateful and stateless authentication is where the authentication data is stored: client-side or server-side. In this lesson, you will be introduced to stateless authentication using JWTs. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- Describe what JSON web tokens (JWTs) are. +- Describe what stateless authentication is. +- Explain some of the differences between authentication with sessions and JWTs. +- Implement basic stateless authentication with JWTs. +- Describe potential security issues with stateless authentication. + +### CUSTOM SECTION HEADING + +CUSTOM SECTION CONTENT. + +### Assignment + +
+ +
+ +### Knowledge check + +The following questions are an opportunity to reflect on key topics in this lesson. If you can't answer a question, click on it to review the material, but keep in mind you are not expected to memorize or master this knowledge. + +- [A KNOWLEDGE CHECK QUESTION](A-KNOWLEDGE-CHECK-URL) + +### Additional resources + +This section contains helpful links to related content. It isn't required, so consider it supplemental. + +- It looks like this lesson doesn't have any additional resources yet. Help us expand this section by contributing to our curriculum. From 4a86ca9120867196380d5201161746715d7c7272 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sat, 10 May 2025 17:40:44 +0100 Subject: [PATCH 02/20] Write section on JWT structure --- nodeJS/authentication/json_web_tokens.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 58355f67ce6..b14a875ddc6 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -14,9 +14,19 @@ This section contains a general overview of topics that you will learn in this l - Implement basic stateless authentication with JWTs. - Describe potential security issues with stateless authentication. -### CUSTOM SECTION HEADING +### JWTs -CUSTOM SECTION CONTENT. +JWTs are tokens that allow us to send information between various clients and servers, or even between servers. Like with session cookies, they are signed which involves hashing the rest of the JWT (including the payload) with a secret known only to the issuing server. + +JWTs are often not encrypted, only encoded in base64. You can use any JWT decoder such as [jwt.io](https://jwt.io/), paste a JWT in and see the contents; the important part is the signature. For example, here is an example JWT: + +```text +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiT2RpbiJ9.FtLFoA9kG8B_gvKz0nEzx4uDYAlsgWhxTGEUfinYcf8 +``` + +You don't need to understand the inner workings of JWTs but let's peek at what's going on. Paste that into [jwt.io](https://jwt.io/) and you'll see a payload of `{ "name": "Odin" }` along with an "invalid signature" warning. If you change the secret in the "verify signature" box to `theodinproject` then paste the JWT back in, it'll say "signature verified". If you change any part of the JWT contents, such as the payload or secret, you'll see the JWT value change. In particular, the signature section changes *dramatically*. + +This is how a server can verify if it did indeed issue an incoming JWT as well as verify if it had been tampered with, as a different payload would generate a different signature, even with the same secret. Unless you also know the secret, you would not be able to create the correct signature for the changed payload. ### Assignment From fbff69d0a7e2f73315f765b37bfd8600748a70d2 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sat, 10 May 2025 17:58:15 +0100 Subject: [PATCH 03/20] Start stateless auth intro --- nodeJS/authentication/json_web_tokens.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index b14a875ddc6..873d83a9800 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -28,6 +28,12 @@ You don't need to understand the inner workings of JWTs but let's peek at what's This is how a server can verify if it did indeed issue an incoming JWT as well as verify if it had been tampered with, as a different payload would generate a different signature, even with the same secret. Unless you also know the secret, you would not be able to create the correct signature for the changed payload. +### Stateless authentication + +We can use JWTs to authenticate our APIs in a stateless manner, that is the server side does not need to store any of the authentication data itself. It only needs to store a secret so it can generate tokens signed with that secret and send them to the client. Then for incoming requests, it needs only verify that it made the incoming JWT and it has not been tampered with, and can deserialize the payload if verified. Else, it can unauthorize the request. All this occurs without needing to make a database call to grab the authentication data (like with sessions, a stateful solution). Neat, no? + +This is not all sunshine and roses, however. There are always tradeoffs, especially when security is concerned, and we will discuss these in more detail in a later lesson where we compare stateful authentication with sessions and stateless authentication with JWTs. Nonetheless, you're likely to encounter this sort of authentication at some point out in the wild, so it's good to get some experience with the concept (even if a basic implementation). + ### Assignment
From 617aaf593fc3aa50d0ffec360c0213c6e046e1d0 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sat, 10 May 2025 19:34:03 +0100 Subject: [PATCH 04/20] Set up minimal app example code --- nodeJS/authentication/json_web_tokens.md | 44 ++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 873d83a9800..a02d5d13929 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -34,6 +34,50 @@ We can use JWTs to authenticate our APIs in a stateless manner, that is the serv This is not all sunshine and roses, however. There are always tradeoffs, especially when security is concerned, and we will discuss these in more detail in a later lesson where we compare stateful authentication with sessions and stateless authentication with JWTs. Nonetheless, you're likely to encounter this sort of authentication at some point out in the wild, so it's good to get some experience with the concept (even if a basic implementation). +#### Setup + +Like with the Sessions lesson, make a new database within `psql` with a `users` table: + +```sql +CREATE TABLE users ( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + username VARCHAR ( 255 ), + password VARCHAR ( 255 ) +); +``` + +Again, we will make a very minimal Express app. Since we've just been learning about REST APIs, we won't be using EJS views but responding with JSON from our endpoints. Since we won't be making a website for this, you should [download Postman](https://www.postman.com/downloads/) and use that to send requests to the API. + +```bash +npm install express dotenv pg jsonwebtoken +``` + +```javascript +// app.js +require("dotenv").config(); +const { Pool } = require("pg"); +const express = require("express"); + +const pool = new Pool({ + // add your db configuration +}); + +const app = express(); + +app.use(express.urlencoded({ extended: false })); + +app.get("/me", (req, res) => { + res.status(401).json("You are not logged in!"); +}); + +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => { + console.log(`App listening on port ${PORT}!`); +}); +``` + +Since we're going to use stateless authentication, the server does not need to store the authentication data itself and so our setup code is a little simpler this time round. Run your server with `node app.js` and test it works by using Postman to send a GET request to `/me` at the appropriate localhost port, e.g. `http://localhost:3000/me`. You should get back a 401 with the string `"You are not logged in!"`. + ### Assignment
From 1aed4b3bc12145c16baf8286ea95b0deacc44953 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sun, 11 May 2025 13:54:46 +0100 Subject: [PATCH 05/20] Change tutorial setup to example snippet No need for a full handholdy tutorial. Just highlight the differences between stateful/stateless and session/JWT --- nodeJS/authentication/json_web_tokens.md | 58 +++++++++--------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index a02d5d13929..8886a8e31ba 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -2,7 +2,7 @@ Previously, we learned about using sessions to persist logins and authenticate users. Session data would be stored server-side and the client issued their session's ID via a cookie. When authenticating, the session store would be checked for a matching session. This kind of authentication is "stateful". -An alternative approach to authentication, and one that is common with REST APIs, is to use "stateless" authentication with JSON web tokens (JWTs). The main difference between using stateful and stateless authentication is where the authentication data is stored: client-side or server-side. In this lesson, you will be introduced to stateless authentication using JWTs. +An alternative approach to authentication, and one that is common with REST APIs, is to use "stateless" authentication with JSON web tokens (JWTs). The main difference between using stateful and stateless authentication is where the authentication data is stored: server-side or client-side. In this lesson, you will be introduced to stateless authentication using JWTs. ### Lesson overview @@ -34,49 +34,35 @@ We can use JWTs to authenticate our APIs in a stateless manner, that is the serv This is not all sunshine and roses, however. There are always tradeoffs, especially when security is concerned, and we will discuss these in more detail in a later lesson where we compare stateful authentication with sessions and stateless authentication with JWTs. Nonetheless, you're likely to encounter this sort of authentication at some point out in the wild, so it's good to get some experience with the concept (even if a basic implementation). -#### Setup +### Generating JWTs -Like with the Sessions lesson, make a new database within `psql` with a `users` table: - -```sql -CREATE TABLE users ( - id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - username VARCHAR ( 255 ), - password VARCHAR ( 255 ) -); -``` - -Again, we will make a very minimal Express app. Since we've just been learning about REST APIs, we won't be using EJS views but responding with JSON from our endpoints. Since we won't be making a website for this, you should [download Postman](https://www.postman.com/downloads/) and use that to send requests to the API. - -```bash -npm install express dotenv pg jsonwebtoken -``` +Back in the sessions lesson, when a user successfully logged in, their ID was serialised to a session which was saved to the database, and a cookie sent back to the client with the signed session ID. With JWTs, a very similar process occurs, just a JWT is created and sent instead, and nothing gets saved to the database. You can generate JWTs using the [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken) library. For example, in a login route middleware: ```javascript -// app.js -require("dotenv").config(); -const { Pool } = require("pg"); -const express = require("express"); - -const pool = new Pool({ - // add your db configuration -}); +// importing the jsonwebtoken library somewhere appropriate +const jwt = require("jsonwebtoken"); + +// somewhere in a login route middleware +if (user?.password === req.body.password) { + const token = jwt.sign({ + id: user.id + }, process.env.SECRET, { expiresIn: "1d" }); + + res.set({Authorization: `Bearer ${token}`}).json("Login successful"); +} else { + res.status(401).json("Incorrect username or password"); +} +``` -const app = express(); +There are many ways JWTs can be sent to and from servers, such as in the response's "Authorization" header via the [Bearer scheme](https://security.stackexchange.com/questions/108662) or via httpOnly cookies. Since we have not yet covered how to handle cross-site cookies, the example above sends the JWT as a bearer token in the response's Authorization header. -app.use(express.urlencoded({ extended: false })); +
-app.get("/me", (req, res) => { - res.status(401).json("You are not logged in!"); -}); +#### JWT payloads and sensitive data -const PORT = process.env.PORT || 3000; -app.listen(PORT, () => { - console.log(`App listening on port ${PORT}!`); -}); -``` +Remember that JWTs are sent to and stored on the client. If a malicious party is able to access the token at any point, they can read its contents. While you should not need to do so anyway, **do not store sensitive data in a JWT.** -Since we're going to use stateless authentication, the server does not need to store the authentication data itself and so our setup code is a little simpler this time round. Run your server with `node app.js` and test it works by using Postman to send a GET request to `/me` at the appropriate localhost port, e.g. `http://localhost:3000/me`. You should get back a 401 with the string `"You are not logged in!"`. +
### Assignment From 2f9008f91967a543dab718b49c7ae66f4dd67506 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sun, 11 May 2025 15:07:18 +0100 Subject: [PATCH 06/20] Write example of JWT verification middleware --- nodeJS/authentication/json_web_tokens.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 8886a8e31ba..02662d85600 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -2,7 +2,7 @@ Previously, we learned about using sessions to persist logins and authenticate users. Session data would be stored server-side and the client issued their session's ID via a cookie. When authenticating, the session store would be checked for a matching session. This kind of authentication is "stateful". -An alternative approach to authentication, and one that is common with REST APIs, is to use "stateless" authentication with JSON web tokens (JWTs). The main difference between using stateful and stateless authentication is where the authentication data is stored: server-side or client-side. In this lesson, you will be introduced to stateless authentication using JWTs. +An alternative approach to authentication, and one that is common with REST APIs, is to use "stateless" authentication with JSON web tokens (JWTs). While many of the overarching auth concepts and processes remain the same, the main difference between using stateful and stateless authentication is where the authentication data is stored: server-side or client-side. In this lesson, you will be introduced to stateless authentication using JWTs. ### Lesson overview @@ -36,7 +36,7 @@ This is not all sunshine and roses, however. There are always tradeoffs, especia ### Generating JWTs -Back in the sessions lesson, when a user successfully logged in, their ID was serialised to a session which was saved to the database, and a cookie sent back to the client with the signed session ID. With JWTs, a very similar process occurs, just a JWT is created and sent instead, and nothing gets saved to the database. You can generate JWTs using the [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken) library. For example, in a login route middleware: +Back in the Sessions lesson, when a user successfully logged in, their ID was serialised to a session which was saved to the database, and a cookie sent back to the client with the signed session ID. With JWTs, a very similar process occurs, just a JWT is created and sent instead, and nothing gets saved to the database. You can generate JWTs using the [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken) library. For example, in a login route middleware: ```javascript // importing the jsonwebtoken library somewhere appropriate @@ -64,6 +64,25 @@ Remember that JWTs are sent to and stored on the client. If a malicious party is
+### Verifying JWTs + +So when a user successfully logs in, the server generates and sends a signed JWT in response. What about for incoming requests to routes we want to protect? + +The client must attach the JWT to any such requests, whether that's through `fetch` in a script or when using something like Postman. In our case, we'll do the same as earlier and write to the "Authorization" header using the format `Bearer: `. Just like with the Sessions lesson, any routes we want to protect will need a middleware to authenticate the request first. However, instead of doing session stuff like before, we need to extract the JWT and verify its signature. For example: + +```javascript +// in an authentication middleware +const token = req.headers.authorization?.split(" ")[1]; +try { + req.user = jwt.verify(token, process.env.SECRET); + next(); +} catch (err) { + res.status(401).json("Could not authenticate user"); +} +``` + +Upon successful verification, the payload is returned and can be handled however necessary; in the example above, it gets attached to `req` and the next middleware is called. If the token is not valid, whether that's from it having expired or not valid or even non-existant, an error is thrown which we can catch and unauthorize the request, responding to the client with a 401 since we do not know who they are. + ### Assignment
From 56d1db87d5e27b5dc06a1bcb35d80c6ed84c5e67 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sat, 17 May 2025 23:05:45 +0100 Subject: [PATCH 07/20] Use Express method to get authorization header req.headers is a Node built-in getter as part of the Message interface (which extends Request). Not as intuitive to find docs for since people will likely look at the Request interface first. It'll be easier for learners to find an Express method in Express docs - consistent with other Express methods used in this and other lessons. --- nodeJS/authentication/json_web_tokens.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 02662d85600..5cb38237bb1 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -72,7 +72,7 @@ The client must attach the JWT to any such requests, whether that's through `fet ```javascript // in an authentication middleware -const token = req.headers.authorization?.split(" ")[1]; +const token = req.get("authorization")?.split(" ")[1]; try { req.user = jwt.verify(token, process.env.SECRET); next(); From 51f801074d7d95acfd12d0932136bdf28503db94 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sat, 17 May 2025 23:47:33 +0100 Subject: [PATCH 08/20] Adds brief caveats section More detail left for a future Sessions vs JWTs lesson --- nodeJS/authentication/json_web_tokens.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 5cb38237bb1..a357c22d854 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -83,6 +83,16 @@ try { Upon successful verification, the payload is returned and can be handled however necessary; in the example above, it gets attached to `req` and the next middleware is called. If the token is not valid, whether that's from it having expired or not valid or even non-existant, an error is thrown which we can catch and unauthorize the request, responding to the client with a 401 since we do not know who they are. +### Caveats + +Stateless authentication seems super handy. We can ensure we only verify untampered tokens that were generated by our server (assuming a random unguessable secret) while reducing database calls per request, as the authentication data can be extracted from a verified token itself. Not all is sunshine and rainbows, however. + +How do you invalidate a token? For example, say a user logs out. With stateful authentication, we can destroy the respective session in the database so even if the original session cookie still exists, future requests that use it will fail authentication when no matching session is found. But with stateless authentication, we can only delete the token on the client. What if that token was copied elsewhere? It could still be sent in another request and as far as the server would be concerned, if it's untampered, it's get verified and the request authenticated despite the user having logged out. As long as the token has not expired and the server secret not changed, the token is considered "valid" and is still usable. + +To combat this, shorter token expiry times can be set to reduce how long tokens are vulnerable for, but then this can come at the cost of the user experience if users keep needing to log in when their tokens quickly expire. In reality, there is no easy way to invalidate a token without introducing some kind of stateful mechanism, such as a list of revoked tokens or something like "refresh tokens" (which you may encounter in the wild). One may argue this introduces complexity without the original benefits of stateless authentication. + +We will compare stateful authentication with sessions and stateless with JWTs in more detail in a future lesson, so don't worry about these caveats just yet. As with any authentication system, they are only as good as their implementations and there certainly are sensible implementations of JWT-based authentication. The main thing is that you are exposed to the concept, whatever you end up personally implementing in your own projects. + ### Assignment
From 8d6c3dd780392cb58097048f6b84c80dbbaeba41 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sun, 18 May 2025 00:19:54 +0100 Subject: [PATCH 09/20] Complete lesson assignment/knowledge check sections --- nodeJS/authentication/json_web_tokens.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index a357c22d854..8979a9b5e17 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -24,7 +24,7 @@ JWTs are often not encrypted, only encoded in base64. You can use any JWT decode eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiT2RpbiJ9.FtLFoA9kG8B_gvKz0nEzx4uDYAlsgWhxTGEUfinYcf8 ``` -You don't need to understand the inner workings of JWTs but let's peek at what's going on. Paste that into [jwt.io](https://jwt.io/) and you'll see a payload of `{ "name": "Odin" }` along with an "invalid signature" warning. If you change the secret in the "verify signature" box to `theodinproject` then paste the JWT back in, it'll say "signature verified". If you change any part of the JWT contents, such as the payload or secret, you'll see the JWT value change. In particular, the signature section changes *dramatically*. +You don't need to understand the inner workings of JWTs but let's peek at what's going on. Paste that into [jwt.io](https://jwt.io/) and you'll see a payload of `{ "name": "Odin" }` along with an "invalid signature" warning. If you change the secret in the "verify signature" box to `theodinproject` then paste the JWT back in, it'll say "signature verified". If you change any part of the JWT contents, such as the payload or secret, you'll see the JWT value change. In particular, the signature section changes *dramatically*. This is how a server can verify if it did indeed issue an incoming JWT as well as verify if it had been tampered with, as a different payload would generate a different signature, even with the same secret. Unless you also know the secret, you would not be able to create the correct signature for the changed payload. @@ -54,7 +54,7 @@ if (user?.password === req.body.password) { } ``` -There are many ways JWTs can be sent to and from servers, such as in the response's "Authorization" header via the [Bearer scheme](https://security.stackexchange.com/questions/108662) or via httpOnly cookies. Since we have not yet covered how to handle cross-site cookies, the example above sends the JWT as a bearer token in the response's Authorization header. +There are many ways JWTs can be sent to and from servers, such as in the response's "Authorization" header via the [Bearer scheme](https://security.stackexchange.com/questions/108662) or via httpOnly cookies. Since we have not yet covered how to handle cross-site cookies, the example above sends the JWT as a bearer token in the response's Authorization header.
@@ -97,13 +97,19 @@ We will compare stateful authentication with sessions and stateless with JWTs in
+1. Read through [Postman's article "What is JWT?"](https://blog.postman.com/what-is-jwt/) for a little more on JWTs themselves. +
### Knowledge check The following questions are an opportunity to reflect on key topics in this lesson. If you can't answer a question, click on it to review the material, but keep in mind you are not expected to memorize or master this knowledge. -- [A KNOWLEDGE CHECK QUESTION](A-KNOWLEDGE-CHECK-URL) +- [How does stateless authentication differ from stateful authentication?](#introduction) +- [What is a JSON web token?](#jwts) +- [How does a JWT protect against tampering?](#jwt-signature) +- [What are some ways that JWTs can be sent between client and server?](#sending-jwts) +- [What are some of the pros and cons of stateless authentication when compared to stateful?](#caveats) ### Additional resources From 63f87f0db325174d5fd5c2fd34c9f5456a7dc949 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sun, 18 May 2025 00:23:32 +0100 Subject: [PATCH 10/20] Fix grammar issues --- nodeJS/authentication/json_web_tokens.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 8979a9b5e17..dbb6015f093 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -16,27 +16,27 @@ This section contains a general overview of topics that you will learn in this l ### JWTs -JWTs are tokens that allow us to send information between various clients and servers, or even between servers. Like with session cookies, they are signed which involves hashing the rest of the JWT (including the payload) with a secret known only to the issuing server. +JWTs are tokens that allow us to send information between various clients and servers, or even between servers. Like with session cookies, they are signed, which involves hashing the rest of the JWT (including the payload) with a secret known only to the issuing server. -JWTs are often not encrypted, only encoded in base64. You can use any JWT decoder such as [jwt.io](https://jwt.io/), paste a JWT in and see the contents; the important part is the signature. For example, here is an example JWT: +JWTs are often not encrypted, only encoded in base64. You can use any JWT decoder, such as [jwt.io](https://jwt.io/), paste a JWT in, and see the contents; the important part is the signature. For example, here is an example JWT: ```text eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiT2RpbiJ9.FtLFoA9kG8B_gvKz0nEzx4uDYAlsgWhxTGEUfinYcf8 ``` -You don't need to understand the inner workings of JWTs but let's peek at what's going on. Paste that into [jwt.io](https://jwt.io/) and you'll see a payload of `{ "name": "Odin" }` along with an "invalid signature" warning. If you change the secret in the "verify signature" box to `theodinproject` then paste the JWT back in, it'll say "signature verified". If you change any part of the JWT contents, such as the payload or secret, you'll see the JWT value change. In particular, the signature section changes *dramatically*. +You don't need to understand the inner workings of JWTs, but let's peek at what's going on. Paste that into [jwt.io](https://jwt.io/) and you'll see a payload of `{ "name": "Odin" }` along with an "invalid signature" warning. If you change the secret in the "verify signature" box to `theodinproject` then paste the JWT back in, it'll say "signature verified". If you change any part of the JWT contents, such as the payload or secret, you'll see the JWT value change. In particular, the signature section changes *dramatically*. This is how a server can verify if it did indeed issue an incoming JWT as well as verify if it had been tampered with, as a different payload would generate a different signature, even with the same secret. Unless you also know the secret, you would not be able to create the correct signature for the changed payload. ### Stateless authentication -We can use JWTs to authenticate our APIs in a stateless manner, that is the server side does not need to store any of the authentication data itself. It only needs to store a secret so it can generate tokens signed with that secret and send them to the client. Then for incoming requests, it needs only verify that it made the incoming JWT and it has not been tampered with, and can deserialize the payload if verified. Else, it can unauthorize the request. All this occurs without needing to make a database call to grab the authentication data (like with sessions, a stateful solution). Neat, no? +We can use JWTs to authenticate our APIs in a stateless manner; the server side does not need to store any of the authentication data itself. It only needs to store a secret so it can generate tokens signed with that secret and send them to the client. Then, for incoming requests, it needs only verify that it made the incoming JWT and it has not been tampered with and can deserialize the payload if verified. Else, it can unauthorize the request. All this occurs without needing to make a database call to grab the authentication data (like with sessions, a stateful solution). Neat, no? -This is not all sunshine and roses, however. There are always tradeoffs, especially when security is concerned, and we will discuss these in more detail in a later lesson where we compare stateful authentication with sessions and stateless authentication with JWTs. Nonetheless, you're likely to encounter this sort of authentication at some point out in the wild, so it's good to get some experience with the concept (even if a basic implementation). +This is not all sunshine and roses, however. There are always tradeoffs, especially when security is concerned, and we will discuss these in more detail in a later lesson where we compare stateful authentication with sessions and stateless authentication with JWTs. Nonetheless, you're likely to encounter this sort of authentication at some point out in the wild, so it's good to get some experience with the concept (even if it's a basic implementation). ### Generating JWTs -Back in the Sessions lesson, when a user successfully logged in, their ID was serialised to a session which was saved to the database, and a cookie sent back to the client with the signed session ID. With JWTs, a very similar process occurs, just a JWT is created and sent instead, and nothing gets saved to the database. You can generate JWTs using the [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken) library. For example, in a login route middleware: +Back in the Sessions lesson, when a user successfully logged in, their ID was serialized to a session which was saved to the database, and a cookie sent back to the client with the signed session ID. With JWTs, a very similar process occurs, only a JWT is created and sent instead, and nothing gets saved to the database. You can generate JWTs using the [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken) library. For example, in a login route middleware: ```javascript // importing the jsonwebtoken library somewhere appropriate @@ -68,7 +68,7 @@ Remember that JWTs are sent to and stored on the client. If a malicious party is So when a user successfully logs in, the server generates and sends a signed JWT in response. What about for incoming requests to routes we want to protect? -The client must attach the JWT to any such requests, whether that's through `fetch` in a script or when using something like Postman. In our case, we'll do the same as earlier and write to the "Authorization" header using the format `Bearer: `. Just like with the Sessions lesson, any routes we want to protect will need a middleware to authenticate the request first. However, instead of doing session stuff like before, we need to extract the JWT and verify its signature. For example: +The client must attach the JWT to any such requests, whether that's through `fetch` in a script or when using something like Postman. In our case, we'll do the same as earlier and write to the "Authorization" header using the format `Bearer: `. Just like with the Sessions lesson, any routes we want to protect will need a middleware to authenticate the request first. However, instead of doing session stuff like before, we need to extract the JWT and verify its signature, which can also be done with the `jsonwebtoken` library. For example: ```javascript // in an authentication middleware From 81f045ebee01ce895b694ade17bbabfde12de3ce Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sun, 18 May 2025 00:27:58 +0100 Subject: [PATCH 11/20] Fix typo --- nodeJS/authentication/json_web_tokens.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index dbb6015f093..2c910e3d435 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -81,7 +81,7 @@ try { } ``` -Upon successful verification, the payload is returned and can be handled however necessary; in the example above, it gets attached to `req` and the next middleware is called. If the token is not valid, whether that's from it having expired or not valid or even non-existant, an error is thrown which we can catch and unauthorize the request, responding to the client with a 401 since we do not know who they are. +Upon successful verification, the payload is returned and can be handled however necessary; in the example above, it gets attached to `req` and the next middleware is called. If the token is not valid, whether that's from it having expired or not valid or even non-existent, an error is thrown which we can catch and unauthorize the request, responding to the client with a 401 since we do not know who they are. ### Caveats From c752bad6b60dd6bc86fabeecc8c4c82af353d2a8 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sun, 18 May 2025 03:58:31 +0100 Subject: [PATCH 12/20] Reduce duplicate wording --- nodeJS/authentication/json_web_tokens.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 2c910e3d435..3df66a54a56 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -85,13 +85,13 @@ Upon successful verification, the payload is returned and can be handled however ### Caveats -Stateless authentication seems super handy. We can ensure we only verify untampered tokens that were generated by our server (assuming a random unguessable secret) while reducing database calls per request, as the authentication data can be extracted from a verified token itself. Not all is sunshine and rainbows, however. +Stateless authentication seems super handy. We can ensure we only verify untampered tokens that were generated by our server (assuming a random unguessable secret) while reducing database calls per request, as the authentication data can be extracted from a verified token itself. -How do you invalidate a token? For example, say a user logs out. With stateful authentication, we can destroy the respective session in the database so even if the original session cookie still exists, future requests that use it will fail authentication when no matching session is found. But with stateless authentication, we can only delete the token on the client. What if that token was copied elsewhere? It could still be sent in another request and as far as the server would be concerned, if it's untampered, it's get verified and the request authenticated despite the user having logged out. As long as the token has not expired and the server secret not changed, the token is considered "valid" and is still usable. +But how do you invalidate a token? For example, say a user logs out. With stateful authentication, we can destroy the respective session in the database so even if the original session cookie still exists, future requests that use it will fail authentication when no matching session is found. But with stateless authentication, we can only delete the token on the client! What if that token was copied elsewhere? It could still be sent in another request and as far as the server would be concerned, if it's untampered, it's get verified and the request authenticated despite the user having logged out. As long as the token has not expired and the server secret not changed, the token is considered "valid" and is still usable. -To combat this, shorter token expiry times can be set to reduce how long tokens are vulnerable for, but then this can come at the cost of the user experience if users keep needing to log in when their tokens quickly expire. In reality, there is no easy way to invalidate a token without introducing some kind of stateful mechanism, such as a list of revoked tokens or something like "refresh tokens" (which you may encounter in the wild). One may argue this introduces complexity without the original benefits of stateless authentication. +To combat this, shorter token expiry times can be set to reduce how long tokens are vulnerable for, but then this can come at the cost of the user experience if users keep needing to log in when their tokens quickly expire. In reality, there is no easy way to invalidate a token without introducing some kind of stateful mechanism, such as a list of revoked tokens or something like "refresh tokens" (which you may encounter in the wild). One may argue this hybrid approach introduces complexity without the original benefits of a purely stateless system. -We will compare stateful authentication with sessions and stateless with JWTs in more detail in a future lesson, so don't worry about these caveats just yet. As with any authentication system, they are only as good as their implementations and there certainly are sensible implementations of JWT-based authentication. The main thing is that you are exposed to the concept, whatever you end up personally implementing in your own projects. +We will compare stateful authentication with sessions and stateless with JWTs in more detail in a future lesson, so don't worry about the details of these caveats just yet. As with any authentication system, they are only as good as their implementations, and there certainly are sensible implementations of JWT-based authentication. The main thing is that you get exposure to the concept, whatever you end up personally implementing in your own projects. ### Assignment From 6f7f8fa7614b3177e45fb70ea8ff80ddbca7db01 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sun, 18 May 2025 14:13:30 +0100 Subject: [PATCH 13/20] Streamline wording flow Co-authored-by: Kevin Browne --- nodeJS/authentication/json_web_tokens.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 3df66a54a56..816e9f21312 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -18,19 +18,19 @@ This section contains a general overview of topics that you will learn in this l JWTs are tokens that allow us to send information between various clients and servers, or even between servers. Like with session cookies, they are signed, which involves hashing the rest of the JWT (including the payload) with a secret known only to the issuing server. -JWTs are often not encrypted, only encoded in base64. You can use any JWT decoder, such as [jwt.io](https://jwt.io/), paste a JWT in, and see the contents; the important part is the signature. For example, here is an example JWT: +JWTs are often not encrypted, only encoded in base64. You can use any JWT decoder, such as [jwt.io](https://jwt.io/), paste a JWT in, and see the contents; the important part is the signature. For example, here is a sample JWT: ```text eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiT2RpbiJ9.FtLFoA9kG8B_gvKz0nEzx4uDYAlsgWhxTGEUfinYcf8 ``` -You don't need to understand the inner workings of JWTs, but let's peek at what's going on. Paste that into [jwt.io](https://jwt.io/) and you'll see a payload of `{ "name": "Odin" }` along with an "invalid signature" warning. If you change the secret in the "verify signature" box to `theodinproject` then paste the JWT back in, it'll say "signature verified". If you change any part of the JWT contents, such as the payload or secret, you'll see the JWT value change. In particular, the signature section changes *dramatically*. +You don't need to understand the inner workings of JWTs, but let's peek at what's going on. Paste that into [jwt.io](https://jwt.io/) and you'll see a payload of `{ "name": "Odin" }` along with an "invalid signature" warning. Change the secret in the "verify signature" box to `theodinproject` then paste the JWT back in. You'll see it says "signature verified". Now try changing any part of the JWT contents, such as the payload or secret, and you'll see the JWT value change. In particular, the signature section changes *dramatically*. This is how a server can verify if it did indeed issue an incoming JWT as well as verify if it had been tampered with, as a different payload would generate a different signature, even with the same secret. Unless you also know the secret, you would not be able to create the correct signature for the changed payload. ### Stateless authentication -We can use JWTs to authenticate our APIs in a stateless manner; the server side does not need to store any of the authentication data itself. It only needs to store a secret so it can generate tokens signed with that secret and send them to the client. Then, for incoming requests, it needs only verify that it made the incoming JWT and it has not been tampered with and can deserialize the payload if verified. Else, it can unauthorize the request. All this occurs without needing to make a database call to grab the authentication data (like with sessions, a stateful solution). Neat, no? +We can use JWTs to authenticate our APIs in a stateless manner; the server does not need to store any of the authentication data itself. It only needs to store a secret so it can generate tokens signed with that secret and send them to the client. Then, for incoming requests, it needs only verify that it made the incoming JWT and it has not been tampered with and can deserialize the payload if verified. Else, it can unauthorize the request. All this occurs without needing to make a database call to grab the authentication data (unlike sessions, a stateful solution). Neat, no? This is not all sunshine and roses, however. There are always tradeoffs, especially when security is concerned, and we will discuss these in more detail in a later lesson where we compare stateful authentication with sessions and stateless authentication with JWTs. Nonetheless, you're likely to encounter this sort of authentication at some point out in the wild, so it's good to get some experience with the concept (even if it's a basic implementation). @@ -68,7 +68,7 @@ Remember that JWTs are sent to and stored on the client. If a malicious party is So when a user successfully logs in, the server generates and sends a signed JWT in response. What about for incoming requests to routes we want to protect? -The client must attach the JWT to any such requests, whether that's through `fetch` in a script or when using something like Postman. In our case, we'll do the same as earlier and write to the "Authorization" header using the format `Bearer: `. Just like with the Sessions lesson, any routes we want to protect will need a middleware to authenticate the request first. However, instead of doing session stuff like before, we need to extract the JWT and verify its signature, which can also be done with the `jsonwebtoken` library. For example: +The client must attach the JWT to any such requests, whether that's through `fetch` in a script or when using something like Postman. In our case, we'll do the same as earlier and write to the "Authorization" header using the format `Bearer: `. Just like with the Sessions lesson, any routes we want to protect will need a middleware to authenticate the request first. However, instead of saving a session server-side, we only need to extract the JWT and verify its signature, which can also be done with the `jsonwebtoken` library. For example: ```javascript // in an authentication middleware @@ -87,7 +87,7 @@ Upon successful verification, the payload is returned and can be handled however Stateless authentication seems super handy. We can ensure we only verify untampered tokens that were generated by our server (assuming a random unguessable secret) while reducing database calls per request, as the authentication data can be extracted from a verified token itself. -But how do you invalidate a token? For example, say a user logs out. With stateful authentication, we can destroy the respective session in the database so even if the original session cookie still exists, future requests that use it will fail authentication when no matching session is found. But with stateless authentication, we can only delete the token on the client! What if that token was copied elsewhere? It could still be sent in another request and as far as the server would be concerned, if it's untampered, it's get verified and the request authenticated despite the user having logged out. As long as the token has not expired and the server secret not changed, the token is considered "valid" and is still usable. +But how do you invalidate a token? For example, say a user logs out. With stateful authentication, we can destroy the respective session in the database so even if the original session cookie still exists, future requests that use it will fail authentication when no matching session is found. But with stateless authentication, we can only delete the token on the client! What if that token was copied elsewhere? It could still be sent in another request and as far as the server would be concerned, if it's untampered with, it'll get verified and the request will be authenticated despite the user having logged out. As long as the token has not expired and the server secret not changed, the token is considered "valid" and is still usable. To combat this, shorter token expiry times can be set to reduce how long tokens are vulnerable for, but then this can come at the cost of the user experience if users keep needing to log in when their tokens quickly expire. In reality, there is no easy way to invalidate a token without introducing some kind of stateful mechanism, such as a list of revoked tokens or something like "refresh tokens" (which you may encounter in the wild). One may argue this hybrid approach introduces complexity without the original benefits of a purely stateless system. From 8f1d4d7423b1e8d10f788e0931776b4eaa0001b4 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sat, 31 May 2025 01:13:41 +0100 Subject: [PATCH 14/20] Remove "Caveats" section Content to be covered in a later planned lesson. --- nodeJS/authentication/json_web_tokens.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 816e9f21312..12206b11dc2 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -12,7 +12,6 @@ This section contains a general overview of topics that you will learn in this l - Describe what stateless authentication is. - Explain some of the differences between authentication with sessions and JWTs. - Implement basic stateless authentication with JWTs. -- Describe potential security issues with stateless authentication. ### JWTs @@ -83,16 +82,6 @@ try { Upon successful verification, the payload is returned and can be handled however necessary; in the example above, it gets attached to `req` and the next middleware is called. If the token is not valid, whether that's from it having expired or not valid or even non-existent, an error is thrown which we can catch and unauthorize the request, responding to the client with a 401 since we do not know who they are. -### Caveats - -Stateless authentication seems super handy. We can ensure we only verify untampered tokens that were generated by our server (assuming a random unguessable secret) while reducing database calls per request, as the authentication data can be extracted from a verified token itself. - -But how do you invalidate a token? For example, say a user logs out. With stateful authentication, we can destroy the respective session in the database so even if the original session cookie still exists, future requests that use it will fail authentication when no matching session is found. But with stateless authentication, we can only delete the token on the client! What if that token was copied elsewhere? It could still be sent in another request and as far as the server would be concerned, if it's untampered with, it'll get verified and the request will be authenticated despite the user having logged out. As long as the token has not expired and the server secret not changed, the token is considered "valid" and is still usable. - -To combat this, shorter token expiry times can be set to reduce how long tokens are vulnerable for, but then this can come at the cost of the user experience if users keep needing to log in when their tokens quickly expire. In reality, there is no easy way to invalidate a token without introducing some kind of stateful mechanism, such as a list of revoked tokens or something like "refresh tokens" (which you may encounter in the wild). One may argue this hybrid approach introduces complexity without the original benefits of a purely stateless system. - -We will compare stateful authentication with sessions and stateless with JWTs in more detail in a future lesson, so don't worry about the details of these caveats just yet. As with any authentication system, they are only as good as their implementations, and there certainly are sensible implementations of JWT-based authentication. The main thing is that you get exposure to the concept, whatever you end up personally implementing in your own projects. - ### Assignment
@@ -109,7 +98,6 @@ The following questions are an opportunity to reflect on key topics in this less - [What is a JSON web token?](#jwts) - [How does a JWT protect against tampering?](#jwt-signature) - [What are some ways that JWTs can be sent between client and server?](#sending-jwts) -- [What are some of the pros and cons of stateless authentication when compared to stateful?](#caveats) ### Additional resources From 10a0cd01a07871801e1f085b4275fbe90bc3a2dc Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sat, 31 May 2025 01:31:05 +0100 Subject: [PATCH 15/20] Query DB for user details Closer parallel with sessions example, only omitting DB query for session --- nodeJS/authentication/json_web_tokens.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 12206b11dc2..18e2df3f092 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -73,14 +73,24 @@ The client must attach the JWT to any such requests, whether that's through `fet // in an authentication middleware const token = req.get("authorization")?.split(" ")[1]; try { - req.user = jwt.verify(token, process.env.SECRET); + const { id } = jwt.verify(token, process.env.SECRET); + const { rows } = await pool.query( + "SELECT * FROM users WHERE id = $1", + [id], + ); + const user = rows[0]; + req.user = { + // whatever user details may be needed for any requests + } next(); } catch (err) { res.status(401).json("Could not authenticate user"); } ``` -Upon successful verification, the payload is returned and can be handled however necessary; in the example above, it gets attached to `req` and the next middleware is called. If the token is not valid, whether that's from it having expired or not valid or even non-existent, an error is thrown which we can catch and unauthorize the request, responding to the client with a 401 since we do not know who they are. +Upon successful verification, the payload is returned and can be handled however necessary; in the example above, we query our database for the right user details, assign what we need to `req.user`, then the next middleware is called. If the token is not valid, whether that's from it having expired or not valid or even non-existent, or if the user no longer exists, an error is thrown which we can then catch and unauthorize the request, responding to the client with a 401 since we do not know who they are. The authentication and database query can also be handled in separate middleware functions if you wish. + +Essentially, this is a similar process to our previous session-based authentication system only since the authentication data came with the JWT payload, we did not need to make an additional database call to grab that data from a session. ### Assignment From b546d3708007e5d54aa6e9774af3cb14b6e9fa86 Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sat, 31 May 2025 01:31:24 +0100 Subject: [PATCH 16/20] Add brief mention of browser storage options for JWTs --- nodeJS/authentication/json_web_tokens.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 18e2df3f092..fedd88c5fca 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -53,7 +53,7 @@ if (user?.password === req.body.password) { } ``` -There are many ways JWTs can be sent to and from servers, such as in the response's "Authorization" header via the [Bearer scheme](https://security.stackexchange.com/questions/108662) or via httpOnly cookies. Since we have not yet covered how to handle cross-site cookies, the example above sends the JWT as a bearer token in the response's Authorization header. +There are many ways JWTs can be sent to and from servers, such as in the response's "Authorization" header via the [Bearer scheme](https://security.stackexchange.com/questions/108662) or via httpOnly cookies. Since we have not yet covered how to handle cookies when the client and server are deployed on different domains, the example above sends the JWT as a bearer token in the response's Authorization header. When received, the client can store the JWT in a number of ways, such as in the same httpOnly cookie it came in, as well as extracting the token from the Authorization header then storing it in localStorage.
@@ -107,7 +107,7 @@ The following questions are an opportunity to reflect on key topics in this less - [How does stateless authentication differ from stateful authentication?](#introduction) - [What is a JSON web token?](#jwts) - [How does a JWT protect against tampering?](#jwt-signature) -- [What are some ways that JWTs can be sent between client and server?](#sending-jwts) +- [What are some ways that JWTs can be sent and stored between client and server?](#sending-jwts) ### Additional resources From e4336426817ccb13e40b2417a9acda4863c6751e Mon Sep 17 00:00:00 2001 From: MaoShizhong <122839503+MaoShizhong@users.noreply.github.com> Date: Sat, 28 Jun 2025 23:22:56 +0100 Subject: [PATCH 17/20] Add info about logging out --- nodeJS/authentication/json_web_tokens.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index fedd88c5fca..7f7d067b968 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -92,6 +92,12 @@ Upon successful verification, the payload is returned and can be handled however Essentially, this is a similar process to our previous session-based authentication system only since the authentication data came with the JWT payload, we did not need to make an additional database call to grab that data from a session. +### Logging out with JWTs + +When we were using sessions, we logged users out by destroying the session itself. You could delete the session cookie at the same time but the main thing is that the session no longer exists. But since we're using JWTs with stateless authentication, the server doesn't store any of this authentication data. Therefore, the only way we can "log out" a user would be to get rid of the JWT on the client, whether that's having the client delete the JWT from local storage or unsetting a cookie if cookies are used etc. + +This change of mechanism does come with some caveats but they will be discussed in more detail in a later lesson. For now, it's more important to get an idea for how stateless authentication and JWTs work as a whole. + ### Assignment
From d6a19c8e5e756494f374b67d8d956ab012d265cb Mon Sep 17 00:00:00 2001 From: mao-sz <122839503+mao-sz@users.noreply.github.com> Date: Sat, 13 Sep 2025 14:56:27 +0100 Subject: [PATCH 18/20] Rephrase JWT encoder/decoder instructions jwt.io now has a slightly different decoder/encoder interface. --- nodeJS/authentication/json_web_tokens.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 7f7d067b968..a91a7be249a 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -23,7 +23,7 @@ JWTs are often not encrypted, only encoded in base64. You can use any JWT decode eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiT2RpbiJ9.FtLFoA9kG8B_gvKz0nEzx4uDYAlsgWhxTGEUfinYcf8 ``` -You don't need to understand the inner workings of JWTs, but let's peek at what's going on. Paste that into [jwt.io](https://jwt.io/) and you'll see a payload of `{ "name": "Odin" }` along with an "invalid signature" warning. Change the secret in the "verify signature" box to `theodinproject` then paste the JWT back in. You'll see it says "signature verified". Now try changing any part of the JWT contents, such as the payload or secret, and you'll see the JWT value change. In particular, the signature section changes *dramatically*. +You don't need to understand the inner workings of JWTs, but let's peek at what's going on. Paste that into [jwt.io](https://jwt.io/) and you'll see a payload of `{ "name": "Odin" }` along with an "invalid signature" warning. Change the secret in the "verify signature" box to `theodinproject` and you'll see it now says "signature verified". Now switch to the JWT Encoder tab, then try changing any part of the JWT contents, such as the payload or secret. You'll see the JWT value change. In particular, the signature section changes *dramatically*. This is how a server can verify if it did indeed issue an incoming JWT as well as verify if it had been tampered with, as a different payload would generate a different signature, even with the same secret. Unless you also know the secret, you would not be able to create the correct signature for the changed payload. From 4735c7df5c943d5fa98c88ea3e4e97fa45812355 Mon Sep 17 00:00:00 2001 From: mao-sz <122839503+mao-sz@users.noreply.github.com> Date: Sat, 13 Sep 2025 15:00:32 +0100 Subject: [PATCH 19/20] Fix code example formatting --- nodeJS/authentication/json_web_tokens.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index a91a7be249a..23beead8cb2 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -43,9 +43,11 @@ const jwt = require("jsonwebtoken"); // somewhere in a login route middleware if (user?.password === req.body.password) { - const token = jwt.sign({ - id: user.id - }, process.env.SECRET, { expiresIn: "1d" }); + const token = jwt.sign( + { id: user.id }, + process.env.SECRET, + { expiresIn: "1d" }, + ); res.set({Authorization: `Bearer ${token}`}).json("Login successful"); } else { From 8054786af4a67e199f4a307aa8f76bfcd8d75a5f Mon Sep 17 00:00:00 2001 From: mao-sz <122839503+mao-sz@users.noreply.github.com> Date: Mon, 17 Nov 2025 23:29:18 +0000 Subject: [PATCH 20/20] Rephrase LO items to be topic overviews than learning objectives Better matches style guide's intent for the section: https://github.com/TheOdinProject/curriculum/blob/main/LAYOUT_STYLE_GUIDE.md#lesson-layout --- nodeJS/authentication/json_web_tokens.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nodeJS/authentication/json_web_tokens.md b/nodeJS/authentication/json_web_tokens.md index 23beead8cb2..5d3d4492ce8 100644 --- a/nodeJS/authentication/json_web_tokens.md +++ b/nodeJS/authentication/json_web_tokens.md @@ -8,10 +8,10 @@ An alternative approach to authentication, and one that is common with REST APIs This section contains a general overview of topics that you will learn in this lesson. -- Describe what JSON web tokens (JWTs) are. -- Describe what stateless authentication is. -- Explain some of the differences between authentication with sessions and JWTs. -- Implement basic stateless authentication with JWTs. +- JSON web tokens (JWTs). +- Stateless authentication. +- Differences between authentication with sessions and JWTs. +- Implementing basic stateless authentication with JWTs. ### JWTs