Transcript
OAuth 2.0 Course for Beginners
From freeCodeCamp.org · Episode page · Watch on YouTube
· 1 h 30 min · 10,472 words · 83 timestamped sections
This is the full, timestamped transcript of "OAuth 2.0 Course for Beginners", an episode of freeCodeCamp.org published January 5, 2026 (1 h 30 min, about 10,472 words). It mentions 10 people, works and ideas, including OAuth, GitHub, PKCE, OOTH 2.0, JWT.
What is discussed, and when
Notable quotes from "OAuth 2.0 Course for Beginners"
Welcome to this deep dive into OOTH 2.0, the industry standard framework for delegated authorization. In this course, you will learn to build a fully functional OOTH ecosystem from the.
about OOTH 2.0 · atThird-party applications limited access to their data like photos or contacts on another service like Google or Facebook and it doesn't allow them to actually share their…
about Facebook · atOkay. So, that's basically it. we'll just create a repo of this on GitHub so you can get an access to it. So, I'm going to go here, sign in, and going to go here and we're going to.
about GitHub · at
Who and what is mentioned in this episode?
Companies & orgs
- GitHub — Company · 6 mentions · first at
- Facebook — Company · 1 mention · first at
- Google LLC — Company · 1 mention · first at
Third-party applications limited access to their data like photos or contacts on another service like Google or Facebook and it doesn't allow them to actually share their…
Products & tech
- Axios — Product · 2 mentions
Concepts & topics
- OAuth — Concept · 8 mentions
- PKCE — Concept · 5 mentions
- OOTH 2.0 — Concept · 4 mentions · first at
- JWT — Concept · 3 mentions
- JSON — Concept · 2 mentions
- Node.js — Concept · 1 mention · first at
This tutorial is going to be covered in Node.js and I will cover the following topics which will be creating an authorization server which is allowing us to issue tokens. we're going to create a resource server which.
Full transcript with timestamps
Welcome to this deep dive into OOTH 2.0, the industry standard framework for delegated authorization. In this course, you will learn to build a fully functional OOTH ecosystem from the ground up. Harris begins by breaking down the four essential roles: resource owner, client, authorization server, and resource server. Before going into modern security musthaves like PKCE, you'll learn to code your own authorization and resource servers, implement a client app, and even troubleshoot realorld errors like JWKS and Axios 400 issues. By the end, you'll have the practical expertise to implement secure professional-grade authorization in any modern web application. Today, we're going to be doing OOTH 2.0 O and how this is an industry standard authorization framework that lets users grant third-party applications limited access to their data like photos or contacts on another service like Google or Facebook
and it doesn't allow them to actually share their actual passwords but we use temporary access tokens for secure delegated access across web mobile and desktop apps has been very popular method and people use it widely for their apps and like sign in with Google or Facebook buttons connecting apps like a fitness tracker to your health data web and mobile apps accessing cloud storage or social media data seamlessly. Um this tutorial is going to be covered in Node.js and I will cover uh the following topics which will be creating an authorization server which is allowing us to issue tokens. Uh we're going to create a resource server which protects an API and validates the tokens. And we're also going to be creating the client app that does authorization code plus PKCE then calls a protected API. So this is one of going to be the cleanest way to explain OOTH 2.0 because viewers can kind of see the
full picture and I'll keep concepts simple and tie them directly to the code. So before even diving in we'll explain what OATH 2.0 know in simple terms and like I mentioned before it's kind of like a secure [snorts] uh valet key system. So you as the user own the car your data right an app wants to drive your car for a specific purpose like read your profile post a message so oath is the process where you approve that what the app can do and you know you limit scopes or permissions the app gets a short-lived key which is the access token that lets it call APIs and the app does not learn your password.
Important to note is OOTH is mainly for authorization what an app can do and authentication like who are you is usually handled via open id connect on top of oath. So those are very important things to understand and um let's go ahead and just open up our directory here in Visual Studio Code. So I'm going to go here and I'm going to open up with code. So here you have an empty empty state um and we just have a folder created as OOTH 2.0. Now the four OOTH rules um you'll reference [snorts] are the resource owner which is the user. Okay that's you the client the app that trying to access the data. our client app that we could could create. The authorization server is the server that logs the user in and issues tokens off server and then we have the resource server the API holding protected data resource server. Um now tokens in simple terms are like authorization code. They a temporary kind of like receipt that's
created after login and exchanged for tokens. [snorts] we access uh token is kind of like a short-lived token used to call APIs what we'll use and the last thing what we're talking about is a refresh token and it's a longer live token used to get act new access tokens will uh will include it so why pkce matters so let me just go ahead and open up a um text file or I'll call it node txt okay so one thing I want uh actually bring up is what PKC is, right? So if I go here and just open this up and I say okay PKCE, right?
What is this? So it protects the authorization code flow for public clients like the app that can't keep secrets safely like spas and mobile apps. So what PKCE is is like if a client creates a secret random string like suppose the secret random string is called code verifier. Okay. Client hashes it to create code challenge. Okay. Client sends code challenge. Okay. inauthorize. So it would pass it to authorize when exchanging the code in token. Okay, client sends code verifier. Okay, it would send this one. Now what happens is that author server hashes the verifier and checks it matches the challenge which is basically it's hashed and when you bring it back you know you would it would turn into code challenge. So so even if someone steals the authorization code they can't ch exchange it without the verifier.
Okay. So let's have a project set up. This will make more and more more sense as we go uh throughout this project. So I'm just going to delete this and I'm going to start with clean folder which is ooth node and 2.0. So after that we'll just create a folder in here. So I'm just going to go here and I'm going to say new folder and I'm going to call this one as o node demo. Now we could like cd in here. So if I go here cd o ooth no demo and then once we're over here we don't need this one but we can just open um so we can open this up in code.
[snorts] So now that we are over here, we can go ahead and um we have to create three different folders. So this is a new folder button. One is the O server like I was mentioning in the beginning. We will create that. Then the other folder is the resource server. And the last one that we'll create is the client app. Okay. So we have to make sure they're all independent folders. So client app. Okay. So now these three folders are created. We will run O server on uh localhost port 3000, the client app on localhost port 4,000 and the resource server on localhost port 5,000. Okay. 3,000, 4,000, and 5,000.
Uh we're going to build the authorization server which is the issu which issues the tokens. Okay. This is the this is the server that you know logs you in will simulate login for demo. It'll create authorization codes and exchanges codes for tokens and then publishes the public keys via G JWKS so APIs can verify tokens. Now we're going to have to go into this O server. So again we can go here O server and then open code. So we have a new one here. So within O server what we can do is we can open up the terminal here control and back tick and right here what we can do is we can say npm or init y.
So you can see over here it created this JSON and it has the author version description main which is index.js scripts keywords author and license is C. Now if we do npm i express cookie parser body parser and Jose so we are going to use these particular libraries and you're going to see them being populated um within here. So in the dependencies you can see over here. Okay. And you can see also that we are node modules has been created and the package hyphen lock uh JSON is also there. Okay. So far so good. We can use Jose because it's modern and handles JWT or JWKS correctly. Um and if you want to learn more about JWT, I have a video separate on my YouTube channel Coding Cleverly.
So you can go and check it out. Now we're going to set ESM mode um which is the in the O server. So we're going to in the package JSON file and we're going to add a type here. So you can see over here [snorts] um there's there's these things but right after the dependencies. Okay. So you can add a comma and then you can add type and you can just say module. Okay. Now we also have um we have to create an index.js file. So we'll create a file here and this one called index.js. For index.js JS we kind of need to add a couple of things so that it can start running. Importantly, uh we'll have to generate a real private key and paste it into the private key PEM um environment variable. And then we also have viewers that don't need to understand key formats deeply, but they uh should run a command and we're going to have to use open SSL inside the all server to generate um those private PEMS. then copy the contents of private
PKCS8 PM into the private key. Um, so what I'm going to do is first of all I'm going to make sure we have the following uh imports in the index.js file. So we're going to have import express from express. We have import body parser from body parser. Okay. Okay. And then we have import cookie parser from cookie parser. We have import random bytes. [snorts] And then we also have create hash from crypto. And uh we also need to have um import sign JWT and we have export JWK and we have import PKC S8 uh from Jose. So let's create the application which is const application express.
We also have the app dot use. We're going to use a body parser dot URL encoded and then extended is not true but false. And then we also have app dot use um body parser.json and we also have app dot use cookie parser. Okay. So now that we have done most of this section uh you got to understand ooth server basics which uh the authorize folder okay creates an authorization code and then it redirects back to client and then the slash token folder exchanges code for the access token and then we have the slashwell and inside it would be a jwks JSON file and that kind of publishes public keys so resource servers can verify the JWTs.
Okay, JSON web tokens which are JWTs. Now um a demo data store in production use DB or Reddus. So we can have a const uh clients [snorts] let's see spelling c l i e n ts is equal to new map. Okay. Then we have const authorization. Authorization codes is equal to new map. And we have const uh refresh tokens is equal to new map. And then we have a register registering a demo client a public client. So we have clients set and then we have demo client and then in here we have the client ID which is the uh demo client and we have the redirect URIs. is and th that is basically um http colon uh two for slashes localhost and port we yeah have 4,000 and uh slash call back. Okay.
So, we don't need a comma after that or we'll just keep it. But we need to make sure we close that with a semicolon here. Now, over here we have uh the next section is the JWT signing key. So, in production, rotate keys and store that safely. Uh a real PKCS8 private key string should be used. Uh for the tutorial we kind of would create one quickly. Uh for a clean tutorial repo you can generate uh this key once and keep it in your environment variables. So let's just have a random private key PM. Okay. And then we have begin private key. And then we have a random private key.
And then uh we have an end private key. It's kind of like this how it's created. Okay. So we'll just have something randomly generated just like this. Um so after that okay I think I mentioned one thing here is we don't need that. So we don't need uh multiple but this is the back tick and it's kind of done. So that's there. And one important thing is to make sure the uh we replace the above with a real private key. This is just a dummy one that we created. And a quick way to generate for your repo is using open SSL uh gen key and hyphen algorithm RSA. And and we just kind of use the OpenSSL commands with PKCS8 to generate those private keys for us.
[snorts] And over after that you also need to specify your issuer. Okay. [snorts] And you can have the uh localhost 3000 set up here. We can also have the con uh key ID and that should be set to demo key demo key. And then we have one. Okay. So I'm going to make sure that we are using uh a particular quote. I would recommend using double quotes throughout the entire app. So stick with one quotations. And over here what we're going to do is we're going to have demo key one. Okay. After that we have a function and we have base 64 um URL and inside here we'll have the input and it would return the input dot to string [snorts] and we'll have the base 64 and we'll kind of replace that replace with the slash and then we have backslash plus slashg and then we'll have dash.replace replace.
Then we also have the slash then backslash and then we have slashg, um, and then we have the dash here. Okay. R dotreplace and thinking this is how it's supposed to be done. So we have slash equal plus dollar slash and then you have a g over here and then we have comma and then we have that. So this is basically our b 64 URL function. Um, we also need a function for SHA 256 base URL and that should be something like this. So this is going to be SHA 256 base 64 base 64 URL pass in a string as the input and it creates the hash with SHA 256 input parameter parameter and then it has the update string.igest digest and it returns a B 64 URL hash.
We also need to have the generate code function. It would kind of have a random bytes of 32. So it would look something similar to this. And we also have a demo login. So we have to make sure we kind of simulate that return Alice which is sub name is Alice example email is aliceample.com. Now after that we have um different um authorization. So we have to make sure our app.get has a proper query parameter validation script. Um otherwise it won't match properly. So I would add some of these sections and I will explain line by line what they all mean.
So here we have um the authorized [snorts] um wreck request and response and we have these uh response type client ID uh redirect URI scope state and code challenge which also has the code challenge method and the request query. We also have basic validation that we've already been doing um and making sure that unknown client ids and all only those are required so that it doesn't mess up. We have a normal show login plus consent UI for tutorial simplicity auto login and auto consent. So this will just give you some of that um demo get demo user and all that. Then we have a redirect new URI URL redirect and we also have the params to search with code set and we have the state if redirect.archps set and current state and we also have the redirect uh to string. We also have this post token async function which has the grant type and we also have a grant type which is authorization code along with the
authorization statuses. If it's 400 we have an invalid grant. But when it's uh something like this one which is less than the date expires at which is less than the date. Now which is an invalid grant. We also have if it's not equal to the client ID. The client I record client ID it does not equal to the client ID or the record redirect URI is not equal to redirectory URI. We also have an invalid grant 400 which is a client mismatch. We have the PKCE validation which is uh computed challenge um SHA 256 base 64 URL uh code verifier and if that computer challenge is not equal to the record code challenge we kind of throw out a invalid grand which is a PKC validation failed.
Um we also have one time use code which is authorization codes.delete code. We also have a create jw access token and we're using the private key variable as the await import pkcs8 private key p that dummy that we had and we also pass in the 256 uh rs 256 here as the second input argument and we also have the access token which has the new sign JWT as a scope name and the email and we're coming through those records and we're going to make sure that they have all these parameters set as the RS 256 and the key ID also allows the issuer client ID records user.sub and 15 minutes for expiration time and refresh token set for generate code and making [snorts] sure that all of these parameters are set. We have the return response.json.
We have the access token. We have the bearer. We have the expires and 900 refresh token and scope record.scope. We have grant type which is the refresh token and it has make sure that all of these are specified and if the record doesn't match we throw out again invalid grant. Um and if they don't match again another invalid grant so we have an in private key and we have the import and we have the returning response of the bas uh the JSON we also have the response status which is 400 and unsupported grant type making sure that we have the app.get which is well known j uh jwks.json JSON have the import of this RS 256 and we're making sure we're using these uh key simplicity. This is Jose exports J uh JWK for a key object but we need like a public JWK for tutorial simplicity like Jose can export from private key to it includes like public uh parts. So that's why we have that and we have response.json which has key set to JWK.
Now this is the code which has allowing us to run it on port 3000 which is app.n 3000 and it has an arrow function set to console.log all server running on local host port 3000. Now since all of this is there um you can see that important notes here is that one is that you must like generate a real private key and paste into the private key p. So if you go here private key if I search here this one viewers don't need to understand key formats deeply but they should run a command uh use this uh one which you kind of want to understand which is inside o server. So if I go here so I'm in o server um and uh I'll show you you know what command what command has to be run. So it's this one and uh it's this one.
So we have these two commands that need to be run open SSL. Um and if we run these it'll create us those contents of you know PM into uh private PKCS8 into the private key and then after that we have a buildup resource server. So, you know, you could run these unit using PowerShell. I experienced it. Maybe it worked. Let's see. So, go ahead and we run this. So, if I go here CLS. Okay. Okay. So, it doesn't run because it's in I think PowerShell, but it's going to run in Linux environment. So, if you have um git bash, I think I don't have it. Let's see. Git bash. I do have it. So, we kind of run and make sure we're in the same directory.
Okay. Okay. So, it doesn't even know which directory they are in. LS ls. Okay, one second. LS and we have all these folders, but we need to make sure we're in the documents folder, which is this one. So, we can just say cd documents. Okay. CD docq. Okay. And ls. Now you have a bunch of other folders. But the thing is that what we want is we want this one. And it's not going to work until um you have the path uh you know as forward slashes. So yeah. I mean it's um you know you're going to have to go through that. One way what we can do is if you want to just see the running of those commands like I could show you those. So if go here first command is this one and I'll just paste it here. Open SSL gen play. See what this is what happened in the documents folder. So you can go here copy address you can just paste it here.
Okay. So just go to the documents folder here. I assume we're in the uh documents folder of G&G. Okay. So here let's say item modified. So type. Okay. So over here we have Okay. Um yeah there's a private key. See that? [snorts] Now we have that we can also run the other command that was given to us and that was this one. Okay. Okay. It's this one. So we can run this second one. Open SSL. Copy all that. And then we go back to this one which is over here and run that command right here. So you can see over here this was the second command and the date for the modification was updated.
And uh if you go back that's the output of it [snorts] and go and open this up in notepad. You can see this is kind of the private key. So you can just copy from the beginning to the end. Okay. And just copy that. Go here into the section here. Just replace that one. This one. Okay. And we're all good. [sighs] So yeah, I mean this was basically the section that I wanted to discuss. go back here and this is the O server. We need to build a resource server, the API that validates the tokens now. So if we go to the project again, which is over here and uh you'll see then the resource server is up here.
We can open this one up uh in another instance of Visual Studio Code. [snorts] And what we can do with this one is that and now we'll have to have this server is the protected API. It will like kind of read authorization bearer token. It will fetch the JWKS from author server. It will also verify token signature plus issuer plus audience plus expiry all of that kind of good stuff. Also allow request only if token is valid. So we'll have to install the dependencies. Um, so making sure we're open opened up the terminal here, we can go and just make it a little bigger. And let's say we have the npm in it y, which is going to generate the package.json file. After that, we have the npmi express and we also have Jose as well. [snorts] So this is going to install those for us. We have to make sure we go to the package.json JSON just like how we did the other one and go here at the end and right after that we'll just add and type
module. Okay. After that we have is uh the resource server inside here we'll create an index.js file. Okay. Okay. I created a folder. Let me delete that. And we need an index.js file. Not in the node modules but just outside of it. We'll just create one thing called index.js. Okay. Now in the index.js file, we'll have some imports. import have express uh from express and then we just keep going in and we'll say import uh JWT verify from Jose. Okay, we also need one more thing which is um the create remote JWK set. Uh and that is all coming from Jose. So we're good with that. You can see it's a little dimmed out because it's not active. We're not using them. But once we use them within our code, they're going to start lighting up. So we have um app is equal to express and apps using the express.json. Very classical. Uh we have a const issuer.
So the const issuer is equal to the um local host but we're not running on port 3 9,000 but we're running on port 3000. We have a con um audience and that's the demo client. So let me go here and that is demo client that we're going to have the audience as. We have a con uh jwks URL new URL and uh we're going to use just double quotes here. going to say http colon two forward slashes localhost um localhost port 3000 and well known jwks.json perfect. After that we have the
[snorts] uh Jose helper that fetches and caches the JWKS automatically. So we have const JWKS is equal to create remote JWK set and we have JWKS as the URL. We have the async function. Um and then we could say that as the require off. We have the request response and next inside here we have uh const author authorization is set to request.headers headers authorization and if we have the authentication as authorize O we have not O and then we have starts with and then we have bearer and then we have return of resource response status 401 And then we have JSON and then we have error we have missing missing underscore token.
Now, after all of that, okay, we're going to have there's one problem here is we don't have a closing brack for that. So, that should be set for this one. So, there we go. And now we can also apply that semicolon here. Okay. And uh since there was a a closing bracket we didn't add right now we'll just keep it because within it's in the the require authorization function. So we just keep going down. You can also just keep it like that. So I mean so it's not going to complain. Um and then over here we're going to add in a const token is equal to authorization that slice and we have the bearer and the bearer has the length and we also have a try catch block.
So in the try we have a const payload. [snorts] Okay. And the payload is set to await. Okay. Let me just fix that up. The formatting here. Okay. Payload is set to await JWT verify which has the token in JWKS. Issuer is the issuer. Audience is the audience. And then it closes like this. Okay. Okay. Um. Okay. There you go. Now we also have um one other thing here which is the request user.payload. And then we have the next function. We also have the catch block. And in the catch block we have return response status 401. And then we have invalid token. You can also have a message uh printed out as well. So we just say message colon error dossage.
Okay. So all of that should be good. We go and zoom out a little. This is how our function looks like. So this one require authorization. So after that we have the uh require scope function. And in the require scope function. It kind of looks similar to this. And here we have a request response next as well. And we're going to make sure we're using scopes as string. And we split them and filter them based on the boolean. And we also have the scopes include scope return the response of 403 insufficient scope and the required as next.
After that we also have the uh app.get get which is the API profile and we also have the other section here. So API profile and require authorization require scope API read require response and we have the response JSON message user and we have all this [snorts] and then we have the listen function. So we're going to listen on port 5000 this time and [snorts] we're going to say running server on localhost port 5000. The resource server does not trust the client. Okay? It only trusts tokens that are this server we're creating. Okay? It only trusts tokens that are signed by the author's key. The first thing that we created um not expired and issued by the correct issuer which is ISS and intended for this audience which is AUD. Scopes are how we enforce what is allowed. And uh now we have to go ahead and move on to the next section.
Let me go here. So we had uh the off server and the resource server. All server um um and the resource server is basically um is the thing that which I mentioned does not trust the client app and it will only only uh trust tokens that are uh sent uh signed by the O server's key. Okay. And [snorts] uh not expired and issued by the correct issuer and intended for the audience. So that's what the resource server would do right uh it will only trust those tokens from the O server and once that are done what we can do is we can build the client app so let's go here going to create a new folder or [snorts] open it up in VS code now the the client app which is the authorization code and the PKCE this app will kind of send the user to authorize which is a slash authorize fun uh folder or route. It would receive the code in /callback. It would exchange the code plus code verify at slash token and call
the resource server. Um then we created over here. So if we go back, so if I go back here, [snorts] so this is the resource server and then um so what the uh client app would do, it would send the user to the slash authorize path. It would uh go and receive code in /allback. It would exchange the code plus the code verifier plus this slash token. It'll call the resource server which we created over here with authorization bearer. And now let's go back to the folder for client app and open this up in a terminal and dedicated here. Um, and what we're going to do is going to make sure we have the npm in it hyphen y and we also have the npm i express cookie parser and we have axios.
All right, all of that should be installed. And let's go to the package.json JSON just like how we normally do just comma here and oh not dev dependencies but um we have the so let me go back okay so this is the express uh we don't have a type here I normally version description main you know people used to add it here in the keyword section so mean and then we have um type come on. Why is the thing not running? Um, maybe it's because Yeah, I I'll just keep it consistent how I'm doing it. So, I'm just going to keep it here on the bottom even though it's not going to be the best practice to include it. So, I'm just going to Okay, type and then we have the double quotes module create the client app index um.js. So we go here and in here we're going to create a very simple index.js which uh uses a lot of the things that we've been using before. So [snorts] I'm going to explain it line by line. Let's
go here. The first thing is we're having express cookie parser axios and random bytes and create hash from crypto. We are using the initialization for express and we also use the cookie parser. We have the O server at port 3000. We have the resource server which is at port 5,000. And the client ID is demo client. And we also have the redirect URI which is a 4,000 call back. Um we have a B 64 URL input which is setting all that um which was there before. Now also inside the helper functions we have generate verifier. We have a code challenges s uh 256 and uh we also have the generated state also have the get the login and response cookies and all all those other important necessary stuff to [snorts] get the app running on port 4000 look close.
So if we go here we have um all this like we mentioned generate verifier the code challenge s 256 and the generate state. Okay. And uh we also have this slash um that goes to request and response and we send a client app. This app uses authorization code and we have the login and this is a login path which has the generate verify the code challenges of S2 um um S256. We have the code verifier and the generate state. We also have the response code verifier with code verifier range HTTP only which is true. And we have the resource of oath stage and the state of HTTP true as well. And these are the uh authentication pieces.
We have the authorized URL search params and we have the redirect authorized URL to string. We have the call back function that kind of calls back um making sure that the token response exchange the code for a token and uh we have the grant type which is authorization code code redirect URI set to that client ID is a client ID and the code verifier which is a two string of headers with content of X www form URL encoded and we have a con ex uh access token and refresh token expires in which is the token response data and the Access token has this which is set to true. Response co token as all that set to true. We have the send uh logged in and making sure all of that is accessed and the profile has all these axios.get authorization and the stringify making sure the strings are called and matched according to what the API call has been updated to. The refresh URL has the tokens and the refresh token has the
token at rest response. Um and it has the headers which has the content and the cookies access token and the send which we're doing which along with listening to the portal 4,000. Um so the slash login starts um you know what we see over here we go to this one there's different functions /lo/callback slp profofile slrefresh okay so let me explain this so that you understand it in a very s uh simplistic manner this one is just a get function okay this is a normal get function and it's just going to show out the client app okay so these are all the http endpoints and the /lo is it starts OOTH by building a URL to slash authorize.
Okay, you can see over here um we generate the state protects against CSRF. That's a big issue. We also code uh verifier. You can see over here uh our secret PKCE value. We [snorts] have the code challenge. Um that one is going to be for the hash of verifier that we safely sent to O server. Okay. And we have the slash callback which kind of receives the [snorts] code. Then we call slash token. Um in the slash callback we have um a lot of the code that we need and we call call / token with a code verifier to prove it's really us. Then we call the protected API using authorization bearer. Okay.
um access token allowing us to view and you know make sure everything is running according to what we're expecting it for right to work for and all of that is being done within the local port 3 4000. Now we have to run the full system uh we need like three terminals opened up. So the first one is the O server. So if we go to the uh this is the O server here. So we can open up the terminal and uh [snorts] we are going to do node and we have index.js. So since all server is running on port 3000 you can see over here it's running.
Okay. We could go to the resource server which is on the other side here and we open up its terminal and we do the same which is node and we have index.js and this is running on 5,000. Okay 3,00 5,000 [snorts] the resource server is running the o server is running. Now what we want is the client application to run. So [snorts] let's go to the client application which is this one. Open up its terminal as well. Clear that. And we run node index.js. And this is running on port 4000. Again you can see over here this one was the code that we gave with the slash only.
And this uses authorization code plus PKCE. Okay. Now when we click log in and we're logged in. You can see over here call back code and then we have this very very characterized uh string. So access token expires in 900 seconds. Uh you should see the JSON from the resource server. Okay. So now you see call protected API and refresh access token. If we call protected API we have API call failed error invalid token message expected 200 okay from the JSON web key uh set HTTP response which is totally fine but if we go back here and refresh the token refresh access token call protected API again and it's uh giving the fail response.
Now you got to have to understand one very important thing making sure that all these applications are running and making sure that nothing is failing. So let's go back to here and log in and you know just making sure we're in the entire resource code. The best practices that make this production grade is that we uh have the conventions to highlight clearly in the video like always validate tokens on the resource server. In the resource server, we have to validate the tokens. Never trust the client. The API must verify signature via JWKS. the expiry, the issuure, the audience, the scopes, all of that has to be done through the resource server which is going to be here. [snorts] So the resource resource server validates tokens on the tokens. Uh that's exactly what we did with JWT verifier, issuer and audience if you remember in our uh index.js that we created here. Um then we also have we
have to keep access tokens short-lived. So we live we use like 15 minutes the the limits damage if leaked and uh we use refresh tokens carefully because they store refresh we store like the refresh tokens carefully and securely rotate them in production uh issue a new refresh token each refresh invalidate old one and add revocation log out uh PKCE is mandatory for public clients. [snorts] So this is a core modern best practice that we're using and adopting and we use HTTPS in production. OOTH uh redirects and token exchanges must be protected by TLS.
um common mistakes to demonstrate and great for a best tutorial is kind of what makes your uh makes it real and teach it like how we de debugging skills is like we make sure we uh understand that there's a lot of mistakes people do and one is the state mismatch the CSRF protection and uh we remove the state cookie or change it to show error uh and this prevents attacker driven and redirects. Also, another mistake is like the wrong audience. If we change the audience in resource server to something else, uh we show token rejected and explain the token wasn't meant for this API. Another mistake that's common is scope missing.
Change requested scope to not include API read and it shows 43 insufficient scope and explains ooth is about permissions. After building this demo, I would mo the most terms do not build their own uh after building this uh you know dummy demo. I wanted to demonstrate that most teams do not build their own ooth uh servers from scratch. Instead they use oath octa AWS cognto Azure AD or key clock and implement client app flow resource server validation via GK GWKS. But the reason we built this is so they understand what slash authorize does slash token does what PKCE exists and what the resource server must check.
So Oath 2.0 is a secure way for apps to access APIs without passwords and authorization code plus PKCE is the modern safe user login flow. Resource servers must validate GWT signatures [snorts] and and claims using JWKS and enforce scopes. Short-lived access tokens plus carefully handled refresh tokens reduce risk. So that's what I would want to demonstrate here. And one thing that I would say is like, okay, we're logged in. When we access call protected API, okay, and I copy this and I say, okay, I get this kind of response. Okay.
So I get the URL which is this one but um the response is this thing. So this is kind of like an error that we want to fix. And the uh it's common when like people build OOTH JWT systems from scratch and fixing it will actually make uh this better because you kind of explain how resource servers really verify tokens. Um uh so let's walk through exactly the error what it means and how it's happening and how to fix this. So this error we're seeing um this is the error is coming from the resource server. Okay, not the client. So, we're in the client, but it's coming from the resource server. And what it means in plain English is the resource server is trying to verify the access token.
And to do that, it reads the JWT header, sees it's signed with RS 256, looks for the public signing key via the JWKS endpoint, and sends an HTTP request to uh localhost port 3000, well-known JWKS.json. But one of these is happening that either the GWKS endpoint is not is not returning HT 200 or the JWKS response is malformed or the key format is invalid for verification or it could also mean the JWTK ID does not match any key in the JWKS. When this happens, Jose throws this error and that's what we're seeing. The root cause I think in the authorization server uh code that we provided this part is incorrect for horiz verification. So go back here into the O server.
Uh we have a um const. So if you remember there was a part which had this thing which is const JWK await is equal to export private key. Now this exports a private key JWK not a public uh key JWK and create remote JWK um set expects public keys only. So that's why as a result the response server fails with the JWKS and the response exits but it cannot find a p valid public signing key verification fails meaning expected 200 okay from the web JSON web server set HTTP response and the correct fix uh what we do is we would generate one RSA key pair we use the generate key uh we use the private key to sign tokens and um we publish only the public key in the JWKS endpoint. Uh so we're going to generate keys properly uh from from inside the O server. Um you remember again I was trying to go inside here. We have to run the open SSL commands and we will have now private.pam and public.pam used to verify and update authorization
server key handling. [snorts] replace the part in the all server index.js and we'll remove and replace it and load keys correctly and fix the token fix the JWKS and it should work. Um so what we want to do is first we have to generate keys properly inside the O. Uh we want to run that. So I would normally go okay and see where is this located. So first of all making sure I know the path here. So it's in documents. After documents we have projects. So we have here cd projects should be over here. There you go. Projects.
And then we have um oath 2.0. So cd oath 2.0. Okay. Let's go back here. Oath. There you go. After that, we're in the OOTH node demo. O node demo. Now, within [snorts] the OOTH node demo, um, we have O server. So, we're going to go to CD O server. Okay, we're in the O server now, which is very good. And we're going to run the first open SSL command. Um, so we're going to have the private.pm. Paste it here. See all that. And you'll see a reflection here. Okay. After that, we have the public.pm as well. So, run that as well.
Paste it. And there's the public. Okay, cool. Now we have you uh private.pm which is used to sign okay and the public.pm is used to verify we have author uh update the authorization server key handling and uh in the o server index.js file. So remember that part where we were using this function. Um just go back here so that you can see it. Okay. import and go. Okay, so we're in all server the So remember the import that we were doing? I think it's somewhere over here. Import sign JWT export from Jose. We're going to replace it with this thing which is okay.
[snorts] Import FS from FS. Now load keys correctly. add this near the top. So [snorts] in the section, what we're going to do is we're going to make sure we also add. I'm just going to make sure the after this we'll have this added private key pam. Okay, since it's already here, we can remove that issuer and key ID because all of that was there before. Or I could have just replaced it with that one section. So I'll just cut that and bring it here. Okay. So now that we have all of that there, we have to fix the slash token signing code. Uh so [snorts] so if you go to the private key await and the access token if we go here uh private key then import a way import all that 256.
Okay, the access token and we have this one. So if you go here to the section access token and then we have all that. Okay. So this is kind of um what we can do is we can just make sure we're uh using it correctly. So, private key all the way to this section here. Place it with this one just so that it's properly aligned. You can just use a tab to uh improve the spacing here. Um after that, we also have to fix the JWKS endpoint. This is the key fix. Um so, remember the section where we're getting the app.get get um the section over here.
[snorts] So well known JWKs we're going to replace it with this one. So like that and since that is there we we these fixes import the SPKI imports a you know a public key exports the JWK exports it to a valid J uh JWKS format. The resource server can now fetch the key match. K ID and verify the signature. Um verify the JWKS endpoint manually. Very important. So open the browser. If I run this application, it should be running. So if I go to the um uh URL this one. So right now you can see here um we're type we're getting a type error. Okay.
But when we finish it, we get a proper key signature. Um, so what I can do right now is go back here and just have to verify that my public key and private key are being linked properly. So since we were using those um, read file sync. Okay. And they're in the same directory here. Turn the server off. Restart it.
Okay. And I think that was the all server. Okay. Okay. So now you can see here since I restarted the server um you can see something similar to this which is keys KT KTY RSA and all this uh being passed in. This is exactly correct. If N and E exist which is over here and here uh HTTP status is set to 200 then the resource servers will work. Okay. Now we have to make sure we restart everything. We have to stop all servers. So let's stop this one. We'll stop this one and we'll stop the client as well. [snorts] So we'll stop all servers and restart in the order. First off server is going to run then the resource server and then the client app. [snorts] Then we go back here. So, so the O server will first run using node index.js.
Then the resource server will run node index.js. And then the client app will run node node index.js. Okay. So when all of these are running, we go to the um the local host of port 4,000 and we click on login. Now you can see over here call key uh click call protected API and then it should example uh see the JSON but as we pass in or we click on uh this I get the URL endpoint as and the axio's error. I'm getting a status error. Okay. Okay. So, we're going to try to fix this axio error which is a request failed. Uh so we have first we have to understand why we're getting this. Um we're getting this 400 error and this error happens after the redirect succeeds. So we have a call back code and then we have this thing. Okay, this means like the slash authorize worked and uh the authorization code was created. Redirect state handling is correct. Failure happens during the step. We have client
which is post token exchange code plus code verifier for tokens. We have a 400 from uh slash token. Almost always means one of these is wrong with either the code verifier, the redirect URI, authorization code already used or expired and gone to type or the client ID mismatch will diagnose. So, [snorts] so cause which is um kind of like the client is losing the code verifier cookie between login and call back. This this makes the line fail this uh computer challenge um because code verifier is undefined and the result is in invalid grant 400 uh in client app. If we go back here um we have the console.log cookies and code verifier.
Um, we have to fix the um, if you go to call back function which is right here. Okay, so this is the call back function. I don't know where it ends. Okay. So we just end Okay. We just write something like
okay. So let me just have this two lines added. If I get the uh if I get in the console the client I have running on request fail with status 400 code verifier. So let me run that cls and then okay um node index.js JS JS Okay, login. Let's go back. Okay, we're getting access token, refresh token, code verifier, and a oat state. Um, so we're getting all of these passed in. Code verifier is this one. And then we have the axial call. So if you look at this properly and so our error is failing in the profile section and it's failing because the Axio get uh profile and the Axio stack trace points to line 74 which is still the call back token exchange. Um the correct fix would be make the call back handle only the ooth response. So if you go here um so the call back so at the top of the call back if you see over here refresh profile call back. So we have and we have this section here which is
this one. So we have code um request query. Okay, we'll just add this section. It prevents accidental execution of callback without a code. Clear PKCE after the successful exchange. So once the token exchange um succeeds immediately delete the PKC cookies. Um so after the cookie you would see over here a line this one cost access token refresh token const access token refresh token token this one this is all in the call back. Right. So after this block, we'll just add the clear uh cookies code so that we have them cleared.
And then it's critical oath hygiene. And once we're done with that. Okay. So, we're just going to make sure this is added. Sorry, this is added right after this one. [snorts] And uh should be also reset and we'll have the redirect. So instead of that, so over here you can see this send function. We'll just paste in the redirect. Hope that would help fix that. Uh so the call back basically would be like that. Okay. So let's go here and um let's go here. We also go here.
node index.js node index.js node index.js JS insufficient code required API and the reason why this uh uh authorization PKC is because we don't have the scale of it but I hope you kind of understand this whole logic behind it. Um this was kind of a gist of this entire app and uh the client never you have to understand one thing and that is the client never gets passwords. Okay. The O server never serves data and the resource server never handles login. So that's basically it and the refresh token uh rotation and um you know you can also have OIDC discovery and all this kind of stuff but explanation of the roles is client. First of all, if you look at the client uh which is this one, this is the app the user is using.
Okay, it asks for permissions, stores tokens and calls APIs like it would say please let me access this user's data. Okay, the authorization server which is this one is the security authority. It logs users in and creates signed tokens. Yes, this app is allowed. Here's the cryptographic uh proof. Okay, cryptographic proof. And then the resource server, the API with the data. It does not trust apps. It trusts tokens. So, so it would say, "Show me a valid token. I'll return the data." Okay. So, that's basically it. Um, we'll just create a repo of this on GitHub so you can get an access to it. So, I'm going to go here, sign in, and uh going to go here and we're going to create a repository.
So, one other uh repository I have is here. So, I'll just I'll use the other one. And what I'm going to do is I'm going to make sure we have the um repository created. So let's go here, create a new repository, and I'll call this as oath code project. Okay. And uh just going to you know, you can turn on that if you want to, but we'll just create like that. And now we can go to this one. I will just put it in here. So, okay. Not in cm. What is that? CMD. Um, so we have get in it initialized. After that, we go to other commands like get add.
So get add everything get commit of an M we have first commit and then we have get branch m main we get remote add origin. Let's copy this and then we'll have get push origin main. So we have bas supported for get operations.
All right. So one other thing I wanted to do is uh also show you that we can have um get up co-pilot help us. Okay. So let's see here. Okay. I want you to help me push this code on GitHub. [snorts] First, write me a read memb. Okay, so let's see what it does for us. I'm just going to create the readme file again. And I'll create a short to-do list for adding the readme and pushing the just create it for me. Okay.
But once it's done, so I will allow that. It's a little slow, but yeah. Okay, cool. I don't know where it added it. I don't even see it honestly. The repo root. Where are you? Where is it? What is he even talking about? Okay, so let's go here um and see why it's not running. So I try to run or I try to push it. Okay, we're gonna have another token access because I think the old one expired or something. So, we go to the settings. Of course, I'm going to have to go to the developer options, personal access tokens classic um and expired. So, we're going to have to create a new one. We [snorts] have generate new token. So, I'm going to show you how to do it. I have to do it off screen because um one thing you want uh is not to leak the data. So we'll just generate a new token.
[snorts] Okay. and just make it sure it doesn't expire or sometime [snorts] copy it and paste it here. All right. So, as I added that now, let's see. It should push it. Okay, we're getting a issue here. Let's see again. I pasted that. Okay. Why am I getting that? I don't understand. So let's say I [snorts] get this while pushing my code to GitHub. I added the personal access token classic as well during the sign in box from GitHub.
The 40 denied means get up to get you but the token user you're using does not have the right access to the repository. adding a pad in the signin box doesn't allow guaranteed permissions. So what it's going to do is it's going to say what in the world is this clear stored GitHub credentials Windows R control G Windows R and okay. Oh wow. Website credentials could be over here as well. Yeah, I did this a couple of times back as well. So [snorts] it should be like git somewhere. Microsoft account. Um I don't see any git.
[snorts] Yeah, I I don't see anything. Go to the Windows credentials. Remove anything related to GitHub or this thing. No. Git will force you to reauthenticate because it's not there, right? It's not there. I I saw there was nothing there to get over get uh repush the token correctly. So I'm going here and paste that again. It's going to ask me for this thing and then there's a personal access token. I'll go and again I have to stop this and uh let's see.
Okay. So, it's still not working. Um, I can go and show you the results here. So, you can see over here we have GitHub, origin, fetch, and push. [snorts] I mean it's it's the same thing. I I set the origin properly. [snorts] So you go back here if this revolves create under a different account or organization if you want paste hyphen V. So, I mean, it's all there. All we have is a push and it doesn't allow us. Just sign in with the code. Sign with your browser. I'm just going to open that up, which is not what I want.
I don't know what happened now. So yeah, main issue cannot be resolved. A branch that's what I'm getting. [snorts] Okay. Okay. Okay. Okay. You're going to have to change that. So, get branch m [snorts] and then push it to main. Why is it entering random characters? Okay, it's not push it to the set up stream correctly. New origin master. I don't know why, but it's not working, bro. Okay, we'll just add all this stuff. Let's see. It's not adding any of that. Okay. Use master instead of main if that's your branch. All right. So, let's go say get branch.
Which branch are we in? Main. Get branch. Get checkout master. [snorts] All right. So, let's just say I don't know why it's not working. Get branch. It's like sometimes um it messes up and then it's not going to like access it completely. Okay. So, yeah, this is what I'm getting better.
So it's not master or branch is that one. So we're just going to paste this one. Okay. Why is it copying everything? We just need to copy that one. Oh wow. All righty. Resolve merge prompted get status and commit push. Push. I don't know why this is the a last case scenario.
So you can see over here we have an issue. Oh, it's capital M. Don't tell me that, bro. That's the worst, man. Why in the world is it like that? So weird. Anyways, now that we are done that, you can see here the code is finally there. Okay, so hope you like this video. Um, please subscribe to coding cleverly. Um, that's my YouTube channel. And thank you for watching this free code camp tutorial. Hope you learned a lot about authentication, OOTH, and PKCE and tokens and all that good stuff. So, stick around. See you around. Peace out.
Questions about this episode
What is discussed in "OAuth 2.0 Course for Beginners"?
In order: 0:00 OOTH 2.0, Facebook, Google LLC, Node.js; 1:10:00 GitHub; 1:20:00 GitHub.
What is mentioned in this freeCodeCamp.org episode?
10 people, works and ideas are mentioned, including OAuth, GitHub, PKCE, OOTH 2.0, JWT, Axios. Each one on this page links to its own page with every episode that mentions it.
How long is "OAuth 2.0 Course for Beginners"?
The episode runs 1 h 30 min; the transcript is about 10,472 words in 83 timestamped sections.
More from freeCodeCamp.org
About this transcript: the episode's spoken words as captioned on YouTube, split into timestamped sections. Reclip.Me locates every person, work and idea in the text, keeps the time it is first named, and quotes the words around it verbatim. Last updated January 24, 2026.