article

Tuesday, July 23, 2024

Next.js 14 Node Express Login Register with Passportjs | Mysql

Next.js 14 Node Express Login Register with Passportjs | Mysql

https://expressjs.com/
Express JS
Fast, unopinionated, minimalist web framework for Node.js

Install
$ npm install express --savev PS C:\nodeproject> npm install express --save
https://expressjs.com/en/starter/hello-world.html

mysql
https://github.com/mysqljs/mysql
$ npm install mysql
PS C:\nodeproject>npm install mysql

cors
CORS is a node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options.
https://www.npmjs.com/package/cors
PS C:\nodeproject>npm i cors

Passport
https://www.npmjs.com/package/passport
Passport is Express-compatible authentication middleware for Node.js.
npm i passport

express-session
https://www.npmjs.com/package/express-session
Create a session middleware with the given options.
npm install express-session

cookie-parser
https://www.npmjs.com/package/cookie-parser
Parse Cookie header and populate req.cookies with an object keyed by the cookie names.
npm install cookie-parser

bcrypt
https://www.npmjs.com/package/bcrypt
A library to help you hash passwords.
npm install bcrypt

passport-local
https://www.npmjs.com/package/passport-local
Passport strategy for authenticating with a username and password.
npm install passport-local

run PS C:\nodeproject> node index.js
index.js
//index.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const passport = require('passport'); //npm install passport https://www.npmjs.com/package/passport
const expressSession = require('express-session'); //npm install express-session  https://www.npmjs.com/package/express-session
const cookieParser = require('cookie-parser'); //npm install cookie-parser https://www.npmjs.com/package/cookie-parser
const bycrypt = require('bcrypt'); //npm install bcrypt https://github.com/kelektiv/node.bcrypt.js
const db = require('./db');

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressSession({ secret: 'cairocoders-ednalan', resave: false, saveUninitialized: false })); 

app.use(cors({
      origin: 'http://localhost:3000',
      credentials: true
}));

app.use(cookieParser('cairocoders-ednalan')); 

app.use(passport.initialize());
app.use(passport.session());
require('./passportConfig')(passport); 

//routes
app.post('/register', (req, res) => {
  
    const query = "INSERT INTO users (username, password) VALUES (?,?)";
    const query2 = "SELECT * FROM users where username = ?";
  
    db.query(query2, [req.body.username] ,async (err, rows) => {
      if (err) {console.log(err);}
      if (rows.length > 0) {res.send("User already exists");}
      if (rows.length === 0) {
        const hashedPassword =  await bycrypt.hash(req.body.password, 10);
        db.query(query, [req.body.username, hashedPassword], (err, rows) => {
          if (err) {console.log(err);}
          res.send("User created");
        });
      }
    })
})

app.post('/login', (req, res, next) => {
  passport.authenticate('local', (err, user, info) => { 
    if (err) {console.log(err);}
    if (!user) {res.send("User not found");}
    if (user) {
      req.login(user, (err) => {
        if (err) {console.log(err);}
        res.send("success");
        console.log(user);
      })
    }
  })(req, res, next); 
})

app.get('/getUser', (req, res) => {
  res.send(req.user);
  console.log(req.user);
})

app.listen(3001, () => {console.log('Server started on port 3001')});
db.js
//db.js
const mysql = require("mysql");

const db = mysql.createPool({
    socketPath: '/Applications/MAMP/tmp/mysql/mysql.sock',
    connectionLimit: 10,
    host: "localhost",
    user: "root",
    password: "root",
    database: "nodeexpressDB"
});

// Ping database to check for common exception errors.
db.getConnection((err, connection) => {
    if (err) {
        if (err.code === 'PROTOCOL_CONNECTION_LOST') {
            console.error('Database connection was closed.');
        }
        if (err.code === 'ER_CON_COUNT_ERROR') {
            console.error('Database has too many connections.');
        }
        if (err.code === 'ECONNREFUSED') {
            console.error('Database connection was refused.');
        }
    }
 
    if (connection) connection.release();
 
    return;
});

module.exports = db;
passportConfig.js
//passportConfig.js
const db = require('./db');
const bcrypt = require('bcrypt');
const localStrategy = require('passport-local').Strategy; //npm install passport-local https://www.npmjs.com/package/passport-local

module.exports = function(passport) {
    passport.use(
        new localStrategy((username, password, done) => {
            const query = "SELECT * FROM users where username = ?";
            db.query(query, [username] ,(err, rows) => {
                if(err)throw err;  
                if(rows.length === 0) {
                    return done(null, false);
                }
                bcrypt.compare(password, rows[0].password, (err, result) => {
                    if (err) throw err;
                    if (result === true) {
                        return done(null, rows[0]);
                    } 
                    else {
                        return done(null, false);
                    }
                })
            })
        }))


    passport.serializeUser((user, done) => {
        done(null, user.id);
    })


    passport.deserializeUser((id, done) => {
        const query = "SELECT * FROM users where id = ?";
        db.query(query, [id] ,(err, rows) => {
            if(err)throw err;  
            const userInfo = {
                id: rows[0].id,
                username: rows[0].username
            }
            done(null, userInfo);
        })
    }) 
}
Next.js

Install requirements
npm install axios
https://www.npmjs.com/package/axios

app\page.js
//
export default function Home() {
  return (
    <div className="w-screen py-20 flex justify-center flex-col items-center">
      <div className="flex items-center justify-between gap-1 mb-5">
        <h1 className="text-4xl font-bold">Next.js 14 Node Express Login Register with Passportjs | Mysql</h1>
      </div>    
      Homepage 
    </div>
  );
}
app\users\login\page.jsx
//app\users\login\page.jsx
"use client";

import { useState } from 'react'
import axios  from 'axios'
import Link from 'next/link';
import { useRouter } from 'next/navigation';

export default function Home() {

    const [loginUsername, setLoginUsername] = useState('')
    const [ loginPassword, setLoginPassword ] = useState('')  
    const[message, setMessage]= useState('');

    const router = useRouter();

    const login = () => {
        axios({
        method: 'post',
        data: {
            username: loginUsername,
            password: loginPassword
        },
        withCredentials: true,
        url: 'http://localhost:3001/login'
        })
        .then(res => {
          console.log(res)
          if (res.data == "success") {
              router.push('/users/profile')
          }else {
              setMessage(res.data); 
          }
        })
        .catch(err => {console.log(err)})
    }

    return (
    <div className="flex justify-center relative">
      <div className="mx-auto max-w-lg px-6 lg:px-8 absolute py-20">
        <h1 className="text-center text-3xl">Login </h1>
        <div className="rounded-2xl bg-white shadow-xl">
          <div className="lg:p-11 p-7 mx-auto">
            <div className="mb-11">
                <h1 className="text-gray-900 text-center font-manrope text-3xl font-bold leading-10 mb-2">Welcome Back</h1>
                <p className="text-gray-500 text-center text-base font-medium leading-6">Let’s get started with your 30 days free trail</p>
            </div>
            <input type="text" className="w-full h-12 text-gray-900 placeholder:text-gray-400 text-lg font-normal leading-7 rounded-full border-gray-300 border shadow-sm focus:outline-none px-4 mb-6" 
                placeholder="Username" 
                name="username"
                onChange={e => setLoginUsername(e.target.value )}
                />
            <input type="text" className="w-full h-12 text-gray-900 placeholder:text-gray-400 text-lg font-normal leading-7 rounded-full border-gray-300 border shadow-sm focus:outline-none px-4 mb-1" 
                placeholder="Password" 
                name="password"
                onChange={e => setLoginPassword(e.target.value )}
                />
            <Link href="#" className="flex justify-end mb-6">
              <span className="text-indigo-600 text-right text-base font-normal leading-6">Forgot Password?</span>
            </Link>
            <button  onClick={login} className="w-full h-12 text-white text-center text-base font-semibold leading-6 rounded-full hover:bg-indigo-800 transition-all duration-700 bg-indigo-600 shadow-sm mb-11">Login</button>
            <p className="text-red-700"><b>{ message }</b></p>
            <Link href="/users/register" className="flex justify-center text-gray-900 text-base font-medium leading-6"> Don’t have an account? <span className="text-indigo-600 font-semibold pl-3"> Sign Up</span>
            </Link>
          </div>
        </div>
      </div> 
    </div>
  );
}
app\users\register\page.jsx
//app\users\register\page.jsx
"use client";

import { useState } from 'react'
import axios  from 'axios'
import Link from 'next/link';
import { useRouter } from 'next/navigation';

export default function Home() {

    const [registerUsername, setRegisterUsername] = useState('')
    const [ registerPassword, setRegisterPassword ] = useState('')
    const[message, setMessage]= useState('');

    const router = useRouter();

    const register = () => {
        axios({
        method: 'post',
        data: {
            username: registerUsername,
            password: registerPassword
        },
        withCredentials: true,
        url: 'http://localhost:3001/register'
        })
        .then(res => {
            console.log(res)
            if (res.data == "User already exists") {
                setMessage(res.data); 
            }else {
                router.push('/')
            }
        })
        .catch(err => {console.log(err)})
    }


    return (
    <div className="flex justify-center relative">
      <div className="mx-auto max-w-lg px-6 lg:px-8 absolute py-20">
        <h1 className="text-center text-3xl">Register </h1>
        <div className="rounded-2xl bg-white shadow-xl">
          <div className="lg:p-11 p-7 mx-auto">
            <div className="mb-11">
                <h1 className="text-gray-900 text-center font-manrope text-3xl font-bold leading-10 mb-2">Create Account</h1>
                <p className="text-gray-500 text-center text-base font-medium leading-6">Get started with your free account</p>
            </div>
            <input type="text" className="w-full h-12 text-gray-900 placeholder:text-gray-400 text-lg font-normal leading-7 rounded-full border-gray-300 border shadow-sm focus:outline-none px-4 mb-6" 
                placeholder="Username" 
                name="username"
                onChange={e => setRegisterUsername(e.target.value )}
                />
            <input type="text" className="w-full h-12 text-gray-900 placeholder:text-gray-400 text-lg font-normal leading-7 rounded-full border-gray-300 border shadow-sm focus:outline-none px-4 mb-1" 
                placeholder="Password" 
                name="password"
                onChange={e => setRegisterPassword(e.target.value)}
                />
            <button  onClick={register} className="w-full h-12 text-white text-center text-base font-semibold leading-6 rounded-full hover:bg-indigo-800 transition-all duration-700 bg-indigo-600 shadow-sm mb-11">Submit</button>
            <p className="text-red-700"><b>{ message }</b></p>
            <Link href="/users/login" className="flex justify-center text-gray-900 text-base font-medium leading-6"> have an account? <span className="text-indigo-600 font-semibold pl-3"> Login</span>
            </Link>
          </div>
        </div>
      </div> 
    </div>
  );
}
app\users\profile\page.jsx
//app\users\profile\page.jsx
"use client";

import { useState } from 'react'
import axios  from 'axios'

export default function Home() {

    const [ user, setUser ] = useState(null)

    const getUser  = () => {
        axios({
        method: 'get',
        withCredentials: true,
        url: 'http://localhost:3001/getUser'
        }).then(res => {setUser(res.data.username)}).catch(err => {console.log(err)})
    }

    return (
    <div className="flex justify-center relative">
      <div className="mx-auto max-w-lg px-6 lg:px-8 absolute py-20">
        <h1 className="text-center text-3xl">Profile </h1>
        <div className="rounded-2xl bg-white shadow-xl">
          <button onClick={getUser} className="w-full h-12 text-white text-center text-base font-semibold leading-6 rounded-full hover:bg-indigo-800 transition-all duration-700 bg-indigo-600 shadow-sm mb-11">Submit</button>
          {user ? <h1>{user}</h1> : null}
        </div>
      </div> 
    </div>
  );
}
run C:\nextjs>npm run dev

Related Post