Passkey Authentication with Express.js and Docker – Web Authentication API Tutorial
Key Takeaways
This video tutorial demonstrates how to implement passkey authentication using Express.js and Docker, covering topics such as public key pair credential systems, containerization, and web authentication APIs.
Full Transcript
learn more about the web authentication API by building an app that implements Pass Key authentication the web authentication API lets you create and use origin scoped public key credentials to authenticate users at the end of this course you will have an expressjs app connected to a postrest database inside a Docker container hello and welcome to this div Rhino video here on the free code cam channel so glad to see you in this tutorial we will learn more about the web authentication API by building an expressjs app that implements Pass Key authentication to follow along you will need to have Docker installed and running you can head over to their website to find the version that suits your environment even though we're using no JS in this tutorial you do not have to have it installed on your machine this tutorial will focus on the key Concepts involved in the implementation of of a public key pair credential system it does not dive into security any robust error handling data validation or any other authentication Edge Case by the end of the tutorial we will have created an app that will allow the user to register and create a pass key with an authenticator of their choice they will also be able to log out and log back in with their brand new Pass Key now let's get started we can begin by creating a new folder for our project and immediately changing into it then we will create the config files we'll need to work with Docker we'll create a Docker file a Docker composed. yaml file and a Docker ignore file now let's open up the docker file to add an instruction to pull the official node.js image for version 19 we will also add an instruction to set the path to the working directory over in the docker composed. yaml file we will set the version to 3.8 then we will create our first service for our web server it will use port 3000 and persist data in a volume in the dot Docker ignore file we will want to ignore node modules and any npm debugging logs that's the minimal config we need to get started we can now run the docker compose up command to create our new container now we can verify that node.js was successfully installed by entering our web container and checking for the version if you see a similar print out you're good to go while we're still in the web container Shell let's initialize our project to use npm then we can install the Express package as a dependency now we can create a new index.js file to act as the entry point to our application then we can head to the expressjs documentation and copy over the example web server starter code we'll update the port constant to to use an environment variable and also set the host variable to listen to any available network interface now we can start up our web server by running the node index.js command if we open up Local Host Port 3000 in our browser we should see the hello world text let's head back to our web server code and say hello to the universe instead if we go and refresh our browser now our changes are not reflected our app has no way of rebuilding on the Fly yet to introduce this Behavior we can install the node Monon package as a Dev dependency now with this package we can start our web server by using the node mon command instead if we head to our browser now our changes are reflected upon refresh but what happens if we delete our node modules folder perhaps a teammate has cloned the repository and has not install the dependencies if they try to run the web server they will see a message about missing packages they could always just reinstall them but this is kind of sad let's improve the developer experience by updating our Docker file with an instruction to copy over the package.json files then we can add another instruction to install dependencies so now whenever a new Dev spins up this project they have all the dependencies they need we will also head over to the docker compost. yaml file and add a volume to persist the node modules between builds we should also add the startup command here too so that it maps to the docker compose up command now we can exit the web container and rebuild our app we can restart our app using the docker compose up command if we head into our browser nothing has visually changed however our app is still functioning as expected which is a good sign to round this section out we will add some final touches to our Docker file we can add a line for the starting command then we can add another instruction that copies all the project files from the host into the Container these instructions won't make a difference for our local setup but they will be useful if we ever deploy our app to a service that doesn't support Docker compose this section is over and you're doing great let's keep going in this section we will set SQ eyes up as our om while our app is still running we can open a different terminal Tab and install the SQL IED package then we can also install the SQL CLI package as a Dev dependency once that is installed we can initialize sqy for our project using the CLI tool this command will create all the files we need however they are not in the locations that we'd like to have them in so let's do some housekeeping let's create a couple of new directories called app and DB then let's move the models directory into the new app directory we'll move the migrations folder into DB and the SE this folder into the DB folder as well then we will rename the config.js file to database. JS now we will open up the models index file and make sure our config is pointing to the correct file we've made quite a few changes but squiz expects the files it generates to be found in specific locations so we need to create a sqy RC config file to overwrite the default paths that SQL eyes expects finally we need to update our database config file we'll convert the Json to JavaScript and store it in a constant called DB settings which we will export we need to convert this file from Json to JavaScript so we can access environment variables we also change the dialect from MySQL to postgressql by default SQL users MySQL let's set up a postgress database for our application we've already started using using environment variables in our code but we haven't actually defined them yet so let's create a EnV file to store them the PG host will point to the database container that we will create shortly PG Port will be set to the default port for postgress PG user will be the name of the user that will connect to the database PG password will be the password and PG database will be set to the name we will give our database once our variables are defined we can head into the docker composed. yaml file and tell our web service to pull them in now we can set up a new service for our postgress database we will not set up our own Docker file for this service and instead we will use an official postgress image we will also use the environment variables from the EnV file to set up our database the port will use the default postgress port and we will set up a named volume to persist our database between builds lastly we will set the web service to depend on this new DB service now let's build our database container we can kill the server and rebuild the app then we can start it up again we will access the shell of the new DB container and use psql to connect to our database using the credentials we have stored in ourv file if you see a similar output your database has been set up we can also use a database client to connect to our database we can use the same credentials from ourv file here as well our database is currently empty while we have seen how we can connect to the database manually we should also give our web server a way to connect programmatically let's head back into the terminal and install the postgress package then we can create a helpers folder in our DB directory here we will create a little file file that will allow our code to access the database this file will define an export a module called DB this module will use sqy to create a new database connection using the variables located in ourv file we will also set the dialect to postgressql now that our app can use sqy to connect to the database let's generate our first sqz model this will correspond to a database table we will run the model generate command to create a user model we will give it a name and a single attribute for email this command will generate a new model file and an accompanying migration file let's open up our user model file and add some more attributes we can see that our email attribute already exists which is expected now let's improve it until tell sqe that it should be unique we can now add a new handle attribute and the usual timestamp column we want to follow a different convention for the table names so let's also provide a new table name setting we can head over to our migration file and update the table names in there as well let's also tell the migration that we need the email to be unique we can add the handle attribute here as well now we have enough to migrate the database we can use the sqz CLI tool to perform the migration once the command executes we can see that the settings in our migration file have been applied if we open up our database client we can see a new users table that contains all the columns we configured let's set up a dedicated routes file and our first controller in our terminal let's create a new file to organize our routes within this new routes file we will import the Express package so that we can create a new router now that we have a dedicated routes file we no longer need to to put our routes in the index.js file let's cut our only route from index.js and paste it into the routes file and then let's import and use our routes file within index.js now let's create a new folder for our controllers within the controllers folder we can create a new pages controller introducing controllers will allow us to further clean up our route handlers inside the pages controller let's add a new action called welcome the new welcome action is a call back Handler that we will pass to our routes it takes a request a response and the next call back is its arguments within its body it will check for a user if there is no user it will render a welcome message if there is a user it will call the next function on the stack if this pattern looks familiar it's because this is just a regular old middleware function that is commonly seen in nextjs apps now we can head back into our routes file and clean it up a little further we will import the pages controller and set up the root route so that it calls the welcome action from the pages controller and with that we now have the concept of routes and controllers within our app now let's configure our app for front-end views and static assets we will be using the ejs and express ejs layout packages so we can hop into the terminal and install them then we can head into the index.js file to configure them we will tell our app to use layouts and we will set the path to where all our View files will live then we will set our default layout file and also set our view engine to use ejs we are using the path package so we should import it now that our app is configured to handle views let's create a folder for them we will start working on the global application layout first so let's create a folder and a file for it we can open up the application layout file and add some boilerplate HTML markup to it there isn't much happening here we've just given it a title and specified that it should be responsive the only interesting part is this template string this is the bit that will pull the body content from our individual View files now we can create our first Real Page since we've already got a welcome controller action it only makes sense to create a new welcome view we will head to the project repository and copy over the markup for our new welcome page HTML is out of scope for this tutorial so we will not go over this but please feel free to dive into it on your own if you're interested now that we have a proper welcome page we can head into the pages controller and update the action to render it if we head into the browser now we can see the content we added for the welcome page now now let's work on giving our views some Styles back in the terminal we can create a new CSS file in the public folder then we will head into the project repo and copy over the readymade CSS CSS is also out of scope for this video so we will not be explaining any of the Styles once we save the file and head into the browser we see that nothing happened that's because our app knows nothing about this new stylesheet we added so first we can head into our application layout file and import our stylesheet then we can open up the index.js file and configure our app to look for static Assets in the public folder now when we go back to our browser and refresh we should see our Styles coming through but our logo is broken let's go back to the project repository and download the images folder then we can drag it into our local public folder inside we have three custom cursor images and a couple of different logos if we head back into our browser now our logo image should now be showing up now let's create a page that will hold our registration form if we head into the browser now and click on the register button we're met with an error saying the register route does not exist well this makes sense because we haven't created a register route yet let's open up our routes file and get to work we are importing the O controller that doesn't exist yet then we add a new get route Handler that calls an action called register which will live in the O controller that we will create now we can open up the new Au controller and add the register action which will render the register view then we will head into the project repo and copy over the markup for the register view if we refresh our browser now our register page should be rendering correctly now let's create a page for our login form then we can add a route Handler for the login route and inside our off controller we can add a login action that will render the login page then we can head into the project repo and copy over the markup for this login page once that is saved we can head back into the browser and check it out it looks very similar to the register page but this is the login form now let's set up the final page which will be the dashboard let's add a new view file for it and we will update the root route to take a second action then we can create a new admin controller and add a dashboard action which will render the dashboard page this dashboard action is the one we referenced back in the routes file now let's get the markup for the dashboard and paste it into the view file we will not view the dashboard page in the browser for now it can be a nice little surprise once we start implementing pass Keys we've made it to another Milestone we've covered a lot and you've been phenomenal let's create a model and migration for the public key credentials we already have a users table now we need a public key credentials table to store the public keys for each user we will use the model generate command from the SQ CLI tool giving it a model name and a single attribute which is the public key string this command will generate a model file and an accompanying migration file we can open up the public key credentials model and add a coup couple more attributes for username and external ID these columns will help us identify public keys and Associate them to specific users and since we're following a different convention for table names let's also configure the table name setting too we will add a belongs to Association because a public key should belong to a user record through the user ID now let's open up the migration file and add the same attributes here as well then we must also remember to update the table name to match the new table name we set in the model file we've already stated that public key records belong to users so we should head into the users model and indicate that a user has many public key credentials now we can migrate the database so that our new changes can be applied if we open up our database client we can see a brand new table called public key credentials our database is all set now now let's let's configure passportjs to handle authentication we will be creating a service to organize our passportjs related code let's first install the base passport package then we can install the passport web Oren strategy package this will give us the ability to authenticate with pass Keys now we can create a new file for a service in the services directory we will set up the module with an init method that contains the three main functions we need to implement now we can head into our routes file and import our newly created passport service we will also import the session challenge store from the web Oran strategy package because we will need this to generate challenges then we can create a new instance of the passport service and a new instance of the session challenge store then we can initialize the passport service instance and pass the challenge store to it back in our passport service file the first thing we need to do is configure passport to use the web orth strategy the passport use method accepts a call back we have created one that we have called use web auen strategy within the function body we will return a new web Oren strategy instance which takes three pieces of information first we will pass in the challenge store in an object then we will pass in a verify call back and a register call back we should should also remember to import the passport and web Oren strategy packages at the top of the file since we are using them the verify call back will be used when a user logs in and the register call back will be used when a user registers a new account neither of these callbacks exist yet so we will need to create them we can start with the verify call back the end goal of this function is to look up a specified user in the database and get their public key it will will be an async function that takes in an ID user handle and a done call back we've made it an async function because it will be interacting with our database within the body of the function we will create a database transaction if anything were to go wrong the database transaction will allow us to roll back our actions without persisting the changes to the database we're using the DB helper so we should remember to import it at the top of the file back in the function body we will use a TR catch statement if the actions within the tri block are successful we will commit the transaction however if we get an error we will drop into the catch block and roll back the transaction now let's put together our database interactions back in the tri block we will use sqz defined one public key credentials record from the database where the external ID matches the ID value we passed into the verify function we must also remember to pass in the transaction because we want this action to be wrapped in a database transaction we're using the models object here so we should import it at the top of the file for some basic error handling we will send back an error message if we cannot find the public key credentials now let's try to find the specific user that owns the public key credential we will also remember to pass the transaction to this action as well because we want to to be wrapped within the same database transaction as our first action if we are unable to find the user we will send an error message saying as much we will also add another check to ensure that the user handle we passed in matches the user handle for the user we retrieved from the database if all these actions are successful we will commit the transaction and execute the done call back with the credentials record and public key and now our verify function is complete we can collapse it and work on the register call back the end goal of this function is to create a new user and a new Associated public key credentials record and persist them in the database it will be an async function that takes in a user ID public key and a done call back within the body of the function we will create a database transaction if anything were to go wrong the database transaction will allow us to roll back our changes without persisting any data to the database we will use a TR catch statement if the actions within the tri block are successful we will commit the transaction however if we get an error we will drop into the catch block and roll back the transaction now let's put together our database interactions in the tri block we will attempt to create a new user record in the database using values from the user object we passed into the register function we will also remember remember to pass the transaction to this database interaction because we want to be able to roll it back if anything goes wrong for some basic error handling if we are unable to create a new user record we will send back an error message next we will try to create a new public key credentials record to associate with our new user record again we are passing in the transaction because we want this action to be wrapped within the same database transaction as our user cre creation action if we are unable to create new credentials we will send back an error message if the actions within the tri block are successful we will commit the transaction but if we get an error we will drop into the catch block and roll back the action now that the register function is also complete we can collapse it and move on looking back at our notes the next task we need to work on is to serialize the user to a token serialization involves taking in a regular JavaScript object containing user details and converting that into an encrypted string which is also known as a token to achieve this we will use the serialized user method from the passport package the serialized user method takes a call back so we will create a little call back function to pass to it we will just name it serialized user function because naming is difficult when this call back function is executed it will call nodes process process next tick function which will invoke the done function with the JavaScript object containing user details process next tick is a node.js thing and is a little out of scope for our current tutorial the last thing we need for this passport service is the ability to deserialize a user deserialization involves reading and extracting user information from a token we can use passports deserialized user method here and pass it a little call back function that we will call deserialize user function this is very similar to the serialize user function but we sent through the whole user object the web Oren API requires session support so let's set that up we can kick things off by installing the express session package we can also install the connect session SQL package so we can use sqy to store session data to our database now we we can head into the index JS file and import the passport package the express session package and the connect session sqz package we should also import the DB helper since we will be storing session data in the database now we can create a new instance of a session store and configure it to use our existing database we will now configure out app to use the session we've set the secret to an environment variable that doesn't exist yet so we will come back to it we've also made sure to tell our app to use the session store we created above and we've indicated that we'd like the cookies to expire after 1 week in a real production system this is the place we would also add more security related settings finally we will sync our session store and also configure our app to use passport to authenticate with sessions now let's go back and add the session secret variable to ourv file we're just using some random string in order to see our changes take effect we need to restart our server that has been running in a different terminal tab if we visit our database client now we should see a new sessions table this is automatically generated for us if you are having trouble seeing the same results you may need to head into the browser and do a quick refresh now our app supports sessions while we're in a configuration mood let's head back into index.js and add a few more helpful settings we need our app to be able to work with Json so let's configure it to use the buil-in express. Json function we will also install and configure a package called malter so we can submit multi-art form data then we will also install and configure the cookie passer package so that our app knows how to pass cookies finally we should also configure our app to handle any URLs that contain query pram data we are now ready to take a high Lev look at how pass Keys work public key cryptography Is Not A New Concept if you are familiar with RSA private and public Keys these ideas may feel Vaguely Familiar to you however pass keys are making the concept more mainstream with the goal of replacing password authentication pass keys are used in two different phases es the first phase is the attestation phase where the pass key is created during the user registration process the second phase is the assertion phase where the users's credentials are verified allowing them to log in during these two phases there are three main entities involved first is the authenticator which could be a smartphone a password manager or a USB device next is the client an example of which could be a user interacting with their browser and finally the relying party which could be your own server or the server of another application that supports pass keys in the upcoming sections we will implement the two different phases let's Implement phase one where a pass key is created upon user registration before we dive into the code let's go over a quick summary of the main things that happened between the client the authenticator and the relying party first the user submits the the registration form prompting the client to request a challenge from the relying party secondly the client calls the Navigator do credentials. create method this will prompt the authenticator to use the challenge it receives to create a new credential key pair the authenticator will pop up a dialogue asking the user for verification in our case this verification will be done through a fingerprint once the user is verified the private key is stored on the authenticator and used to sign the challenge then the public key the sign Challenge and the credential ID are sent back to the client finally the client sends the public key the sign Challenge and the credential ID to the relying party the server verifies the sign challenge using the public key and the session information it receives if the verification process is successful the server will store the public key credentials and user details in the database now that we have an idea of the flow we want to achieve let's start working on the code inside our code editor let's find our bearings by taking a look inside the register View at the very bottom of the file we have imported two script files however neither of them exist yet so we will need to create them first we will create the attestation register script file then we will create the base 64 URL script file we will head into the project Repository story and copy over the contents for the base 64 URL script the original GitHub link for this little utility helper can be found at the top of the file so you can get the code from the original project as well this base 64 URL script will ensure that binary data and URLs are properly encoded to plain text to avoid ambiguity once we've saved the file we can start working on our attestation register module we will create a new register class that contains an init function we have left some notes to guide us the first three steps correspond to the steps we saw in the section overview the fourth step is Just a ux Touch where we redirect the user to the dashboard at the bottom of the file we will listen for the window object to load once the window has loaded we will add an event listener to the registration form so whenever it is submitted we will create a new instance of our register CL class and call the init function we will also prevent the default behavior that the browser gives to the form element our first function will allow the client to request a challenge from the server it will be an async function that we will call get challenge in the body of this get challenge function we will make a request to this endpoint that doesn't exist yet it will be a post request and we will send through the data a user submits through the registration form then at the end of the function we will return the Json response now let's go and add this register public key challenge endpoint to the routes file we're adding a new post route Handler that calls the create Challenge from function from the off controller this action doesn't exist yet so we can head into the off controller and add it within the body of this action we will return a middleware function inside this middleware function body we will set up a user object we will set its ID to a uu ID value we generate the name will be derived from the email address the user sends through in the registration form we will also remember to import The UU ID package at the top of the file we haven't installed it yet but we will install it shortly we will then use the challenge method from The Challenge store to set up yep you guessed it a challenge the user object and the challenge will be returned in the Json response we have used the base 64 URL package to encode our user ID and challenge we also use this on the front end but this is the back end so it needs its own copy of the package while we're here we will also install The UU ID package from earlier we should also remember to import the base 64 URL package before we move on now we can head back to our attestation register script and work on the Second Step this is the step where the client will prompt the authenticator to use the challenge to generate a new credential key pair it will be an async function that will receive a challenge then it will use the Navigator credentials create function to create a new credential key pair this Navigator credentials create function is part of the web authentication API from JavaScript at the end of the function we will return the newly created credentials you may have noticed that we passed in some options that don't don't exist yet so we will need to create them the first key we set up in the options object needs to be public key because this is the type of credential we are looking to create we will also set the name of the relying party and then we will set up a user object containing the user details next we will set the sign challenge we got from the previous step and finally we will set up an array of encrypted credentials the relying party will accept each of these correspond on to a cosos cryptographic algorithm and are ordered with the most preferred algorithm at the top of the list finally we will add an authenticator selection that prefers user verification that's all we need for the Second Step let's collapse the function and move on to our third step this step will send the credentials to the server for verification and return the authenticated user we will create an async function that takes in user credentials this request will also require us to send through some options but we will build those in a separate function shortly we will use the fetch API to send a request to the login public key endpoint that does not exist yet this request will be a post request and we will pass along the options object which we have yet to create then we will return the response now onto the options we'll set up a new function to build these the response field will contain some details about the client data Json and the attestation object we will also check if the authenticator supports different types of transports transports can include things like Bluetooth USB or smartphone desktop hybrids to name a few a full list of options are available in the documentation if the authenticator has transports we will set those in the options too now let's work on the login public key endpoint we will head into the routes file and add a new new post route Handler this Handler has three actions configured when working with expressjs we are able to move through a stack of middleware so in this case we will first use passport to perform a check to see if the user is authenticated correctly if they are we will grant them admission into the application if the check indicates the user is not authenticated then we will drop into the last middleware function in this list and deny them entry this is very similar to border control at an airport none of these actions exist yet so we can head into the off controller to create them at the very top of the controller we will set up our passport check this function will be using the authenticate method from passport so we will remember to import passport at the top of the file the authenticator method takes in the strategy and an options object we are using the webo and strategy and setting up a couple of options that are related to error messages now we will work on admitting an authenticated user if they are eligible for entry we will set their destination to the roote route this will bring them to the dashboard then we can add the deny user function we will first check the status code to ensure that it is not a 400 code so we can return early but if we do get a 400 code and the user is not found then we will indicate everything is not okay and redirect the unauthenticated user to the Lo login page this is all we need to authenticate a user the final step we need to complete the registration process is to redirect the user to the appropriate page our redirect function is pretty straightforward we will just set the window location's href value to the destination we set when checking the user's passport our registration flow is now complete so let's head into the browser to test it out we will use our cool email address to sign up the client will then prompt the authenticated to seek verification from us there are different options available to us these options relate to the transports we set in our options earlier if a user is on a Mac they can opt to use the built-in keychain they can also choose to authenticate using a QR code which they can scan on their smartphone but for Simplicity we will use our Chrome profile however all other methods will work too one once we hit continue we will be asked to scan our fingerprint and because our passport check was successful we can now see our empty dashboard we covered a lot of ground but our registration is done thanks for persevering you're doing a great job now let's add the ability for a user to log out in our browser we can see that we already have a log out button but if we click it now the world will end we need to head back into our code and wire things up first we will start in our routes file and add a new post route Handler for the logout route which will call the logout action which doesn't exist yet so we can hop over to our off controller to add it the logout action will be a middleware function which will use the log out method from the request to log the user out and redirect them to the root route you may be surprised but this is all we need now if we head back to the browser we can safely click on the button to log our user out let's Implement phase two where a pass key is used to authenticate a user and sign them in before we start writing the code let's see a quick summary of the main things that happen between the client the authenticator and the relying party in this phase first the user submits the signin form prompting the client to request a new random Challenge from the relying party this first step is more or less the same as the first step of the attestation f secondly the client calls the Navigator credentials get method this will prompt the authenticator to request verification from the user in our case the verification will be done through a fingerprint once the user is verified the authenticator will use the private key it had previously stored to sign the challenge then it will send the sign challenge the credential ID and the username back to the client the client then sends this data onwards to the relying party the server verifies the sign challenge with the public key it previously stored in the database if the verification is successful the server will find the correct user in the database and log them into our application now that we have an idea of the flow we want to achieve let's get to work on the code inside our code editor let's Orient ourselves by looking in the login View at the very bottom of the file we have imported two script files we handled the base 64 URL script in Phase 1 but the assertion login script file does not exist yet so let's create it now we can open up our newly created file and add a new login class that contains an innit function we have left some notes to guide us in the first step we will check to see if our browser supports conditional mediation for public key credentials the middle three steps correspond to steps we saw in the phase 2 overview and the last step is a redirect function that works exactly the same as the redirect function we implemented in the registration phase at the bottom of the file we will listen for the window object to load we will then create a new login instance and if the browser supports pass Keys we will initialize our new login instance as a first step we will check if the browser supports conditional mediation for pass keys if the browser does not support this we will return early this conditional mediation will allow the browser to find any available pass keys and display them to the user in a dialogue box if the browser does not support this it will not be able to find the available pass keys this will prevent the login process from completing for our second step the client will request a challenge from the server we will use the fetch API to make a request to this endpoint that doesn't exist yet it will be a post request without anybody content then we will return whatever response we get back from the request now let's go and add this login public key Challenge and point to the routes file we're adding a new post route Handler that calls the get Challenge from function from the off controller this action doesn't exist yet so we can head into the off controller and add it it will take in an instance of the challenge store and it will return a middle middleware function in the body of the middleware function we will use the challenge method to create a new challenge if there is an error we will fall through to the next middleware on the stack if the challenge was created successfully we will send it back in the response body now we can head back to our assertion login script and work on the third step this is the step where we get the users existing Pass Key and use the sign challenge to authenticate on the server it will be in an async function that will receive a challenge then it will use the Navigator credentials get function to get an existing credential key pair from the user this Navigator credentials get function is also part of the web authentication API from JavaScript at the end of the function we will return the credentials we found you may have noticed that we passed in some options that don't exist yet so we will need to set them up within the options we have indicated that we will be using conditional mediation and our credentials are of the type public key within the public key option we will send through the sign challenge now we can move on to step four which will be the step where we use the credentials to log the user in we will create an async function that takes in user credentials this request will also require us to send through some options but we will build those in a separate function shortly we will use the fetch API to send a request to the login public key endpoint which we created in Phase One this will be a post request and we will pass along the options object which we have yet to create then we will return the response now onto the options we'll set up a new function to build these the body will contain the user's credential ID then the response field will contain some details about the client data Json authenticated data the signature and the user's handle we will also check if the public key credential object can give us any information about the type of authenticator that was used to create our credential key pair if we are able to get this authenticator information we will set that in the options to now we can collapse these completed functions and work on our final step for phase two this step will redirect the user to the appropriate page this redirect function is exactly the same as the one we implemented in phase one we have decided to duplicate it here to keep each phase separate and make the flow of information easier if you have strong feelings about dry code you may refactor it as UC see fit our login flow is now complete so let's head into the browser to check it out when we click on the email input field a little dialogue appears to display our available pass keys this is the conditional mediation that we were checking for in our code once we select our Pass Key we will be prompted for verification we can scan our fingerprint to verify and because our passport check was successful we are now back in our empty dashboard and our login button still works this concludes our implementation of phase 2 where we use our Pass Key to log in and there you have it in this tutorial we learned more about the web authentication API by implementing pass keys in an expressjs app we also use Docker and Docker compos to manage our nodejs installation a text version of this tutorial can be found on div rhino.com and a repo can be found on my GitHub account I'll leave both links in the description for this video If you learned something new while building this project please let me know in a comment or like the video and subscribe to my channel thank you for watching I appreciate you
Original Description
Learn about the Web Authentication API by building an app that implements passkey authentication. At the end of the tutorial, we will have a little Express.js app connected to a Postgres database, through Sequelize, in Docker container.
📎 Text tutorial: https://divrhino.com/articles/passkeys-express
💻 Code: https://github.com/divrhino/divrhino-passkeys-express
✏️ Course developed by @DivRhino
❤️ Try interactive Databases courses we love, right in your browser: https://scrimba.com/freeCodeCamp-Databases (Made possible by a grant from our friends at Scrimba)
⭐️ Chapters ⭐️
⌨️ (00:21) Introduction
⌨️ (00:37) Prerequisites
⌨️ (01:10) Expected end result
⌨️ (01:25) Getting started
⌨️ (05:18) Configure Sequelize
⌨️ (06:54) Set up Postgres
⌨️ (10:40) Routes and controllers
⌨️ (12:16) Frontend views
⌨️ (16:58) Public key credentials
⌨️ (18:27) Configure Passport.js
⌨️ (25:38) Sessions
⌨️ (28:03) Passkeys overview
⌨️ (29:11) Phase 1: attestation/registration
⌨️ (39:11) Logout
⌨️ (39:56) Phase 2: assertion/login
⌨️ (46:46) Recap
🎉 Thanks to our Champion and Sponsor supporters:
👾 davthecoder
👾 jedi-or-sith
👾 南宮千影
👾 Agustín Kussrow
👾 Nattira Maneerat
👾 Heather Wcislo
👾 Serhiy Kalinets
👾 Justin Hual
👾 Otis Morgan
👾 Oscar Rahnama
--
Learn to code for free and get a developer job: https://www.freecodecamp.org
Read hundreds of articles on programming: https://freecodecamp.org/news
Watch on YouTube ↗
(saves to browser)
Sign in to unlock AI tutor explanation · ⚡30
Playlist
Uploads from freeCodeCamp.org · freeCodeCamp.org · 0 of 60
← Previous
Next →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
React: Production Server Setup Part 2 - Live Coding with Jesse
freeCodeCamp.org
cookies vs localStorage vs sessionStorage - Beau teaches JavaScript
freeCodeCamp.org
Browser history tutorial - Beau teaches JavaScript
freeCodeCamp.org
Graph Data Structure Intro (inc. adjacency list, adjacency matrix, incidence matrix)
freeCodeCamp.org
React: Parameterized Routing with Next.js - Live Coding with Jesse
freeCodeCamp.org
React: Dealing with jQuery Issues - Live Coding with Jesse
freeCodeCamp.org
setInterval and setTimeout: timing events - Beau teaches JavaScript
freeCodeCamp.org
Browser and Device Testing - Live Coding with Jesse
freeCodeCamp.org
Last Minute Updates - Live Coding with Jesse
freeCodeCamp.org
Post Launch Updates - Live Coding with Jesse
freeCodeCamp.org
React: Setting Up Google Analytics - Live Coding with Jesse
freeCodeCamp.org
React: Masonry Layout - Live Coding with Jesse
freeCodeCamp.org
Load Balancing Digital Ocean Droplets - Live Coding with Jesse
freeCodeCamp.org
try, catch, finally, throw - error handling in JavaScript
freeCodeCamp.org
Load Balancing: SSL Passthrough Setup - Live Coding with Jesse
freeCodeCamp.org
Graphs: breadth-first search - Beau teaches JavaScript
freeCodeCamp.org
React: Masonry Layout Part 2 - Live Coding with Jesse
freeCodeCamp.org
React: WordPress API Live Search - Live Coding with Jesse
freeCodeCamp.org
Creating WordPress Custom Post Types - Live Coding With Jesse
freeCodeCamp.org
Dates - Beau teaches JavaScript
freeCodeCamp.org
Miscellaneous Front End Updates - Live Coding with Jesse
freeCodeCamp.org
Merging a Pull Request from GitHub - Live Coding with Jesse
freeCodeCamp.org
React + Prettier + Standard JS - Live Coding with Jesse
freeCodeCamp.org
React: Sortable Responsive Table - Live Coding with Jesse
freeCodeCamp.org
Geolocation Sorting by Distance - Live Coding with Jesse
freeCodeCamp.org
Tradeoff Matrix - Agile Software Development
freeCodeCamp.org
The Definition of Ready - Agile Software Development
freeCodeCamp.org
Getting first React job without experience - Ask Preethi
freeCodeCamp.org
React: Google Analytics Click Tracking - Live Coding with Jesse
freeCodeCamp.org
Submitting a PR to an Open Source Project - Live Coding with Jesse
freeCodeCamp.org
Should I go back to school to get CS degree? - Ask Preethi
freeCodeCamp.org
Hero Section CSS Changes - Live Coding with Jesse
freeCodeCamp.org
Working Agreement - Agile Software Development
freeCodeCamp.org
A day at Pennybox with Co-Founder Reji Eapen
freeCodeCamp.org
React: Sorting and Filtering Data - Live Coding with Jesse
freeCodeCamp.org
React: Sorting and Filtering Data Part 2 - Live Coding with Jesse
freeCodeCamp.org
React: Building a New UI - Live Coding with Jesse
freeCodeCamp.org
Definition of Done - Agile Software Development
freeCodeCamp.org
Getting started with jQuery (tutorial) - Beau teaches JavaScript
freeCodeCamp.org
Making a React Blog with WordPress Content - Live Coding with Jesse
freeCodeCamp.org
React, NextJS, CSS - Live Coding with Jesse
freeCodeCamp.org
jQuery events - Beau teaches JavaScript
freeCodeCamp.org
React/NextJS Routing and WordPress API Custom Types - Live Coding with Jesse
freeCodeCamp.org
React: Working with API Data - Live Coding with Jesse
freeCodeCamp.org
React: Refactoring Components - Live Streaming with Jesse
freeCodeCamp.org
jQuery effects - Beau teaches JavaScript
freeCodeCamp.org
More React Refactoring - Live Coding with Jesse
freeCodeCamp.org
animate in jQuery - Beau teaches JavaScript
freeCodeCamp.org
"Finishing" My React Site - Live Coding with Jesse
freeCodeCamp.org
Starting a New React Project (P2D1) - Live Coding with Jesse
freeCodeCamp.org
React Project 2 Day 2: Learning Material UI - Live Coding with Jesse
freeCodeCamp.org
The Agile Manifesto - Agile Software Development
freeCodeCamp.org
jQuery: get and set with http, text, val, and attr - Beau teaches JavaScript
freeCodeCamp.org
React Project 2 Day 3 - Live Coding with Jesse
freeCodeCamp.org
The INVEST approach to product backlog items
freeCodeCamp.org
React Project 2 Day 4 - Live Coding with Jesse
freeCodeCamp.org
Chickens and Pigs - Agile Software Development
freeCodeCamp.org
React Project 2 Day 5 - Live Coding with Jesse
freeCodeCamp.org
jQuery: add and remove DOM elements - Beau teaches JavaScript
freeCodeCamp.org
React Project 2 Day 6 - Live Coding with Jesse
freeCodeCamp.org
More on: AI Tools for PMs
View skill →Related Reads
📰
📰
📰
📰
Beyond console.log: Advanced Debugging Workflows That Will Save You Hours
Medium · JavaScript
cgo Overhead Dropped 30%. When Should You Actually Care?
Medium · Programming
Why Everyone is Wrong About Website Development?
Medium · Programming
The Silent Killer in Your Node.js APIs: Mass Assignment & How to Catch It Before Production
Medium · Programming
🎓
Tutor Explanation
DeepCamp AI