0 to Auth in an Hour Using NodeJS SDK


Passwordless authentication may seem complex, but it is simple to add it to any app using the right tool.

We will enhance a full-stack JS application (Node.JS backend + React frontend) to authenticate users with OAuth (social login) and One Time Passwords (email), including:

- User authentication - Managing user interactions, returning session / refresh JWTs

- Session management and validation - Storing the session for subsequent client requests, validating / refreshing sessions

At the end of the workshop, we will also touch on another approach to code authentication using frontend Descope Flows (drag-and-drop workflows), while keeping only session validation in the backend. With this, we will also show how easy it is to enable biometrics and other passwordless authentication methods.

Table of contents

- A quick intro to core authentication concepts

- Coding

- Why passwordless matters


- IDE for your choice

- Node 18 or higher


Let me start with introducing myself. My name is Asaf. I'm a software engineer at Dscope. Dscope is an authentication platform that helps all developers facilitate in authentication in a secure manner. In the next workshop, we are going to add a passwordless authentication to a standard node application. We are going to start by overviewing some basic authentication concepts and basically to go over the flows that we are going to add. This is like a background knowledge that we need to acquire in order to incorporate the authentication properly. This will probably take about 15 minutes. And in the rest of the time, we are going to take an existing application and add the authentication layer for it. If you ask what's in it for me in this workshop? So I believe that by the end of the workshop, you will have a better understanding of the authentication workflow. This is something that may seem a bit intimidating at the start, but I think it's quite simple if you go over it well. And maybe more importantly, I think you will be able to take a standard node application and add authentication on top of it if you have any node application, you can take it and add authentication in a few steps. Like I said before, we start with the core concept. And afterwards, we are going to do the coding. I'll give more information about that later. Hello to the person who wrote in the chat. I'm very excited as well. So I'm going to assume that you have a basic understanding on node and the express framework. The application is written in typescript. So probably if you also if you know javascript, I think it's enough. We don't heavily use typescript, but the node application will be written in typescript. Also just to prepare for the coding session, if you want to do it on your own, you should have a GitHub account, any ID that you choose to call on and node version 18 or higher. So this is up to you, but of course, I'm going to do it here. So this is optional, of course. Let's start with covering the very basic concept. So what is authentication? The way I see it, authentication process is basically verifying someone's identity. The internet is a very distributed place. So if someone owns another machine says that there are some someone, whether it's a user or a system or a device, they basically have to prove it before they want to access the application, the application, a specific resource on the application or the system. This is a very, very essential aspect of the application security. This is a we use the authentication layer basically to protect our system for unauthorized access. And this is something that is super important in the digital age. There are so many data breaches, identity thefts in the world. Doing proper authentication is crucial for your application security. We hear more and more about incidents of accessing unauthorized access. And if we want to write a secure application, we should invest in it. Just as a side note, we have a really good learning center with a lot of graphical assets there. I sometimes go back there and refresh my memory. There is a link in the presentation, which is also going to be shared. So what are we going to build? And this is more related to the flows that we want to add. If we break down the steps of authentication, we can basically break it down into two major steps. The first step is when the user wants to access the resource or the application the first time, they should authenticate. So usually it starts by user wants to access the resource, he submits some kind of request to access. The user usually provides its identity. This can be basically an email, a username, mobile number, social security ID, or what's this is very application specific. And a proof is usually something that basically proves who you are. And there are a few authentication factors that usually they call factors. authentication factor is basically based on something you know, for example, a password or maybe a security question. By the way, a quick tip in security question, usually, if you care about security, don't answer the real information, because usually this is something that is very simple to gather this information like your pet name, etc. Another authentication factor is something you have a very common example for this is a mobile device. So if I send you an SMS with a code, and then you give back the code to me, I can say I can this is basically a proof that you have a mobile device. And also something you and basically the something you are or something you inherit. This is a relatively new factor of biometric information, either with a fingerprint or face recognition. I also heard about solutions that scan iRetina and stuff like this. So does someone have an idea or heard about more authentication factor other than the three that we mentioned? Something you know, something you have something you are. So just like, and just just to share from our experience, there are somewhat new factors of a geographic factor. So basically, if you log in from a certain geographic location, this is something that usually makes you either suspicious or not suspicious. If you heard about the impossible traveler scenario when some user access the same application from both location that basically, a person cannot change the location in in that amount of time. And another one is behavioral factor. And basically, if you behave in a way that is not a suit you like you access resource that you are not used to access or the rate, this is another factor. So basically, after the application gets the proof and identity, it's verified, basically check that they match. And then the application grants the user access. This access usually grants in a way of a session or a token. We'll double click it real soon. And usually the token or session is using something in following requests. This is usually done so the user won't have to provide all the proofs in every request. So usually, you authenticate once, get a token or a session and use it for a certain amount of time. And the verification part after you get a session, you usually provide this session or token and the application only needs to validate it. So if we are talking about session management, we talked about session versus token. There are two technical processes to implement this. One is use a server side session and the other user client side token. So a session usually means that the server store in its database, the session information and provide an identifier to the client. And in subsequent requests, this client, this session ID, sorry, is used to retrieve the session information from the database. The server side session is usually very secure. The server has a, the server, which is a secure place has full control over it. For example, if you want to revoke a session, it's something that is relatively simple to do in server side session. And on the other side, there are client side tokens. I think someone in the chat mentioned before about JOTS. JOTS are basically the acronym of JSON Web Tokens. This is a JSON that contains information about who you are and it signs in asymmetric manners with the private key in the backend, in the server. And the token itself, all the token is sent to the client and in subsequent requests, the JOT is basically validated and its information is used to determine, basically the server or the application can use to determine information about the user. Can someone maybe share what do you think are the advantages of client side tokens, of tokens of JWTs? Okay, so I'll share that basically all modern application, most modern application prefer to use JWT for scalability manners. And so basically, if you're talking about microservices architecture, you don't need to have a single point of access to access, for example, a database. The performance of basically validating JOTS is something that is relatively cheap to compute in comparing to accessing a database entity. And this is basically, I think that Kevin also wrote that you don't have to store the server session. This is absolutely right. Okay, so one last thing before we jump into the coding session, let's describe the application that we are going to build. The application purpose, I'll demo it really quick in a minute, is basically to show the user some kind of financial revenue about the customer user's organization. It doesn't really matter, but the important thing is that it shows like something that we want, it contains a resource and we want to protect this resource with an authentication layer. And the application is composed with a desktop application, the web application that's written in react.js. We are not going to touch, to spend time on the code, but if you want, you can go to the open source repo and take a look. The node.js application server, this is what serves the information to the client. We are going to use a very simple express server. And another component is authentication service. This is usually an external service that manages the authentication, the user's identity and authentication. Usually, the application is communicating with the authentication service with a sort of like an SDK library. In this workshop, we are going to use this code as the authentication service. Okay, right before we are starting, a few words about myself. My name is Asaf, as I said before. From this code, this code is authentication platforms that helps all developers to build a secure and frictionless authentication and to build user journeys for B2B apps, B2C apps, and basically any app that you have. We are focusing on passwordless authentication. We are offering MagicLink, social login by the OAuth protocol, SAML for businesses, one-time password biometrics and more. And we have multiple authentication approach, either no code or some code, and you can also use the api. And if you want to read more about it, just write this code at Google or go to this code com. That's it. Beside that, I play Frisbee a lot of my free time. And I love to learn everything I can about astronomy and astrophysics. And I'm a big Al Madrid fan. Okay, so without further ado, let's start. We are going to use the following repository. This is a repository in the Scope Sample App organization. I'm going to, if there are any questions, feel free to stop me now or write in the chat. I'm going to share the link in the, oh, thank you, Chris, for sharing the link. So I'm going to go over the steps that we described before. Basically I think I have the application running. Okay, so I already cloned the repository to my computer. Feel free to copy, to clone this repository to your machine. You can use the main branch, okay? But I already built up front a few branches. Let's see all the branches. Like we are going to start at the workshop 01. And then from there we are going to progress. I'm going to show each step of adding another part of the authentication in the following steps. You can decide if you want to code it or just listen to me and go over the steps later. I'm going to use the branches to show the progress. So let's start. I already cloned. You can do npm install. This will install all dependencies. And if you want to see the application running, just run npm run dev. This will open a basic application. The front end is in react. The back end is in Node. And basically what this application has, it has a few widgets of basically revenue information. And I'm going to work with the Chrome DevTool Open so we can see the networks and other important information like cookies and the request details. Let's start with describing how the repository looks like. Okay? So we basically have a client folder and the server folder. And I'm not going to cover the client at all. The server has one entry point, index TS. And you can see what the application does here. Basically start by spinning up an express service. And it serves four different routes. And that's it, basically. So right now those routes are not protected at all. And our goal in this workshop is to add authentication to those routes. Okay? So the first step that I want to add is basically to have the user a place that they can authenticate. The way we are going to implement this is to build a very minimal form in the application and implement the request to the authentication in the server side. So again, I'm sorry, I'm returning to the diagram here. The request to initiate the authentication will go from the react application to the Node backend and to the authentication service. So the first thing we are going to do, we want to have a way to interact with the authentication service. You can go to our authentication provider, in this case we are going to use Disco. We can log in. I already have an account, but you can go to this application and create an account. It's free. And you can create it will automatically create a project for you. You don't need to do anything here in this place except copy the project identifier. This will be used to identify the project when you accessing the authentication service. So the first thing we are going to do, I'm going to basically I can check out the first workshop, the branch workshop 01. This is the same as the main branch. And I created the first step already in workshop number two. What I need to do, the first thing I need to do is to run is to go to the server directory. And I want to install this dependency. So I'm going to run npm i minus minus save exact and the name of the package. This is the package SDK that will help us to communicate with the scope. And let's see what we've done here. We import it. And we just created and we created basically an object, a client auth that will be later going to use to interact with the scope. Another thing that you can do with your project ID is to create a dot n file in the server directory. And let's open it. It was already exist. And to create a one entry with your project ID from before. I'm going to copy the format in the chat if you find it useful. Okay. This is basically a way to copy your project ID. You can also put it in the code. Whatever you feel more comfortable. Of course, if I run the sample, after every step, I'm going to run the sample to make sure that it behaves the same. Right now, we didn't do anything important in the backend. We just created an instance of the SDK. And nothing has changed. The next step that we are going to do, the first authentication method that we are going to add is called one time password. One time password is usually a code that the authentication service sends to your mobile or email. In this case, I'm going to send it to the email so I can show you in my mailbox here. We're going to send the code and then we're going to submit it. So the first step that we're going to do is to add the part that sends the email. Okay. So let me check out the branch of Workshop 3. This is the branch that in this branch, what we have done, we have basically added a host route named OTP login. And this route used the client authentication, the client auth, sorry, to perform basically sign in or sign up or in with an email. Okay. If you want, you can do a phone instead, sorry, SMS instead. We also provided a WhatsApp. But this is basically up to you. I'm going to add an email form. And what we've done is basically we make sure that the request succeeded. If you use typescript, this is very helpful to see the response. So you can see what the response contains. Basically an object with an SDK response, which basically is containing the code, the request succeeded or not, and the generic data and error information in case there is an error. So I also added a form in the UI. Let's see it. Okay. Just going to navigate to... This is the form that I've added. And we can see that if I logged in, I got 200 requests. And this is basically send me this code. The next step that we are going to do is to take this code and to verify the user using this code. So let's go to the next branch. Okay. So in this branch, we added another route of one-time passcode verified. This route gets the email and the code from the UI. And it uses another method of... To verify the email. And again, check for error. And the important information after this part is that we want to share the authentication information from the response to the browser. The way that it's done, this is a short function that we wrote. It takes the express response and the SDK response, the SDK output. And what it does, it basically set, take the cookies and the session jot and return and set the cookies from this information. The SDK returns the session JWT on the response. And extra cookies for other use. For example, refresh token maybe here. And what we want to do is to add the cookie to the response. Let's see it in live. So let me delete the previous mail. If I'm logging in right now, I've also added in the UI code form. And once I get the email, the code from the email and enter it, right now I'm... I can see that in the response, the response returns two cookies. Okay? It returns a session cookie and another refresh cookie. The session cookie is the one that we set here automatically. And the refresh cookie is the one that the scope returns in the cookie object. I'm going to talk about refresh in a bit. The refresh is usually the session token is the one that chooses to identify the user. It contains... It may contain more information about the user, like its roles or if it's a B2B app, it may contain its tenants and other custom claims. I'm not going to cover too much about JWT, but in like in few sentences, this is an object with a signature and the object contains information about the user, who the user says that they are. Let me know if you have any questions and I can elaborate on that. Okay. So we saw that the cookie are returned to the application. We can also see in the Chrome DevTool that we have the cookies here, the session cookie and the refresh cookie. And we can see that in every request that we send back to the server, those cookies are automatically set. So the server set the cookies on the response. And from that on, all the requests back to the server are containing those cookies. So I think now it's a good time for the, I said the major part and the important part about authentication is basically to protect those results. Right now we've done the first part of authenticating the user. The user is authenticated, but we still didn't check that the user is verified. So what we want to do now is we want to add a middleware in front of all those routes that will make sure that the user is authenticated. Let's see how it looks like. So we added the following code. We have here a function. This is Express middleware. And we're going to cover it in a bit. And we've created the dedicate router. The router, we will run the middleware before each request, and we change all the APIs that we want to protect to run on this specific router. Let's see what this middleware does. This middleware basically takes the request and the response and what should run afterwards. And the middleware can decide whether to run the next function, which in this case is the function that returns the data or not. So let's see what the middleware does. The first thing is it's parse the cookies. The cookies are on the request. We set it in previous steps. So we can see basically a very simple code that takes the request, take the cookie, and break it down. I wrote it here in plain code. So we can see what happens under the hood. But there are many dedicated packages for these purposes, for example, cookie.js and other javascript libraries that can do it for you. Afterwards we are extracting the refresh and the session token. And we use the client auth, the SDK, to validate the session. What this action will do, it will take the session and will make sure that the signature matches the information of the JWT. If you haven't seen JWT before, it contains three parts that are encoded in base64. The first part contains a metadata about which algorithm to use and other information about the token. The middle part contains the information about the user, the user ID, and maybe stuff like roles and other custom information. And the last part is the signature. So what this part will do, it will validate the signature. And it matches what the user says they are. And if this part basically succeeds, it takes the ‑‑ we created the request context. By the way, up here we declared how the context should look like, if you are interested. This is a way to express, to articulate, basically in typescript to articulate how we want the request context to look like. And we added this information here. The question is basically usually a refresh session token is valid for a certain amount of time. It's valid for five minutes or ten minutes. But usually this is done for security reasons. Because we don't want session to be ‑‑ once a session is signed and generated, basically it's valid until it expires. So the session validation time is usually about five to ten minutes. But we don't want the user to authenticate every five to ten minutes. For that purpose, there is the refresh token. Refresh token is used to communicate with the authentication service and generate a new session token. This is its purpose. Usually it doesn't contain information about the user. And it should be treated as a secret. So the session token should be used as a secret, but this is a very sensitive information. So it should be kept in a very secure manner. For example, all the cookies are set as an HTTP only cookie and as secure cookies. So this is also something worth mentioning. So if the session validation fails, the SDK will throw an exception. The middleware will fall back to try to refresh the session. Okay? We try to refresh the session. If the refresh fails, we will return an authorized response. And if the refresh manages to succeed, we want to set authentication cookies like we've done in the authentication because we want to communicate the user with the new session token. And that's it. Again, this refresh token is this authentication middleware runs before every request. Let's see it in live. Okay? So I'm going to refresh the page and we can see that we are still getting the information. I want to also go to incognito and to see what happens when we submit a request here. We can see that we are getting 401. The reason is because the cookies are not maintained between incognito window. We can see that we don't have any cookie here. And because we haven't sent the authentication, we haven't sent the authentication information, all requests fails. I see that there is a question in the chat. Doesn't the validation of Jot also check that the Jot access token is expired? This is a very good question. Yes. The SDK options that the SDK function that we saw here also validates that the session is valid and didn't expire. Every session, you know what, we can take a look here. Let's take a look here on the session. Going to copy it. There is a nice website called Jot.io. And you can see that those are the three parts that we discussed before. Metadata, the headers of the Jot. This is the payload. It contains, this is basically who the issuer is. This is the project ID in the scope. This is the user ID. And there can be more other metadata in this. And the third part that we see in blue is the signature. You can also see that the part of the payload is when it's expired. So for example, this token expired basically two minutes ago, if I'm not mistaken. Okay. Here's another question. So the session validation is about to generate a new access token once it's expired. So for the second question, the session validation doesn't do it alone. For this purpose, we have created a refresh. If the session validation fails, we create, we submitted a refresh request to get a new session. By the way, the SDK also supports, sorry, auth, validate, and refresh session in the same part. It's basically encapsulate the parts that we've seen here for you. Tom Carr, go ahead. Unmute yourself. Sure. I'm okay. In the validate session function, does it simply validate the JWT token or does it make a call to authentication server to validate the token? So for JWT tokens, usually validated, they are not validated in the backend, but the way that it's done, the validation is done by downloading a public key. The way JWT is built is built with a private key and generated with a private key and validated with public key. So what the api does under the hood, it's download once a public key and it uses to verify the signature of the token. I hope that answered the question. And this basically done, the download of the public key is basically done once. There is a protocol about how to verify JWT, a very interesting topic. And there are other aspects of, maybe we can see it here. Usually there are other information about the JWT, like what is the key ID? Because usually the service provider, the scope also does it. It rotates the public keys for security reasons. So every public key is downloaded once. Okay. Got it. Thanks. And another question is whether a validate and refresh also rewrites the cookies that goes into the client. So the validate and refresh also returns cookies. And when we made this session, like full disclosure, when we made this session, we find out that there are a few gaps in the validate and refresh functionality. This is one of the reasons that I'm demonstrating this in this manner. But yes, ideally the validate and refresh will do all this work for you. And it will return the cookies and then you will be able to take the cookies and put them on the user request. Let me continue. We were on, we have just saw how we protect the route. This is basically what we've covered in the flows that we've discussed. Usually to expose, we want, let's, because we have a bit more time, let's show more use cases of the authentication flows. So another thing that we want to do when we, or users, is usually we want to enable them a way to log out. So I've also implemented a log out route. Not that log out route is also an authenticated route. So let's see what it does. It takes the refresh token from the context and it calls the log out. This process will basically indicates to the authentication service that you cannot use this token to refresh more, to refresh any sessions. And afterwards it's clear the cookies from the request. So the browser will also clear them. We can see it here in live. I've also added another button here that triggers the log out. You can see that once I log out, once I press log out, I activate the log out functionalities. And you can see that I've got an empty cookies, which basically signal the browser to clear them. And if I'm going to the main page, I won't get any answers. Let me continue by cover another authentication method that I want to talk about briefly, which is OAuth. OAuth is a protocol that delegates authentication to another identity service. Usually we use a social accounts for this. For example, when you see a login with Google or login with Twitter or login with Facebook, this uses the OAuth protocol. In a few words about the OAuth protocol, how it works, the authentication, the application delegates the authentication to another service. It redirects the user to another service and it gets back a code or maybe a token. And it takes this token and generates, after it gets the token, it verifies this token and creates a JWT session token and JWT refresh token. The terms here are a bit confusing because there is the OAuth token and there is the session token. So this may be a bit confusing. Let's see it in practice and I think it will be a bit more clear. We added here two more routes. One is when the user presses, for example, the sign in with Google button, we trigger an api call to the authentication service to start authentication with Google. And we tell the service that after the authentication is done, we want to redirect it back to the OAuth finish route. Okay? And after this part is successful, start or start Google, we get back the URL and we redirect the user to this URL. Let's see it in live. Okay. Sorry. Okay. So I've added another button here in the UI. This will trigger the OAuth start route. And you can see that basically what it does, it takes the user to account Google.com OAuth 2. This is the protocol. And now the authentication is done with Google. Okay. And once I authenticate it, I've redirected back to this code. And this part redirected me back. It took the code from the query. This is the code that we transferred with the session. And then we exchanged this code that we got from Google in this case with the session. And afterwards, we set cookies in the same manner and we redirect the user back to the application. Okay. So and you can see that authentication works as before. And nothing has changed in that manner. And we didn't have to change anything in the authentication middleware. Okay. Any question about what we've done so far? In the JWT token, you mentioned that the JWT token is created using the private key. And then it is validated using the public key. Right. So does the server store the public key in memory or with it? Yeah. So yeah, the SDK, basically this object client auth, this is the SDK. It stores it internally in the memory. Just to iterate the first two things. This public key is not something is not secret. Anyone can access it. And its only purpose is to validate that the signature of the JWT matches the information that the user that is in the payload. This basically proves that the JWT was generated with the matching private key. And the other thing that I want to mention is that this public key is very lightweight. And it doesn't affect the application performance. It's very, very low. Okay, another question. What is different? Here the auth, we have an external identity service, the generation of the token. So this is a very good question. What is basically related to the purpose of auth? And the auth doesn't, the output of the social login is only to generate the code. The code, this is basically something that moves to the identities. And auth is a vast protocol. But it basically, beside the user, there is the service provider, which is the application that we have here. Sorry, this is the application that we have here. And the identity provider. Identity provider in this case is Google. So the application takes the user to the identity provider, it gets back a code. And the responsibility to generate the JWT is still on the application, the service provider, which in this case is the node application that we have. I think if you want to read a bit more about auth, we have really good articles in the Discord blog. So I would recommend to read more about it there. In the minute that I have, before I'm answering more questions, I want to present a different approach to what we've done here. So far, we saw an approach that the authentication is done directly from the application server to the authentication service. There is another approach that we in Discord believe is a bit more flexible and simple to incorporate, which is done from the mobile, from the, sorry, from the front end directly to the authentication service. So you can see that I removed all the authentication methods, the OTP and the OAuth. I left with a very, very minimal middleware that does only the validation. And all the work of the showing the forms and managing the tokens is done with the front end. I'm not going to cover it too much because this is basically done in a react. And this is not the scope of this workshop, but the TLDR of it is that we basically add the react SDK in the front end side. We wrap the application in authentication provider. And then instead of the flows, sorry, instead of the forms that we saw before, we just add a very minimal Discord component. And this component basically controls everything. I think I forgot to install the react app. So this form is completely no code. You can go to basically to the scope application and go to the flows page and control the screens and the authentications flows directly from here. So this is the screen that we see here in the application. It's very simple to embed it. And you can control the authentication methods that you want to add in the scope UI. We believe this way is much more flexible and simple to do. For example, if you want to add another authentication method, such as biometrics, all I need to do is to add here, for example, a biometrics button. This UI is highly customizable. For example, I can completely control over the layout of the application. And I can add another step here of biometrics. For example, sign up or in using biometrics, connect it with a very simple drag and drop experience. And once I press save, I can see the changes over here. This is something that is now, for example, I can use this to authenticate. And this is I didn't have to change anything in the front end or in the back end. authentication will work in the same manner. I think that this is what I have to show in the workshop. I'm available for a question here. And before that, I want to say thank you. Thanks a lot for everyone, for your participants. It was a pleasure to meet you. Hi. What you were describing last time was there is a way to that the front end application takes the token directly from the identity server or the code. And that code will be the security code that will use as a code to communicate secure with the back end. Right. That code will be validated from the back end to the identity server. When I'm speaking here identity server, I'm speaking as an entity that's not part of the server that we are using. It can be Google, it can be Amazon, it can be other entity, right? Right. You mean to the OAuth flow, right? Yes, exactly. Right. So, yeah, you are correct. The OAuth protocol redirects the user to the identity service, to the identity provider. Pardon? Identity provider in this case was Google. Google, you make the authentication with Google. Google in its turns takes the user, let me show it in the code, takes the user back to the identity server, they redirect URL with the code as a query parameter. And the application takes this code, validate it against Google. And if the validation against Google succeed, it will generate a session job. I hope this was, this answered the question. Yeah. Thank you very much for clarifying all my questions. Not a problem. Thank you. Thank you, sir. Another question in JotAuth, how to store a public key if you are using multiple instance of the server or a stateless server. So this is something that you don't have to worry about. It's completely transparent to you. You only need to call the validate session and the validate session will automatically, basically what it will be, let's maybe take an example for a session here and go to JotIO. So it will take the key ID from the header and it will download the key ID and the issuer and the key ID and it will download from the identity service, it will download, sorry, from the authentication service, it will download the matching public key. This is how it is done. You don't need to worry about storing it or managing it, the SDK does it automatically. And this is not bound to any specific instance. It can be done from a serverless function without any problem. Great. So one last time, I want to say thank you all. And it was a pleasure and see you next time.
63 min
10 Apr, 2023

Watch more workshops on topic

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career