{"id":8243,"date":"2024-09-21T19:13:55","date_gmt":"2024-09-21T13:43:55","guid":{"rendered":"https:\/\/pheonixsolutions.com\/blog\/?p=8243"},"modified":"2024-09-21T19:13:59","modified_gmt":"2024-09-21T13:43:59","slug":"jwt-authentication-with-access-and-refresh-tokens-in-node-js","status":"publish","type":"post","link":"https:\/\/pheonixsolutions.com\/blog\/jwt-authentication-with-access-and-refresh-tokens-in-node-js\/","title":{"rendered":"JWT Authentication with Access and Refresh Tokens in Node.js"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Authentication and security are crucial for maintaining user privacy and application integrity. A popular method to manage authentication is by using <strong>JSON Web Tokens (JWT)<\/strong>. In this blog, we&#8217;ll walk through how to implement JWT authentication using <strong>Node.js<\/strong>, along with strategies to manage token expiration using <strong>access tokens<\/strong> and <strong>refresh tokens<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What is a JWT?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A <strong>JSON Web Token (JWT)<\/strong> is a compact, URL-safe token that carries information between two parties, usually the client and the server. It consists of three parts:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Header<\/strong>: Contains metadata, such as the token type (<code>JWT<\/code>) and the hashing algorithm used (<code>HS256<\/code>).<\/li>\n\n\n\n<li><strong>Payload<\/strong>: This is where the actual data resides, like the user\u2019s information (e.g., user ID, username).<\/li>\n\n\n\n<li><strong>Signature<\/strong>: A hashed value created using a secret key. The server uses this to verify that the token wasn\u2019t altered by anyone other than the issuer.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Flow of JWT Authentication with Access and Refresh Tokens<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When a user logs in to your application, they receive <strong>two tokens<\/strong>:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Access Token<\/strong>: A short-lived token (typically expires in minutes).<\/li>\n\n\n\n<li><strong>Refresh Token<\/strong>: A long-lived token (typically expires in days or weeks).<\/li>\n<\/ol>\n\n\n\n<h4 class=\"wp-block-heading\">Why Two Tokens?<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <strong>access token<\/strong> is used to authenticate most API calls and has a short lifespan to minimize security risks.<\/li>\n\n\n\n<li>The <strong>refresh token<\/strong>, stored securely (e.g., in an HTTP-only cookie), is used to generate a new access token once the old one expires.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Node.js Implementation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s dive into how to implement JWT authentication in Node.js using both access and refresh tokens.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Generating Tokens<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">When a user logs in, we create two tokens: the access token and the refresh token. Here\u2019s the code to create these tokens:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const jwt = require('jsonwebtoken');\n\n\/\/ Create the JWT and refresh tokens\nconst token = jwt.sign(\n  { userId: user._id, username: user.username },\n  process.env.JWT_SECRET, \/\/ Secret key for signing the access token\n  { expiresIn: \"1m\" } \/\/ Access token expires in 1 minute\n);\n\nconst refreshToken = jwt.sign(\n  { userId: user._id },\n  process.env.JWT_REFRESH_SECRET, \/\/ Secret key for refresh token\n  { expiresIn: \"30d\" } \/\/ Refresh token expires in 30 days\n);<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <strong>access token<\/strong> expires in 1 minute, while the <strong>refresh token<\/strong> is valid for 30 days.<\/li>\n\n\n\n<li>These tokens are then sent to the client: the access token can be stored in local storage, and the refresh token in an HTTP-only cookie.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Middleware to Authenticate Requests<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">To protect your API routes, you need a middleware function to authenticate incoming requests using the access token. If the token is valid, the request proceeds; otherwise, an error is returned.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const JWT_SECRET = process.env.JWT_SECRET;\n\nconst authenticateJWT = (req, res, next) =&gt; {\n  \/\/ Allow certain requests without authentication\n  if (req.url === '\/graphql' &amp;&amp; req.method === 'GET') {\n    return next();\n  }\n\n  const query = req.body?.query || '';\n\n  if (query.includes('IntrospectionQuery') ||\n      query.includes('mutation LoginUser') ||\n      query.includes('mutation RefreshToken')) {\n    return next();\n  }\n\n  \/\/ Extract the token from the authorization header\n  const token = req.headers.authorization?.split(' ')[1];\n  if (!token) {\n    return res.status(401).send('Token is required');\n  }\n\n  \/\/ Verify the token\n  jwt.verify(token, JWT_SECRET, (err, user) =&gt; {\n    if (err) {\n      return res.status(403).send('Invalid token');\n    }\n\n    req.user = user;\n    next();\n  });\n};\n\napp.use(authenticateJWT);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This middleware checks for the presence of a token in the request header. If the token is valid, it extracts the user&#8217;s information and attaches it to the request object for use in the next middleware or route handler.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Refreshing an Expired Access Token<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">When the access token expires, the client can request a new one using the refresh token. Here\u2019s how you can implement an API endpoint to refresh the access token:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const refreshTokenHandler = (req, res) =&gt; {\n  const refreshToken = req.cookies.refreshToken; \/\/ Assume refresh token is stored in an HTTP-only cookie\n\n  if (!refreshToken) {\n    return res.status(403).send('Refresh token is required');\n  }\n\n  jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET, (err, user) =&gt; {\n    if (err) {\n      return res.status(403).send('Invalid refresh token');\n    }\n\n    \/\/ Generate a new access token\n    const newAccessToken = jwt.sign(\n      { userId: user.userId, username: user.username },\n      process.env.JWT_SECRET,\n      { expiresIn: \"1m\" }\n    );\n\n    res.json({ accessToken: newAccessToken });\n  });\n};\n\napp.post('\/refresh-token', refreshTokenHandler);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When the access token expires, the client sends the refresh token to this endpoint. If the refresh token is valid, a new access token is issued. If the refresh token is expired or invalid, the user will be logged out.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Security Considerations<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Refresh Token Storage<\/strong>: Storing the refresh token in an HTTP-only cookie helps mitigate XSS (Cross-Site Scripting) attacks since JavaScript cannot access the cookie.<\/li>\n\n\n\n<li><strong>Short-lived Access Tokens<\/strong>: By keeping access tokens short-lived, the risk of token misuse is minimized.<\/li>\n\n\n\n<li><strong>Token Revocation<\/strong>: To improve security, consider storing the refresh tokens in a database. This allows you to revoke them when necessary (e.g., upon user logout).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Conclusion<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">JWT-based authentication with access and refresh tokens is a powerful approach for managing user authentication in web applications. In this tutorial, we explored the basics of JWT, how to implement token-based authentication using Node.js, and how to securely refresh tokens to keep users logged in without compromising security.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Authentication and security are crucial for maintaining user privacy and application integrity. A popular method to manage authentication is by [&hellip;]<\/p>\n","protected":false},"author":507,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[1022],"tags":[],"class_list":["post-8243","post","type-post","status-publish","format-standard","hentry","category-web-architecture","psol-cat-web-architecture"],"jetpack_publicize_connections":[],"jetpack_shortlink":"https:\/\/wp.me\/phn2x7-28X","jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/8243","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/users\/507"}],"replies":[{"embeddable":true,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/comments?post=8243"}],"version-history":[{"count":0,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/8243\/revisions"}],"wp:attachment":[{"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/media?parent=8243"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/categories?post=8243"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/tags?post=8243"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}