<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[DHATRISH SINGH DIXIT's blog]]></title><description><![CDATA[DHATRISH SINGH DIXIT's blog]]></description><link>https://journal.dhatrish.in</link><generator>RSS for Node</generator><lastBuildDate>Thu, 09 Apr 2026 09:58:42 GMT</lastBuildDate><atom:link href="https://journal.dhatrish.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Implement OAuth 2.0/OIDC in React, Express and TypeScript with Prisma and implementation of Admin Privileges]]></title><description><![CDATA[In my previous blog, I focused mainly on the theoretical aspects of OAuth 2.0 and OpenID Connect. Now it’s time to bring those concepts into practice. For this blog , I’ve built a dedicated Proof of Concept (POC) to demonstrate a real-world implement...]]></description><link>https://journal.dhatrish.in/how-to-implement-oauth-20oidc-in-react-express-and-typescript-with-prisma-and-implementation-of-admin-privileges</link><guid isPermaLink="true">https://journal.dhatrish.in/how-to-implement-oauth-20oidc-in-react-express-and-typescript-with-prisma-and-implementation-of-admin-privileges</guid><category><![CDATA[oauth]]></category><category><![CDATA[OAuth 2.0]]></category><category><![CDATA[OAuth2]]></category><category><![CDATA[OIDC]]></category><category><![CDATA[React]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Express]]></category><category><![CDATA[authorization]]></category><category><![CDATA[authentication]]></category><category><![CDATA[prisma]]></category><category><![CDATA[prismaorm]]></category><category><![CDATA[PostgreSQL]]></category><dc:creator><![CDATA[dhatrish]]></dc:creator><pubDate>Sun, 25 Jan 2026 11:45:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/LBQkjp_ZC_g/upload/c836bba9c82807fbc1fd797254d34065.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my previous blog, I focused mainly on the theoretical aspects of OAuth 2.0 and OpenID Connect. Now it’s time to bring those concepts into practice. For this blog , I’ve built a dedicated Proof of Concept (POC) to demonstrate a real-world implementation. I’ve also included additional authorization features, such as an admin-level privilege layer, to make the demo more realistic.</p>
<p>The POC is built with a <strong>React</strong> frontend and an <strong>Express + TypeScript</strong> backend, implementing OAuth <strong>from scratch without using any OAuth-specific frameworks</strong> .</p>
<p>The deployment setup is fully production-style:<br />• Frontend on Vercel.<br />• Backend on AWS EC2.<br />• NGINX used for load balancing across my other services and handling SSL termination.<br />• A rate limiter implemented at the backend level to protect the EC2 instance from abuse, excessive requests, and potential denial-of-service scenarios. This ensures better stability, security, and controlled resource usage under real-world traffic patterns.</p>
<p>(If you’re interested in learning more about NGINX configuration , rate limiters or deploying services on EC2, I’ll cover those topics in future blogs depending on the engagement this series receives) .</p>
<p>Rather than swaying here and there , let’s focus on the topic currently on hand → so then lets go to system design we will follow for implementing authentication here -</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769294870425/1394b07c-fb13-4899-b5bb-1dfdefd5215f.png" alt class="image--center mx-auto" /></p>
<p>We can use this pattern for any of the auth providers , but for sake of simplicity , we will follow google OAuth / OIDC services .</p>
<p>We will break the approach in three basic parts -</p>
<ol>
<li><p>Password based Authentication .</p>
</li>
<li><p>OIDC/OAuth based Authentication .</p>
</li>
<li><p>Admin level authorization ( bonus ) + way to set password after logging in via google + Two Token Setup .</p>
</li>
</ol>
<p>before going into each of the steps , lets look into the schema first -</p>
<pre><code class="lang-javascript">enum Auth {
    GoogleLogin
    PasswordLogin
    Both
}

model User {
  id            <span class="hljs-built_in">String</span>    @id @<span class="hljs-keyword">default</span>(uuid())
  email         <span class="hljs-built_in">String</span>    @unique        <span class="hljs-comment">// required + unique</span>
  passwordHash  <span class="hljs-built_in">String</span>?                   <span class="hljs-comment">// for email/password login</span>
  googleId      <span class="hljs-built_in">String</span>?   @unique         <span class="hljs-comment">// for Google OAuth </span>
  name          <span class="hljs-built_in">String</span>                    <span class="hljs-comment">// required</span>
  refreshToken  <span class="hljs-built_in">String</span>?   @unique         <span class="hljs-comment">// only one refresh token per user</span>
  createdAt     DateTime  @<span class="hljs-keyword">default</span>(now())
  updatedAt     DateTime  @updatedAt
  authType      Auth      @<span class="hljs-keyword">default</span>(PasswordLogin)
}
</code></pre>
<p>apart from general user data , we are using passwordHash , googleId and authType for handling both authentication methods ,</p>
<ul>
<li><p><strong>passwordHash</strong> used to store the hashed password in case of email / password login</p>
</li>
<li><p><strong>googleId</strong> used to store the id received from google in case of google login</p>
</li>
<li><p><strong>authType</strong> tells which type of auth is used , google , password or both</p>
</li>
</ul>
<h2 id="heading-password-based-authentication">Password Based Authentication</h2>
<p>In Password Based Authentication ,</p>
<p>Both Login and Registration ( account creation ) will take place in two steps -</p>
<p>we will take email and password from the user , check if the password and email are correct and if so then we give the required cookies ( namely access token and refresh token ) to the client browser and log in the user.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769242044415/8c6d165c-f5f7-4396-9744-b291360aa163.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768428866581/c2098ec7-b241-42f5-8141-b6f6c998c1c9.png" alt class="image--center mx-auto" /></p>
<p>Let’s discuss how to implement Email Password Login -</p>
<p>Firstly , user will fill a form with email and password and then a POST request will be sent from the client side to the application server -</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">await</span> axios.post(
        <span class="hljs-string">`<span class="hljs-subst">${BACKEND_URI}</span>/v1/auth/login`</span>,
        {
          <span class="hljs-attr">email</span>: data.email,
          <span class="hljs-attr">password</span>: data.password,
        },
        {
          <span class="hljs-attr">headers</span>: {
            <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
          },
          <span class="hljs-attr">withCredentials</span>: <span class="hljs-literal">true</span>,
        }
      );
</code></pre>
<p>And now on the application server , we will deal with the data , check in the db if the user exists and then issue access and refresh token to the client browser based on the availability of user account .</p>
<pre><code class="lang-javascript">        <span class="hljs-keyword">const</span> {email , password} = req.body
        <span class="hljs-keyword">if</span>(!(email &amp;&amp; password)) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ApiError(<span class="hljs-number">411</span>,<span class="hljs-string">"Either email or password is missing"</span>)

        <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> db.user.findUnique({
            <span class="hljs-attr">where</span> : {
                email
            }
        });

        <span class="hljs-keyword">if</span>(!user?.passwordHash) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ApiError(<span class="hljs-number">411</span>,<span class="hljs-string">"Either account not present or logged in through google"</span>)
        }

        <span class="hljs-keyword">const</span> passwordCheck = <span class="hljs-keyword">await</span> bcrypt.compare(password,user.passwordHash);

        <span class="hljs-keyword">if</span>(!passwordCheck) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ApiError(<span class="hljs-number">406</span>,<span class="hljs-string">"password is not correct"</span>);

        <span class="hljs-keyword">const</span> accessToken = generateAccessToken(user.id);
        <span class="hljs-keyword">const</span> refreshToken = generateRefreshToken(user.id);

        <span class="hljs-keyword">const</span> loggedInUser = <span class="hljs-keyword">await</span> db.user.update({
            <span class="hljs-attr">where</span> : {
                <span class="hljs-attr">id</span> : user.id
            },
            <span class="hljs-attr">data</span>:{
                refreshToken
            },
            <span class="hljs-attr">select</span>:{
                <span class="hljs-attr">id</span>:<span class="hljs-literal">true</span>,
                <span class="hljs-attr">name</span>:<span class="hljs-literal">true</span>,
                <span class="hljs-attr">authType</span>:<span class="hljs-literal">true</span>,
            }
        });



        <span class="hljs-keyword">return</span> res
        .status(<span class="hljs-number">201</span>)
        .cookie(<span class="hljs-string">"accessToken"</span>,accessToken,accessTokenCookieOption)
        .cookie(<span class="hljs-string">"refreshToken"</span>,refreshToken,refreshTokenCookieOption)
        .json({
            <span class="hljs-attr">message</span>:<span class="hljs-string">"user logged in"</span>,
            <span class="hljs-attr">data</span>: loggedInUser
        })
</code></pre>
<p>As we are already discussing about logging in the user lets also look into creating a user account . Similar to Login , a form will be filled in the client browser and data will be sent to the application server via a POST request .</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769246158720/f38b9eb4-a368-40cb-8188-7fa32a616888.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">await</span> axios.post(
        <span class="hljs-string">`<span class="hljs-subst">${BACKEND_URI}</span>/v1/auth/register`</span>,
        {
          <span class="hljs-attr">name</span>: data.name,
          <span class="hljs-attr">email</span>: data.email,
          <span class="hljs-attr">password</span>: data.password,
          <span class="hljs-attr">type</span>:<span class="hljs-string">"PasswordLogin"</span>
        },
        {
          <span class="hljs-attr">headers</span>: {
            <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
          },
        }
      );
</code></pre>
<p>and on the application server side as well most of the things will remain the same . We will first check if the user exists in the db , if not we will hash the password and save the user data along with passwordHash in the db .</p>
<pre><code class="lang-javascript">        <span class="hljs-keyword">const</span> { name,email,password } = req.body ;
        <span class="hljs-keyword">if</span>(!(name &amp;&amp; email &amp;&amp; password)){
           <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ApiError(<span class="hljs-number">411</span>,<span class="hljs-string">"either of name , email &amp; password is missing"</span>)
        } 

        <span class="hljs-keyword">const</span> existingUser = <span class="hljs-keyword">await</span> db.user.findUnique({
            <span class="hljs-attr">where</span>:{
                email,
            }
        })

        <span class="hljs-keyword">if</span>(existingUser) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ApiError(<span class="hljs-number">411</span>,<span class="hljs-string">"User already exists"</span>)

        <span class="hljs-keyword">const</span> hashedPassword = <span class="hljs-keyword">await</span> bcrypt.hash(password,<span class="hljs-number">10</span>);


        <span class="hljs-keyword">const</span> createdUser = <span class="hljs-keyword">await</span> db.user.create({
            <span class="hljs-attr">data</span>:{
               name,
               email,
               <span class="hljs-attr">passwordHash</span>:hashedPassword,
               <span class="hljs-attr">authType</span>:<span class="hljs-string">"PasswordLogin"</span>
            }
        });

        <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">201</span>).json({
            <span class="hljs-attr">message</span>:<span class="hljs-string">"createdSuccessfully"</span>,
            <span class="hljs-attr">data</span>:createdUser
        })
</code></pre>
<p>This is mainly how we deal with Password Based Authentication in general .</p>
<p>Now moving to the main part of the blog handling Authentication with OIDC/OAuth</p>
<h2 id="heading-oidcoauth-authentication">OIDC/OAuth Authentication</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769294770017/67d8a56a-25a0-4220-b51b-1360c02e965d.png" alt class="image--center mx-auto" /></p>
<p>Firstly get the credentials ( namely , client secret and client id ) from the auth provider , for this blog it will be google .</p>
<p>In the frontend have a sign in/sign up with google button , which will basically redirect to authorization server , along with redirect uri and client id as url param .</p>
<pre><code class="lang-javascript">&lt;Button
       variant=<span class="hljs-string">"outline"</span>
       className=<span class="hljs-string">"w-full text-white cursor-pointer"</span>
       disabled={isSubmitting}
       type=<span class="hljs-string">"button"</span>
       onClick={<span class="hljs-function">()=&gt;</span>{
                  <span class="hljs-built_in">window</span>.location.href = <span class="hljs-string">`https://accounts.google.com/o/oauth2/v2/auth
                                          /oauthchooseaccount?
                                          redirect_uri=<span class="hljs-subst">${GOOGLE_REDIRECT_URI}</span>
                                          &amp;prompt=consent
                                          &amp;response_type=code
                                          &amp;client_id=<span class="hljs-subst">${GOOGLE_CLIENT_ID}</span>
                                          &amp;scope=<span class="hljs-subst">${GOOGLE_LOGIN_SCOPE}</span>`</span>
                }}           
       &gt;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769280494472/2b26aee6-4ab3-4a5a-afc9-f7bc0b631835.jpeg" alt class="image--center mx-auto" /></p>
<p>So in the start our user will click the sign in / sign up button and will be redirected to the authorization server which will handle the remaining part of authentication .</p>
<p>which will look some thing like this -</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769284723098/f6754b81-34b4-4910-bf3f-0bbc7872c5e8.png" alt class="image--center mx-auto" /></p>
<p>once the user authenticates with our auth provider they will be guided to consent page .</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769287457569/4ae0bd57-303c-4b37-8b54-456f916416b7.png" alt class="image--center mx-auto" /></p>
<p>Now once user provides the consent , they will be redirected to callback or redirect uri ( with auth code as url param ) , in this case we are keeping a frontend or client side uri as redirect uri .</p>
<p>Then from frontend we will send the auth code to our web server via POST request .</p>
<p>while we get the response from our server , our client will see a loading screen .</p>
<pre><code class="lang-javascript">
<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">OAuthPage</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> navigate = useNavigate();

  <span class="hljs-keyword">const</span> oauthHandler = useCallback(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> searchParams = <span class="hljs-keyword">new</span> URLSearchParams(<span class="hljs-built_in">window</span>.location.search);

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> [key, value] <span class="hljs-keyword">of</span> searchParams) {
        <span class="hljs-keyword">if</span>(key == <span class="hljs-string">"code"</span>){
           <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">await</span> axios.post(<span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-keyword">import</span>.meta.env.VITE_BACKEND_URI}</span>/v1/auth/googleOAuth`</span>,{
                 <span class="hljs-attr">authorizationCode</span> : value
           },{
            <span class="hljs-attr">withCredentials</span>:<span class="hljs-literal">true</span>
           })

           navigate(<span class="hljs-string">"/"</span>);
           } <span class="hljs-keyword">catch</span> (error) {
            <span class="hljs-built_in">console</span>.log(error)
           }

           <span class="hljs-keyword">return</span> ;
        }<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(key==<span class="hljs-string">"error"</span>){
           navigate(<span class="hljs-string">"/auth/google/error"</span>)
        }
    }

  }, [navigate])


  useEffect(<span class="hljs-function">() =&gt;</span> {
    oauthHandler()
  }, [oauthHandler]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Loader</span>/&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>Now as the web server gets the auth code , it does the following -</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> oAuthHandler = <span class="hljs-keyword">async</span> (req:Request,<span class="hljs-attr">res</span>:Response) =&gt; {

    <span class="hljs-keyword">const</span> { authorizationCode } = req.body;

    <span class="hljs-keyword">try</span> {

    <span class="hljs-keyword">if</span>(!authorizationCode) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ApiError(<span class="hljs-number">401</span>,<span class="hljs-string">"authorization code absent"</span>) ;
    <span class="hljs-keyword">const</span> googleAuthorizationServer = <span class="hljs-string">"https://oauth2.googleapis.com/token"</span>;
    <span class="hljs-keyword">const</span> httpMethod = <span class="hljs-string">"POST"</span>;

      <span class="hljs-keyword">const</span> tokenRes = <span class="hljs-keyword">await</span> fetch(googleAuthorizationServer,{
        <span class="hljs-attr">method</span>:httpMethod,
        <span class="hljs-attr">headers</span>:{<span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/x-www-form-urlencoded"</span>},
        <span class="hljs-attr">body</span>:<span class="hljs-keyword">new</span> URLSearchParams({
            <span class="hljs-attr">code</span>:authorizationCode,
            <span class="hljs-attr">client_id</span>:process.env.GOOGLE_CLIENT_ID!,
            client_secret:process.env.GOOGLE_CLIENT_SECRET!,
            redirect_uri:<span class="hljs-string">`<span class="hljs-subst">${process.env.ORIGIN}</span>/auth/google/callback`</span>,
            <span class="hljs-attr">grant_type</span>: <span class="hljs-string">"authorization_code"</span>,
        })
      }) <span class="hljs-comment">// 1</span>

      <span class="hljs-keyword">const</span> token = <span class="hljs-keyword">await</span> tokenRes.json();

      <span class="hljs-keyword">const</span> googleAccessToken = token.access_token;

      <span class="hljs-comment">// fetch user info </span>

      <span class="hljs-keyword">const</span> userProfile = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"https://www.googleapis.com/oauth2/v2/userinfo"</span>,{
         <span class="hljs-attr">headers</span>:{
            <span class="hljs-attr">Authorization</span>:<span class="hljs-string">`Bearer <span class="hljs-subst">${googleAccessToken}</span>`</span>
         }
      }) <span class="hljs-comment">// 2</span>

      <span class="hljs-keyword">const</span> userInfo:userInfoType = <span class="hljs-keyword">await</span> userProfile.json();

      <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> db.user.upsert({
           <span class="hljs-attr">where</span>:{
              <span class="hljs-attr">email</span>:userInfo.email,
           },
           <span class="hljs-attr">update</span>:{
              <span class="hljs-attr">googleId</span>:userInfo.id,
              <span class="hljs-attr">authType</span>: userInfo.authType === <span class="hljs-string">"PasswordLogin"</span> ? <span class="hljs-string">"Both"</span> : <span class="hljs-string">"GoogleLogin"</span> ,
           },
           <span class="hljs-attr">create</span>:{
              <span class="hljs-attr">name</span>:userInfo.name,
              <span class="hljs-attr">email</span>:userInfo.email,
              <span class="hljs-attr">authType</span>:<span class="hljs-string">"GoogleLogin"</span>,
              <span class="hljs-attr">googleId</span>:userInfo.id,
           }
      }); <span class="hljs-comment">// 3</span>


      <span class="hljs-keyword">const</span> accessToken = generateAccessToken(user.id);
      <span class="hljs-keyword">const</span> refreshToken = generateRefreshToken(user.id);

      <span class="hljs-keyword">const</span> loggedInUser = <span class="hljs-keyword">await</span> db.user.update({
            <span class="hljs-attr">where</span> : {
                <span class="hljs-attr">id</span> : user.id
            },
            <span class="hljs-attr">data</span>:{
                refreshToken
            }
        });

       <span class="hljs-keyword">return</span> res
        .status(<span class="hljs-number">201</span>)
        .cookie(<span class="hljs-string">"accessToken"</span>,accessToken,accessTokenCookieOption)
        .cookie(<span class="hljs-string">"refreshToken"</span>,refreshToken,refreshTokenCookieOption)
        .json({
            <span class="hljs-attr">message</span>:<span class="hljs-string">"user logged in"</span>,
            <span class="hljs-attr">data</span>: loggedInUser
        }) <span class="hljs-comment">// 4</span>



    } <span class="hljs-keyword">catch</span> (error:any) {
        <span class="hljs-keyword">const</span> err : ApiErrorTypes = error <span class="hljs-keyword">as</span> ApiErrorTypes ;

        <span class="hljs-keyword">const</span> statusCode = <span class="hljs-keyword">typeof</span> err.status === <span class="hljs-string">"number"</span> ? err.status : <span class="hljs-number">501</span> ; 
              <span class="hljs-keyword">return</span> res
              .status(statusCode)
              .json({
                  <span class="hljs-attr">message</span>:err.message,
                  <span class="hljs-attr">status</span>:<span class="hljs-string">"fail"</span>
              })  
    }
}
</code></pre>
<p>So our web server will do basically 4 things -</p>
<ol>
<li><p>Get the <strong>access token</strong> from the auth provider by sending a POST request to the authorization server with the auth code, other credentials, and the client secret, which is kept only on the web server to prevent exposure on the client side.</p>
</li>
<li><p><strong>Fetch the user data</strong> from the resource server using the access token( provided by the authorization server ) .</p>
</li>
<li><p><strong>Upsert the user data</strong> in the database. If the user previously logged in using the <code>PasswordLogin</code> method, mark the user’s <code>authType</code> as <code>Both</code>; otherwise, set it to <code>GoogleLogin</code>.</p>
</li>
<li><p>Generate the <strong>access and refresh tokens</strong>, store the refresh token in the database, and set both tokens as cookies in the browser.</p>
</li>
</ol>
<p>Once the cookies are set in the browser, the user is considered logged in. This is essentially how the OAuth/OIDC protocol is handled for authentication.</p>
<hr />
<p>Now as promised earlier , we will discuss two other functionality -</p>
<ol>
<li><p>Enabling users who signed up with <code>GoogleLogin</code> to also add password-based authentication to their account.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769331572993/535263c2-bdaa-4054-ace9-fbaf7e9e649d.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">const</span> openIdPasswordAdditionAndChange = <span class="hljs-keyword">async</span> (req:Request,<span class="hljs-attr">res</span>:Response) =&gt; {
      <span class="hljs-keyword">try</span> {

         <span class="hljs-comment">// switch to email - password or just change password </span>

         <span class="hljs-keyword">const</span> { newPassword , changeMode , id } = req.body ;

         <span class="hljs-keyword">if</span>(!newPassword) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ApiError(<span class="hljs-number">400</span>,<span class="hljs-string">"password cannot be empty"</span>);

         <span class="hljs-keyword">const</span> passwordHash = <span class="hljs-keyword">await</span> bcrypt.hash(newPassword,<span class="hljs-number">10</span>);

         <span class="hljs-keyword">let</span> userData ;
         <span class="hljs-keyword">if</span>(!changeMode){
              userData = <span class="hljs-keyword">await</span> db.user.update({
                 <span class="hljs-attr">where</span>:{id},
                 <span class="hljs-attr">data</span>:{
                     passwordHash,
                     <span class="hljs-attr">googleId</span>:<span class="hljs-literal">null</span>,
                     <span class="hljs-attr">authType</span>:<span class="hljs-string">"PasswordLogin"</span>
                 }
              })
         }<span class="hljs-keyword">else</span>{
             userData = <span class="hljs-keyword">await</span> db.user.update({
                 <span class="hljs-attr">where</span>:{id},
                 <span class="hljs-attr">data</span>:{
                     passwordHash,
                     <span class="hljs-attr">authType</span>:<span class="hljs-string">"Both"</span>
                 }
             })
         }

         <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({
             <span class="hljs-attr">message</span>:<span class="hljs-string">"password change"</span>,
             <span class="hljs-attr">data</span>: userData

         })

      } <span class="hljs-keyword">catch</span> (error:any) {
         <span class="hljs-keyword">const</span> err : ApiErrorTypes = error <span class="hljs-keyword">as</span> ApiErrorTypes ;
         <span class="hljs-keyword">const</span> statusCode = <span class="hljs-keyword">typeof</span> err.status === <span class="hljs-string">"number"</span> ? err.status : <span class="hljs-number">501</span> ; 
               <span class="hljs-keyword">return</span> res
               .status(statusCode)
               .json({
                   <span class="hljs-attr">message</span>:err.message,
                   <span class="hljs-attr">status</span>:<span class="hljs-string">"fail"</span>
               }) 
     }
 }
</code></pre>
<p> This endpoint allows users who initially authenticated via Google Login to either add password-based authentication or switch entirely to email–password login.</p>
<p> If <code>changeMode</code> is enabled, the user retains Google Login and the <code>authType</code> is updated to <code>Both</code>. Otherwise, Google Login is removed and the account is converted to <code>PasswordLogin</code>.</p>
</li>
</ol>
<p>Adding an admin authorization layer that allows viewing all users, and deleting or logging out specific user accounts.</p>
<p>For this , we will first have an admin login form -</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769336020136/a82ebc48-fc4f-498f-89cb-e2a39e4468a9.png" alt class="image--center mx-auto" /></p>
<p>and data is sent to the web server for authentication via POST request which will authorize the user , and convert them from user to admin role .</p>
<pre><code class="lang-javascript">        <span class="hljs-keyword">const</span> { username , password } = req.body;

    <span class="hljs-keyword">if</span>( username !== ADMIN_SECRET_USERNAME || password !== ADMIN_SECRET_PASSWORD)
         <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ApiError(<span class="hljs-number">401</span>,<span class="hljs-string">"incorrect admin credentials"</span>)

    <span class="hljs-keyword">if</span>(!process.env.ADMIN_SECRET_KEY) 
         <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ApiError(<span class="hljs-number">501</span>,<span class="hljs-string">"Admin secret key absent in server"</span>)

    <span class="hljs-keyword">const</span> encryptedKey = <span class="hljs-keyword">await</span> bcrypt.hash(process.env.ADMIN_SECRET_KEY,<span class="hljs-number">10</span>);

    <span class="hljs-keyword">const</span> adminToken = <span class="hljs-keyword">await</span> generateAdminToken(encryptedKey)

    <span class="hljs-keyword">return</span> res
    .status(<span class="hljs-number">201</span>)
    .cookie(<span class="hljs-string">"adminToken"</span>,adminToken,adminCookieOptions)
    .json({
        <span class="hljs-attr">message</span> : <span class="hljs-string">"admin privileges added to the account"</span>,
        <span class="hljs-attr">success</span> : <span class="hljs-literal">true</span>
    })
</code></pre>
<p>Once the user logs in as an admin they will see a dashboard with all the users , their authentication methods and other user data , and will have functionality of deleting or logging out their respective accounts .</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769338501450/a776c593-f9b6-4134-a26f-cbdb620a6c8b.png" alt class="image--center mx-auto" /></p>
<p>Here are the code for all of these functions -</p>
<p>For getting all the users and their data</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">const</span> userData = <span class="hljs-keyword">await</span> db.user.findMany({
              <span class="hljs-attr">select</span>: {
                    <span class="hljs-attr">id</span>: <span class="hljs-literal">true</span>,
                    <span class="hljs-attr">email</span>: <span class="hljs-literal">true</span>,
                    <span class="hljs-attr">name</span>: <span class="hljs-literal">true</span>,
                    <span class="hljs-attr">createdAt</span>: <span class="hljs-literal">true</span>,
                    <span class="hljs-attr">updatedAt</span>: <span class="hljs-literal">true</span>,
                    <span class="hljs-attr">authType</span>: <span class="hljs-literal">true</span>,
                    <span class="hljs-attr">refreshToken</span>: <span class="hljs-literal">true</span>,
                }
        });


        <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>)
         .json({
        <span class="hljs-attr">users</span> : userData,
        <span class="hljs-attr">success</span> : <span class="hljs-literal">true</span> 
        })
</code></pre>
<p>Logging out specific user , we will send the id of user to logged out and then set their refresh token to null .</p>
<pre><code class="lang-javascript">        <span class="hljs-keyword">const</span> { id } = req.body ;

        <span class="hljs-keyword">await</span> db.user.update({
            <span class="hljs-attr">where</span> : { id },
            <span class="hljs-attr">data</span> : {
                <span class="hljs-attr">refreshToken</span>:<span class="hljs-literal">null</span>
            }
        });

        <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">201</span>).json({
            <span class="hljs-attr">message</span>:<span class="hljs-string">"user successfully logged out"</span>,
            <span class="hljs-attr">status</span>:<span class="hljs-string">"success"</span>
        })
</code></pre>
<p>and at last , Delete a specific user account , here we will send the user id via params from the frontend and delete the user completely from the db .</p>
<pre><code class="lang-javascript">       <span class="hljs-keyword">const</span> userId = req.params.id <span class="hljs-keyword">as</span> string ;

        <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> db.user.delete({
            <span class="hljs-attr">where</span>:{
                <span class="hljs-attr">id</span>:userId
            }
        });


        <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">201</span>).json({
            <span class="hljs-attr">message</span>:<span class="hljs-string">"user deleted successfully"</span>,
            <span class="hljs-attr">user</span>:user,
            <span class="hljs-attr">status</span>:<span class="hljs-string">"success"</span>
        })
</code></pre>
<hr />
<p>That’s it for this blog.</p>
<p>If you want to see everything in action, you can check out the live POC here: <a target="_blank" href="https://oauth.fe.dhatrish.in">POC Link</a></p>
<p>You can also find the complete code on GitHub here : <a target="_blank" href="https://github.com/dhatrishdixit/OauthDemo">Repo Link</a></p>
<p>As I mentioned at the end of my last blog, I’m starting a series around authentication and everything that comes with it. This article covered implementing OAuth 2.0 and OpenID Connect end to end, along with real-world considerations like token handling, account linking, and admin-level authorization.</p>
<p>Also, there was a bit of a hiatus of around 2–3 months, which was longer than I had initially planned, mainly due to office work and other commitments. I’m hoping to keep things more consistent going forward.</p>
<p>In the upcoming posts, I’ll dive deeper into topics like OTP-based authentication, magic links, email verification, password recovery, and other common auth flows you’d see in production systems.</p>
<p>Meanwhile, if you want to strengthen your understanding of OAuth 2.0 and OpenID Connect, this video from Okta’s YouTube channel is a great resource: <a target="_blank" href="https://www.youtube.com/watch?v=996OiexHze0">Okta Vid Link</a></p>
<p>Thanks for reading, and see you in the next one .</p>
]]></content:encoded></item><item><title><![CDATA[OpenID Connect and OAuth 2.0: Streamlining Authentication/Authorization for Web Applications]]></title><description><![CDATA[In this article, we will explore how OpenID Connect, combined with OAuth functionality, can streamline the authentication as well as authorization process. By using these technologies, applications with a web server can achieve a secure and efficient...]]></description><link>https://journal.dhatrish.in/openid-connect-and-oauth-20-streamlining-authenticationauthorization-for-web-applications</link><guid isPermaLink="true">https://journal.dhatrish.in/openid-connect-and-oauth-20-streamlining-authenticationauthorization-for-web-applications</guid><category><![CDATA[oauth]]></category><category><![CDATA[OAuth2]]></category><category><![CDATA[oauth2.0]]></category><category><![CDATA[OpenID Connect]]></category><category><![CDATA[Security]]></category><category><![CDATA[authentication]]></category><category><![CDATA[authorization]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[dhatrish]]></dc:creator><pubDate>Thu, 23 Oct 2025 21:53:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/tg7xChYyE08/upload/2b9b10eb40fa552f95606b205b9eb072.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article, we will explore how OpenID Connect, combined with OAuth functionality, can streamline the authentication as well as authorization process. By using these technologies, applications with a web server can achieve a secure and efficient mode of authorization &amp; authentication. Before diving into the details, it's essential to understand what OpenID Connect and OAuth 2.0 are, and how they differ from each other.</p>
<h2 id="heading-oauth-20-oidc">OAuth 2.0 / OIDC</h2>
<p>Often misinterpreted as one another , OpenID Connect (OIDC) extends OAuth 2.0 by adding an identity layer, allowing applications to verify user identity and obtain basic profile information . But before diving straight into the OpenID rabbit-hole we must first understand what OAuth is , why it was needed , what problems it solved and how OpenID developed on top of it</p>
<h3 id="heading-oauth">OAuth</h3>
<p>Understanding OAuth would be easier if we understand the problem it solves , Let’s take for example -</p>
<blockquote>
<p>Imagine there’s a wallpaper site you love, and you want to share it with your friends over email. Before the advent of OAuth, you would have had to share your email ID and password with that site so it could send emails on your behalf. Doesn’t that sound like a terribly unsafe idea?</p>
<p>To solve this problem, the creator of the wallpaper site can integrate an OAuth provider (like Google) into their app. When you choose to share something, you’ll first be redirected to a <strong>login screen</strong> from the OAuth provider (if you aren’t already signed in). This step helps the provider confirm that it’s really you.</p>
<p>After that, you’ll see a <strong>consent screen</strong> asking for your approval. Once you grant permission, the site receives an <strong>access token</strong> from the <strong>authorization server</strong> (which also may or may not be associated with the OAuth provider).</p>
<p>Using this access token, the wallpaper site can then <strong>access your relevant information from the resource server</strong> ( which also may or may not be associated with the OAuth provider). This allows the site to retrieve only the specific data it needs—like your contact list—to send invitation emails to your friends, <strong>without ever seeing your password or requesting unnecessary permissions.</strong></p>
</blockquote>
<p>One of the companies that had to rely on this “terribly unsafe idea” in the pre-OAuth era was Yelp, as shown in the screenshot below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1760385384188/f7fd9b49-47ed-4d3a-b876-455d31c0b8f2.png" alt class="image--center mx-auto" /></p>
<p>To solve the security issues that Yelp (and many other apps) faced before OAuth, the protocol defines <strong>authorization grants</strong> (also called <em>flows</em>) — these are the different methods by which a client obtains permission (a credential) to access protected resources.</p>
<p>Each grant type is optimized for certain use-cases. Here are the most common ones:</p>
<ul>
<li><p><strong>Authorization Code</strong> — used by web applications with a backend (this is the flow typically used by applications like Yelp) .</p>
</li>
<li><p><strong>PKCE —</strong> an extension of Authorization Code flow, more secure for public clients (e.g. mobile or SPAs) .</p>
</li>
<li><p><strong>Client Credentials</strong> — used in machine-to-machine or server-to-server scenarios (no user interaction) .</p>
</li>
<li><p><strong>Device Code</strong> — used by devices with limited input (e.g. smart TVs) where the user enters a code via another device .</p>
</li>
<li><p><strong>Refresh Token</strong> — allows renewing an expired access token without prompting the user again.</p>
</li>
</ul>
<p>In our discussion, we’ll focus on the <strong>Authorization Code grant ,</strong> since that’s the most relevant for web apps like Yelp. The other grant types will be covered in later articles.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1760358786886/c925e628-6c93-485c-a237-06fbc1e79a65.jpeg" alt class="image--center mx-auto" /></p>
<p>To understand how this code flow will work first we have to understand all the Stakeholders in this workflow and the role they play ,</p>
<h3 id="heading-client-the-third-party-application"><strong>Client ( The Third-Party Application )</strong></h3>
<p>The client is the application that is attempting to get access to the user's account. It needs to get permission from the user before it can do so.</p>
<h3 id="heading-the-resource-server"><strong>The Resource Server</strong></h3>
<p>The resource server is the API server used to access the user's information.</p>
<h3 id="heading-the-authorization-server"><strong>The Authorization Server</strong></h3>
<p>This is the server that presents the interface where the user approves or denies the request. In smaller implementations, this may be the same server as the API server, but larger scale deployments will often build this as a separate component.</p>
<h3 id="heading-resource-owner-the-user"><strong>Resource Owner ( The User )</strong></h3>
<p>The resource owner is the person who is giving access to some portion of their account.</p>
<hr />
<p>When we register our app in any OAuth provider ( like google ) , we are given some credentials ( Client ID and optionally Client Secret ) whereas we register some credentials ( Redirect URI ) from our end in the OAuth provider’s console , lets get into them one by one -</p>
<h3 id="heading-redirect-uris"><strong>Redirect URIs</strong></h3>
<p>The service will only redirect users to a registered URI ( with authorization code ), which helps prevent <em>authorization code interception</em> or <em>phishing-style redirection attacks</em> . Any HTTP redirect URIs must be served via HTTPS. This helps prevent tokens from being intercepted during the authorization process.</p>
<h3 id="heading-client-id-and-secret">Client ID and Secret</h3>
<ol>
<li><p>The <strong>Client ID</strong> uniquely identifies your app and is included in most requests to the authorization server.</p>
</li>
<li><p>The <strong>Client Secret</strong> is a confidential key used by your app to securely exchange the authorization code for an access token. However, in scenarios where the deployed app cannot keep the secret confidential — such as in Single Page Applications (SPAs) or native mobile apps — the secret should not be used (and ideally, it should not even be issued). In such cases, OAuth providers generally adopt alternate protections (for example, using PKCE, which removes the need to send a client secret) to maintain security.</p>
</li>
</ol>
<p>Now as we have discussed all the terms and have developed the basic understanding of them lets get deep into the code flow -</p>
<ul>
<li><p>Create a login link that takes the user to the authorization server’s <strong>login page (authorization endpoint)</strong> to sign in and approve access.</p>
<pre><code class="lang-http">  https://authorization-server.com/auth?response_type=code&amp;client_id=CLIENT_ID&amp;
  redirect_uri=REDIRECT_URI&amp;scope=contacts&amp;state=foo
</code></pre>
<p>  here lets break down each of the query params -</p>
<ol>
<li><p><strong><em>response_type</em></strong> = It simply indicates <strong>what kind of response</strong> the <strong>authorization server</strong> should return to the client, depending on which <em>grant type</em> (flow) is being used .</p>
</li>
<li><p><strong><em>client_id</em></strong> = client_id as discussed earlier this is the id we get from OAuth provider and is used as identification of our app for the authorization server .</p>
</li>
<li><p><strong><em>redirect_uri</em></strong> = As discussed earlier , specifies the URL to which the user will be redirected after the authorization process is complete. This value must <strong>exactly match</strong> one of the redirect URIs registered with the OAuth provider; otherwise, the authorization server will <strong>return an error response</strong>.</p>
</li>
<li><p><strong>scope</strong> = The permissions your app is requesting (e.g., access to profile, contacts, or photos) , it can be one or many clubbed together .</p>
</li>
<li><p><strong>state</strong> = A random string generated by your application, which is used to verify integrity of response and prevent CSRF attacks , we will see its use later .</p>
</li>
</ol>
</li>
<li><p>After logging in through whatever method the authorization server supports or wants user to log in with , user sees authorization prompt which might look something like this</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761129083454/0410869d-c5e8-40c5-982c-1a882a46d910.png" alt class="image--center mx-auto" /></p>
<p>  User has two options , deny or allow</p>
<p>  first let’s cover the options which doesn’t require much explaining to do deny option -</p>
<p>  User will be redirected to</p>
<pre><code class="lang-http">  https://yelp.com/cb?error=access_denied
</code></pre>
<p>  Here , we have taken , yelp.com/cb as a sample Redirect URI of yelp for this example ,</p>
<p>  In the case of deny from the user authorization server will redirect user with some kind of access denied message embedded in the params , which can be dealt by application frontend as they like .</p>
<p>  When they click on allow , authorization server redirects to the provided Redirect URI , with authorization code and state in the params</p>
<pre><code class="lang-http">  https://yelp.com/cb?code=AUTHORIZATION_CODE&amp;state=foo
</code></pre>
<p>  Here</p>
<ol>
<li><p>code = The authorization server response with an Authorization Code to the client .</p>
</li>
<li><p>state = The authorization server returns with the state</p>
</li>
</ol>
</li>
</ul>
<p>    You should first compare this state value to ensure it matches the one you started with. You can typically store the state value in a cookie or session, and compare it when the user comes back. This helps ensure your redirection endpoint isn't able to be tricked into attempting to exchange arbitrary authorization codes .</p>
<ul>
<li><p>Now as you have checked the state and confirmed that you are getting getting authorization code from the original authorization server from which it is intended to , there is another dilemma , of what to do with the authorization code , till now everything we had done that is every call between our application and authorization server has been done in the front channel ( i.e. through our frontend ) but now as we are dealing with confidential information ( Client Secret and access token ) we cannot do the exchange for access token in the front channel as users browser are at risk with multiple fronts ( for example - some malicious code in any of the browser extension , users tendency to click any of the malicious website ) , so in general we don’t trust user with the sensitive information .</p>
<p>  So we will send the authorization code to our web-server via the frontend , and our server will perform the exchange and then store the access token or the information extracted from the token in our application database based ( or can even perform an action right away like in this case of sending mails to all the contacts and not storing contacts info in database if not needed ) on our needs .</p>
<p>  For this exchange we will trigger a POST request on authorization server’s “/token” endpoint</p>
<p>  which will look something like this</p>
<pre><code class="lang-http">  POST /token HTTP/1.1
  Host: api.authorization-server.com
  Content-Type: application/x-www-form-urlencoded

<span class="ini">  <span class="hljs-attr">grant_type</span>=authorization_code&amp;
  <span class="hljs-attr">code</span>=AUTHORIZATION_CODE&amp;
  <span class="hljs-attr">redirect_uri</span>=REDIRECT_URI&amp;
  <span class="hljs-attr">client_id</span>=CLIENT_ID&amp;
  <span class="hljs-attr">client_secret</span>=CLIENT_SECRET</span>
</code></pre>
<ol>
<li><p><strong>grant_type=</strong> The grant type for this flow is authorization_code .</p>
</li>
<li><p><strong>code=</strong> This is the code you received earlier in the query string .</p>
</li>
<li><p><strong>redirect_uri=</strong> Must be identical to the redirect URI provided in the original call to the authorization server .</p>
</li>
<li><p><strong>client_id=</strong> The client ID you received when you first created the application .</p>
</li>
<li><p><strong>client_secret=</strong> Since this request is made from server-side code, the secret is included .</p>
<p> Authorization server will respond with</p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"access_token"</span>:<span class="hljs-string">"access token"</span>,
   <span class="hljs-attr">"expires_in"</span>:<span class="hljs-number">3600</span>,
 }
</code></pre>
<p> an access token , token expiry ( which will tell us when access token will expire ) and in some cases a refresh token ( can be used to get a new access token generated in case the old one expires ) as well , we will keep these confidential tokens in our server side only and wont expose them to the client side . in case of invalid request we will get an error message as response .</p>
</li>
</ol>
</li>
<li><p>So we will use the access token by sending it to the resource server to get the authorization of doing something on users behalf or getting some of the data regarding user from the resource server .</p>
<p>  Request to resource server will look something like</p>
<pre><code class="lang-http">  GET /userinfo HTTP/1.1
  Host: api.resource-server.com
  Authorization: Bearer ACCESS_TOKEN
</code></pre>
<p>  So , basically this is almost everything which is to be covered under OAuth in authorization code grant type .</p>
</li>
</ul>
<h3 id="heading-open-id-connect">Open ID Connect</h3>
<p>As discussed earlier OIDC was built on top of OAuth 2.0 Standard , Primary motive behind introduction of OIDC was to reduce the malpractice that was to use OAuth for authentication purposes , Initially most of the companies used OAuth as work around to implement authentication in their application .</p>
<p>So as a way to streamline the process of authentication Open ID was introduced , there isn’t much difference in code flow of OIDC from OAuth in case of Authorization code grant type .</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761222648632/a52010a7-4c53-4774-8983-63cdeaa84a26.png" alt class="image--center mx-auto" /></p>
<p>The main difference is ,</p>
<ol>
<li><p>There is no other resource server here authorization server is our resource server.</p>
</li>
<li><p>An extra openid scope is added to our initial request to authorization server .</p>
</li>
<li><p>When we do code — token exchange for access token we get an ID token , which is JWT token , which can be used to extract basic info about the user on the go and as it is JWT it is temper proof .</p>
<p> The ID Token when decoded comes out in a format somewhat similar to this</p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"iss"</span>: <span class="hljs-string">"https://accounts.google.com"</span>,
   <span class="hljs-attr">"sub"</span>: <span class="hljs-string">"10769150350006150715113082367"</span>,
   <span class="hljs-attr">"aud"</span>: <span class="hljs-string">"CLIENT_ID"</span>,
   <span class="hljs-attr">"exp"</span>: <span class="hljs-number">1311281970</span>,
   <span class="hljs-attr">"iat"</span>: <span class="hljs-number">1311280970</span>,
   <span class="hljs-attr">"email"</span>: <span class="hljs-string">"user@example.com"</span>,
   <span class="hljs-attr">"name"</span>: <span class="hljs-string">"John Doe"</span>
 }
</code></pre>
<p> with some of the important keys here being<br /> - <code>sub</code> (subject) - unique user identifier</p>
<p> - <code>iss</code> (issuer) - who issued the token</p>
<p> - <code>aud</code> (audience) - who the token is for i.e. the client app</p>
<p> - <code>exp</code> (expiration) - when it expires</p>
</li>
<li><p>And if we want to get some more information about the user we can use the access token to get more info from the authorization server.</p>
</li>
</ol>
<p>Apart from this there isn’t much that separate both , basically OpenID was introduced as a way to differentiate the task of authentication and authorization .</p>
<hr />
<p>I am starting a series on authentication and everything in between , next article will be about implementing OpenId Connect for authentication along with two token authentication , and further will go in details about using implementing OTP and magic links for authentication , email verification and password recovery , I have many things planned for the future of this series . Meanwhile if you want to learn more about OAuth and OpenID Connect , watch this video from okta’s youtube channel</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=996OiexHze0">https://www.youtube.com/watch?v=996OiexHze0</a></div>
<p> </p>
<p>and special thanks to <a target="_blank" href="https://aaronparecki.com/">Aaron Parecki</a> and his <a target="_blank" href="https://aaronparecki.com/oauth-2-simplified/">article</a> on OAuth and OIDC do check it out as well ,</p>
<p>&amp; if you want learn more about the code flow of OAuth and OIDC try <a target="_blank" href="https://developers.google.com/oauthplayground/">Google's OAuth Playground</a> &amp; <a target="_blank" href="https://www.oauth.com/playground/">another OAuth playground directly from OAuth 2.0</a></p>
]]></content:encoded></item><item><title><![CDATA[Authentication using Refresh Token and Access Token]]></title><description><![CDATA[Imagine stepping into the digital world as easily and securely as you enter your home. This is what token-based authentication in web development offers. In this article, we'll demystify how websites keep your digital identity safe and seamless.
The ...]]></description><link>https://journal.dhatrish.in/authentication-using-refresh-token-and-access-token</link><guid isPermaLink="true">https://journal.dhatrish.in/authentication-using-refresh-token-and-access-token</guid><category><![CDATA[Web Security]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[cookies]]></category><category><![CDATA[tokens]]></category><category><![CDATA[authentication]]></category><dc:creator><![CDATA[dhatrish]]></dc:creator><pubDate>Thu, 18 Jan 2024 13:31:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705583993442/2c69c6a5-20cd-4762-b355-d877e43996b6.avif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine stepping into the digital world as easily and securely as you enter your home. This is what token-based authentication in web development offers. In this article, we'll demystify how websites keep your digital identity safe and seamless.</p>
<h3 id="heading-the-digital-handshake-cookies-and-tokens"><strong>The Digital Handshake: Cookies and Tokens</strong></h3>
<p>When you log into a website, it's like being handed a digital handshake. This handshake is a set of cookies containing two special keys: the access token and the refresh token.</p>
<ul>
<li><p><strong>Access Token</strong>: Think of it as a temporary key to a hotel room - it grants you access but only for a short period.</p>
</li>
<li><p><strong>Refresh Token</strong>: This is akin to a permanent key stored safely. It's used to generate new access tokens.</p>
</li>
</ul>
<h3 id="heading-the-hotel-analogy"><strong>The Hotel Analogy</strong></h3>
<p>Let's use a hotel analogy to understand this better. You're a guest at a hotel. You can't have a permanent room key for security reasons(like it getting stolen). Instead, you get a temporary key (access token) and a permanent key (refresh token). The temporary key is for daily use, and when it expires or gets lost, you use the permanent key at a special desk (the server) to get a new temporary key.</p>
<h3 id="heading-why-two-keys"><strong>Why Two Keys?</strong></h3>
<p>You might wonder why we need both keys. The access token, being short-lived, minimizes security risks. The refresh token, stored securely, ensures that you don't have to repeatedly log in, balancing security with user convenience.</p>
<h3 id="heading-in-practice"><strong>In Practice</strong></h3>
<p>Here’s a basic JavaScript example of how token-based authentication might look:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Example JavaScript code for token-based authentication</span>
<span class="hljs-comment">// generate refresh and access token </span>
<span class="hljs-comment">// here we will generate both refresh tokens and access token </span>
<span class="hljs-comment">// and store them in cookie</span>
<span class="hljs-keyword">const</span> generateTokens = <span class="hljs-keyword">async</span> (userId){
    <span class="hljs-comment">// write function to generate </span>
    <span class="hljs-comment">// can be used to generate both accessTokens and refreshTokens</span>
    <span class="hljs-comment">// or only access Token depending upon the use case</span>
}
<span class="hljs-keyword">const</span> options = {
    <span class="hljs-attr">httpOnly</span>:<span class="hljs-literal">true</span>,
    <span class="hljs-attr">secure</span>:<span class="hljs-literal">true</span>
<span class="hljs-comment">// can add expire based on the use case just for simplicity </span>
<span class="hljs-comment">// i have used same options for both </span>
}
<span class="hljs-keyword">const</span> loginHandler = <span class="hljs-keyword">async</span> (req,res){
    <span class="hljs-comment">// write login logic</span>
    <span class="hljs-keyword">const</span> tokens = <span class="hljs-keyword">await</span> generateTokens(userId);
    <span class="hljs-comment">// tokens = {accessToken:accessTokenValue,refreshToken:refreshTokenValue}</span>
    <span class="hljs-keyword">return</span> res
    .cookie(<span class="hljs-string">"accessToken"</span>,accessToken,options)
    .cookie(<span class="hljs-string">"RefreshToken"</span>,refreshToken,options)

}
<span class="hljs-keyword">const</span> logoutHandler = <span class="hljs-keyword">async</span> (req,res){
   <span class="hljs-comment">// varifies user by taking accessToken from the cookie</span>
   <span class="hljs-comment">// clears cookies</span>
   <span class="hljs-keyword">return</span> res
   .clearCookie(<span class="hljs-string">'accessToken'</span>,options)
   .clearCookie(<span class="hljs-string">'refreshToken'</span>,options)
}

<span class="hljs-keyword">const</span> refreshAccessToken = <span class="hljs-keyword">async</span> (res,res){
   <span class="hljs-keyword">const</span> incomingRefreshToken = req.cookies?.refreshToken || req.body.refreshToken
   <span class="hljs-keyword">const</span> decodedToken = jwt.verify(incomingRefreshToken,jwtSecret);
   <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> User.findById(decodedToken?.id).select(<span class="hljs-string">"-password"</span>);
   <span class="hljs-comment">// at each step check for errors </span>
   <span class="hljs-keyword">if</span>(incomingRefreshToken !== decodedToken?.refreshToken){
   <span class="hljs-comment">// throw error</span>
}
  <span class="hljs-comment">// after all the checks are done </span>
  <span class="hljs-keyword">const</span> {newRefreshToken,accessToken} = <span class="hljs-keyword">await</span> generateTokens(user._id);
  <span class="hljs-keyword">return</span> res
   .clearCookie(<span class="hljs-string">'accessToken'</span>,options)
   .clearCookie(<span class="hljs-string">'refreshToken'</span>,options)
}
<span class="hljs-comment">// this function generates a new access and refresh token everytime you hit a endpoint</span>
</code></pre>
<h3 id="heading-conclusion"><strong>Conclusion</strong></h3>
<p>Token-based authentication is like a well-oiled lock and key mechanism, keeping your digital presence secure yet accessible. It's an essential part of modern web development.</p>
<h3 id="heading-learn-more"><strong>Learn More</strong></h3>
<p>Interested in diving deeper into web development? Check out codedamn for comprehensive courses or follow <a class="user-mention" href="https://hashnode.com/@hiteshchoudhary">Hitesh Choudhary</a> on YouTube for insightful tutorials.</p>
]]></content:encoded></item></channel></rss>