<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: s2ahil</title>
    <description>The latest articles on DEV Community by s2ahil (@s2ahil).</description>
    <link>https://dev.to/s2ahil</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1096123%2Fb5482874-2bb1-4b32-b93b-873b2951989e.jpeg</url>
      <title>DEV Community: s2ahil</title>
      <link>https://dev.to/s2ahil</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/s2ahil"/>
    <language>en</language>
    <item>
      <title>Hanko auth in react and Express.js .</title>
      <dc:creator>s2ahil</dc:creator>
      <pubDate>Mon, 30 Oct 2023 12:13:03 +0000</pubDate>
      <link>https://dev.to/s2ahil/hanko-auth-in-react-and-expressjs--37b</link>
      <guid>https://dev.to/s2ahil/hanko-auth-in-react-and-expressjs--37b</guid>
      <description>&lt;p&gt;Here you  can see how to integrate auth using &lt;strong&gt;hanko&lt;/strong&gt; .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hanko&lt;/strong&gt; : Hanko is a lightweight, open source user authentication solution to quickly setup in your project ( trust me i tried it is very easy to setup. )&lt;/p&gt;

&lt;p&gt;In this post, i will show how I implemented authentication using hanko in a &lt;strong&gt;react frontend&lt;/strong&gt; and &lt;strong&gt;express backend&lt;/strong&gt; .&lt;/p&gt;

&lt;p&gt;I made a Text summarizer ai .&lt;/p&gt;

&lt;p&gt;To see my full code you can go to my github :&lt;br&gt;
&lt;strong&gt;frontend&lt;/strong&gt;:"&lt;a href="https://github.com/s2ahil/Hanko-auth-frontend-"&gt;https://github.com/s2ahil/Hanko-auth-frontend-&lt;/a&gt;"&lt;br&gt;
&lt;strong&gt;backend&lt;/strong&gt; :" &lt;a href="https://github.com/s2ahil/Hanko-auth-backend"&gt;https://github.com/s2ahil/Hanko-auth-backend&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;I will be showing you the important codes for authentication :&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Already docs are available from Hanko *&lt;/em&gt;:&lt;br&gt;
&lt;a href="https://docs.hanko.io/quickstarts/frontend/react"&gt;https://docs.hanko.io/quickstarts/frontend/react&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React codes:&lt;/strong&gt;&lt;br&gt;
Here is the login code :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react'
import { register, Hanko } from "@teamhanko/hanko-elements";
import { useNavigate } from "react-router-dom";
import { useEffect, useMemo, useCallback } from 'react';


const hankoApi = import.meta.env.REACT_APP_HANKO_API
console.log(hankoApi)


export const Login = () =&amp;gt; {
  const navigate = useNavigate();
  const hanko = useMemo(() =&amp;gt; new Hanko(hankoApi), []);

  const redirectAfterLogin = useCallback(() =&amp;gt; {
    navigate("/MainPage");
  }, [navigate]);

  useEffect(
    () =&amp;gt;
      hanko.onAuthFlowCompleted(() =&amp;gt; {
        redirectAfterLogin();
      }),
    [hanko, redirectAfterLogin]
  );

  useEffect(() =&amp;gt; {
    register(hankoApi).catch((error) =&amp;gt; {
      console.log(error)
      // handle error
    });


    if (hanko.session.isValid() === true) {
      navigate('/MainPage')
    }
  }, []);


  return (
    &amp;lt;div className='flex min-h-screen justify-center items-center'&amp;gt;
      &amp;lt;div className='w-[400px] p-10 border rounded-xl shadow-md '&amp;gt;
        &amp;lt;hanko-auth /&amp;gt;
      &amp;lt;/div&amp;gt;

      {/* &amp;lt;hanko-auth api={hankoApi} /&amp;gt; */}
    &amp;lt;/div&amp;gt;
  )
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is logout code :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React,{  useState,useEffect} from 'react'
import { useNavigate } from "react-router-dom";
import { Hanko } from "@teamhanko/hanko-elements";

const hankoApi = import.meta.env.REACT_APP_HANKO_API

export const Logout = () =&amp;gt; {

    const navigate = useNavigate();
    const [hanko, setHanko] = useState(null)

    useEffect(() =&amp;gt; {
        import("@teamhanko/hanko-elements").then(({ Hanko }) =&amp;gt;
            setHanko(new Hanko(hankoApi ?? ""))
        );
    }, []);

    const logout = async () =&amp;gt; {
        try {
            await hanko?.user.logout();
            navigate("/");
        } catch (error) {
            console.error("Error during logout:", error);
        }
    };

    return (&amp;lt;&amp;gt;


        &amp;lt;button  className=" px-4 py-2 mt-2 bg-blue-500 text-white rounded hover:bg-green-600" onClick={async()=&amp;gt;await logout()}&amp;gt;logout&amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
    )
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the profile code :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useEffect, useState } from 'react';
import { register } from "@teamhanko/hanko-elements";
import { useNavigate } from "react-router-dom";
import { Hanko } from "@teamhanko/hanko-elements";


const hankoApi = import.meta.env.REACT_APP_HANKO_API;
const hanko = new Hanko(import.meta.env.REACT_APP_HANKO_API);

export const Profile = () =&amp;gt; {
  const navigate = useNavigate();
  const [showProfilePage, setShowProfile] = useState(false);

  useEffect(() =&amp;gt; {
    async function fetchData() {

      await register(hankoApi);






    }

    fetchData();
  }, []);

  return (&amp;lt;&amp;gt;


    &amp;lt;button
      className="px-4 py-2 mb-2 bg-blue-500 text-white rounded hover:bg-green-600"
      onClick={() =&amp;gt; setShowProfile(!showProfilePage)}
    &amp;gt;
      Profile Setting
    &amp;lt;/button&amp;gt;

    {
      showProfilePage &amp;amp;&amp;amp; (
        &amp;lt;div className='w-[400px] p-3 border rounded-xl shadow-md bg-[#FFFFFF] '&amp;gt;


          &amp;lt;hanko-profile /&amp;gt;
        &amp;lt;/div&amp;gt;

      )
    }

  &amp;lt;/&amp;gt;);
}




&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the final main page :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useEffect, useState } from 'react';
import { Profile } from './Profile';
import { Logout } from './Logout';
import axios from 'axios';
import { TypeAnimation } from 'react-type-animation';
import { Hanko } from "@teamhanko/hanko-elements";
import { useNavigate } from "react-router-dom";

export const MainPage = () =&amp;gt; {


    const navigate = useNavigate();
    const [text, setText] = useState('');
    const [showMainPage, setShowMainPage] = useState(false);
    const [response, setResponse] = useState('');
    const [error, setError] = useState(null); // State variable for error message
    const [loading, setLoading] = useState(false);
    const hanko = new Hanko(import.meta.env.REACT_APP_HANKO_API);
    const send = () =&amp;gt; {
        setLoading(true);
        setError(null); // Clear previous error message, if any
console.log()
        axios.post("http://localhost:3000/summarize", { text_to_summarize: text }, { withCredentials: true })
            .then(res =&amp;gt; {
                console.log('response', JSON.stringify(res.data));
                setResponse(res.data);
            })
            .catch(error =&amp;gt; {
                console.error('Error:', error);
                setError("An error occurred. Please try again."); // Set the error message
            })
            .finally(() =&amp;gt; {
                setLoading(false);
            });
    }


    useEffect(()=&amp;gt;{
            if (hanko.session.isValid() === false) {
        navigate('/login')
      }
    })

    return (
        &amp;lt;div className="p-8 bg-gray-100"&amp;gt;
            &amp;lt;div className='relative space-x-10'&amp;gt;
                &amp;lt;Profile /&amp;gt;
                &amp;lt;Logout /&amp;gt;
            &amp;lt;/div&amp;gt;

            &amp;lt;center&amp;gt;&amp;lt;div className='m-4 mb-8 text-xl font-bold leading-none tracking-tight text-gray-900 md:text-2xl lg:text-3xl dark:text-black'&amp;gt;AI TEXT SUMMARIZER&amp;lt;/div&amp;gt;&amp;lt;/center&amp;gt;

            &amp;lt;div&amp;gt;
                &amp;lt;div className="grid grid-cols-1 lg:grid-cols-2  gap-4"&amp;gt;
                    &amp;lt;div className='flex flex-col'&amp;gt;
                        &amp;lt;textarea
                            className="mt-5 p-2 border border-gray-300 rounded min-h-[20rem] w-full"
                            placeholder="Enter text to summarize"
                            onChange={(e) =&amp;gt; setText(e.target.value)}
                            value={text}
                        /&amp;gt;
                        &amp;lt;div className='max-w-[20rem] mx-auto'&amp;gt;
                            &amp;lt;button
                                className={`px-5 py-3 mt-2 w-full bg-blue-500 text-white rounded-full hover:bg-green-600 ${loading ? 'cursor-not-allowed' : ''}`}
                                onClick={send}
                                disabled={loading}
                            &amp;gt;
                                {loading ? 'Loading...' : 'Send'}
                            &amp;lt;/button&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                    &amp;lt;div&amp;gt;
                        {error ? ( // Check for error message
                            &amp;lt;div className="mt-4 text-red-800"&amp;gt;
                                &amp;lt;strong&amp;gt;Error:&amp;lt;/strong&amp;gt;
                                &amp;lt;p&amp;gt;{error}&amp;lt;/p&amp;gt;
                            &amp;lt;/div&amp;gt;
                        ) : response === '' ? (
                            &amp;lt;div className='mt-5'&amp;gt;
                                &amp;lt;div className='flex flex-col'&amp;gt;
                                    &amp;lt;p className="mb-4 text-4xl font-extrabold leading-none tracking-tight text-gray-900 md:text-4xl lg:text-5xl dark:text-black"&amp;gt;
                                        {loading ? 'Loading...' : &amp;lt;&amp;gt; &amp;lt;TypeAnimation
                                            sequence={[
                                                // Same substring at the start will only be typed out once, initially
                                                'Try typing something to see the summarized text',
                                                2000, // wait 1s before replacing "Mice" with "Hamsters"
                                                'Our AI can summarize anything',
                                                2000,

                                            ]}
                                            wrapper="span"
                                            speed={50}

                                            repeat={Infinity}
                                        /&amp;gt;&amp;lt;/&amp;gt; }
                                    &amp;lt;/p&amp;gt;
                                    {loading ? (
                                        &amp;lt;div className="w-20 h-20 mx-auto"&amp;gt;
                                            &amp;lt;div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500"&amp;gt;&amp;lt;/div&amp;gt;
                                        &amp;lt;/div&amp;gt;
                                    ) : (
                                        &amp;lt;div className=''&amp;gt;
                                        &amp;lt;iframe
                                            height="250"
                                            width="250"
                                            src="https://giphy.com/embed/26AHONQ79FdWZhAI0"
                                            className="giphy-embed "
                                        &amp;gt;&amp;lt;/iframe&amp;gt;
                                        &amp;lt;/div&amp;gt;
                                    )}
                                &amp;lt;/div&amp;gt;
                            &amp;lt;/div&amp;gt;
                        ) : (
                            &amp;lt;div className="mt-4 text-gray-800"&amp;gt;
                                &amp;lt;strong&amp;gt;Summary:&amp;lt;/strong&amp;gt;
                                &amp;lt;p&amp;gt;{response}&amp;lt;/p&amp;gt;
                            &amp;lt;/div&amp;gt;
                        )}
                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    );
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Express.js codes :&lt;/strong&gt;&lt;br&gt;
Here is backend code :"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require('express');
const bodyParser = require('body-parser');
const cors = require("cors");
const cookieParser = require("cookie-parser");
const jose = require("jose"); // Import the jose library
const dotenv = require("dotenv");
const summarizeText = require('./summarize')
dotenv.config()

const app = express();
app.use(cors({ credentials: true, origin: true }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }))
app.use(cookieParser());

console.log(process.env.HANKO_API_URL)


async function Middleware(req, res, next) {


  const token = req.cookies.hanko;

  console.log("token", token)

  if (!token) {
    return res.status(401).json({ error: "Unauthorized" });
  }


  const hankoApi = process.env.HANKO_API_URL

  const JWKS = jose.createRemoteJWKSet(
    new URL(`${hankoApi}/.well-known/jwks.json`)
  );

  console.log(JWKS)
  try {
    // Verify the token using the JWKS
    console.log("aya 1")
    const verifiedJWT = await jose.jwtVerify(token, JWKS);

    console.log("aya 2")
    // console.log(verifiedJWT)
    req.auth = verifiedJWT; // Attach the authentication data to the request object
    next();
  } catch (error) {
    return res.status(401).json({ error: "Unauthorized" });
  }
}

app.get("/protected", (req, res) =&amp;gt; {
  // Only users with a valid Hanko token will reach this route
  if (req.auth) {
    console.log(req.auth)
    res.sendStatus(200);
  } else {
    res.status(401).json({ error: "Unauthorized" });
  }
});

app.post("/summarize", Middleware, (req, res) =&amp;gt; {


  if (req.auth) {

    const text = req.body.text_to_summarize;
    console.log(text)
    summarizeText(text)
      .then(response =&amp;gt; {
        res.send(response); // Send the summary text as a response to the client
      })
      .catch(error =&amp;gt; {
        console.log(error.message);
      });
  }else{
    res.status(401).json({ error: "Unauthorized" });
  }
  // get the text_to_summarize property from the request body

});


app.get('/', (req, res) =&amp;gt; {
  res.send("hello");
})









app.listen(3000, () =&amp;gt; {
  console.log("Server started at 3000");
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note : somehow while deploying req.cookies was not showing cookies from browser , but it was working fine in local host . you can check from your side also .&lt;/p&gt;

&lt;p&gt;Finally, i deployed my frontend to : Vercel&lt;br&gt;
Express backend to : render.com&lt;/p&gt;

&lt;p&gt;Full Live Working project Link :"&lt;a href="https://hanko-auth-frontend.vercel.app/"&gt;https://hanko-auth-frontend.vercel.app/&lt;/a&gt;"&lt;/p&gt;

</description>
      <category>react</category>
      <category>python</category>
      <category>node</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
