DEV Community

Cover image for Simplest Signup/Login system by SilvenLEAF
SilvenLEAF
SilvenLEAF

Posted on • Updated on

Simplest Signup/Login system by SilvenLEAF

Creating a Signup Login system with JavaSript is way easier than you think! Let's made one from scratch!

SIMPLEST WAY TO CREATE LOGIN SIGNUP

  • Create Strategy
  • Config Passport
  • Handle Route
  • Use cookie
  • Use frontEND

Let's go one by one.

COMPLETE TUTORIAL

For those who already know how to set up their basic server and database, skip to STEP 4.

Step 0. Setting up our Project folder

Create a folder called "authDemo" and open it on your favorite text editor. Create a file named app.js. Now type npm init -y on your terminal. It'll just create a package.json file to track our packages etc. Anyway let's begin the real adventure!

Step 1. Basic Server Setup

First type this on your terminal to install these packages
npm i express mongoose passport passport-local cookie-session

Short package descriptions
  • express: to create our server
  • mongoose: to connect to our database
  • passport: our main package for login/signup including Google Github logins too
  • passport-local: to create our login/signup with login and signup forms

Now write these on your app.js file

// core modules
const express = require('express');
const path = require('path'); //it is an in-built node module so no need to install it

const passport = require('passport'); //this is our main package that will help us create the login signup system
const cookieSession = require('cookie-session'); //this is for using cookies so that our users stay logged in





// ------------------------------FIRING EXPRESS APP
const app = express();
app.use(express.json()); //it allows us access the data sent from frontend using req.body
app.use(express.urlencoded({ extended: false })); 
app.use(express.static(path.join(__dirname, `client`))); //here we are saying that our static files I mean html css etc files will be served from this client file




// -------------------------COOKIE AND PASSPORT
app.use(cookieSession({
  maxAge: 24*60*60*1000, //it is the total expiration time, here the cookie will be alive for 1 day
  keys: [`abcdefghijklmn`], //here type whatever your want instead of abcdefghijklm, I just typed abcdefghijklm 
}));








/* -------------------------------------------------
.                    config
------------------------------------------------- */
require('./config/mongodbConfig'); //Here it is firing the mongodbConfig file that has our database configuration, we'll create it soon
require('./config/passportConfig'); //Here it is firing the passportConfig file that has our login/signup configuration, we'll create it soon












/* -------------------------------------------------
.                    routes
------------------------------------------------- */
//                  auth routes
app.use(require('./routes/authRoute')); //here authRoute has our login signup routes, we'll create it soon



// CATCH ALL HANDLER, if there is any route that does not match the above routes, send the index.html file
app.get('*', (req, res, next)=>{
  try {
    res.sendFile(path.join(__dirname, `client/index.html`));
  } catch (err) {
    next(err, req, res)
  }
})






// ERRORS HANDLER
app.use((err, req, res, next)=>{
  console.log(err.message);

  console.log(err);
  res.json({ msg: `Server error`, error: err.message })
});
// --------------------end of routes------------------------


















// -----------------------------------------LISTEN
const PORT = process.env.PORT || 5000;
app.listen(PORT, ()=>{
  console.log(`Server is running on port ${ PORT }`);
});
Enter fullscreen mode Exit fullscreen mode

Will explain this code later. First let's set our database configuration and routes

Step 2. Basic Database setup

create a config folder, here we will store all our configuration. You don't have to, but I prefer it because if your project gets larger it'll help you make your codebase cleaner and easier to maintain. Anyway, now create a file called mongodbConfig.js on that folder.

Write these on mongodbConfig.js file

const mongoose = require('mongoose');







mongoose.connect(YOUR_DATABASE_STRING,
  {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    useCreateIndex: true,
    useFindAndModify: false,
  },



  (err) =>{
    if(err) throw err;
    console.log('connected to MongoDB');
  }
)
Enter fullscreen mode Exit fullscreen mode

Replace YOUR_DATABASE_STRING with your database string, if you don't have that, go and use MongoDB Atlas and create a cluster and you'll get your database string.

Step 3. Creating an USER model

Create a folder called models and create User.js

Type these on User.js file

const mongoose = require('mongoose');



/* ----------------------------------
.           SUB SCHEMAs
--------------------------------------- */
const LocalSchema = new mongoose.Schema({
  email: String,
  password: String,

})









/* ------------------------------------------
.                MAIN SCHEMA
----------------------------------------------- */
const UserSchema = new mongoose.Schema({
  local: LocalSchema, //I'm using this sub schema now because we will be creating Login with Google Twitter Linkedin Github etc, so it'll help us in the future too.

  username: String,

});







/* ------------------------------------------
.                USER MODEL
----------------------------------------------- */
module.exports = User = mongoose.model('User', UserSchema);
Enter fullscreen mode Exit fullscreen mode

Here we will just store the email, password and username to keep everything simple.

Step 4. REAL CODING BEGINS

Create a passportConfig.js file inside config folder. Also create a passportStrategies sub-folder inside config folder. Now create a SignupStrategy.js and LoginStrategy.js files inside passportStrategies folder.

STEP 5. Create Login and Signup Strategies

Write these on your SignupStrategy.js file

const Strategy = require('passport-local'); //this is to create our login signup strategies
const User = require('../../models/User'); //our User model to save our user data also to retrieve our user data
const bcrypt = require('bcryptjs'); //we use it to hash our passwords, if you don't know how to use it, go see my prev blog. I already make one on it explaining in detail











module.exports = SignupStrategy = new Strategy(
  {
    // overriding the default username with email
    usernameField: 'email',  //passport by default uses username and password  to login and signup, just like My Anime List website. We are here changing it so that users signup with email and password system and not with username and password system
    passwordField: 'password',
    passReqToCallback: true, //this will allow use use req on the following callback function
  },




  (req, email, password, done)=>{
    const { username } = req.body; //retrieving username from the data that frontend sent to us. Look here we'll also retrieve other data if it sent us, like first name last name location etc. To keep it simple I'm just using username. One more thing You don't need to retrieve email of password this way because passport will already retrieving it for you


    User.findOne({ 'local.email': email }, (err, user)=>{ //checking if there is already an user with this email


      // if there is an error while checking
      if(err) return done(err); //finish this process right here and send back error to our error handler




      // if there is already an account with this email, we'll finish the process right here and notify the user that this email is already taken
      if(user) return done({ msg: `This email is already taken` }, null);




      // if this email is not already taken, create a new account with this email
      User.create({
        'local.email': email,
        'local.password': bcrypt.hashSync(password, bcrypt.genSaltSync()), //here saving the hashed password, see my prev blog to know in detail


        username,

      }).then(newUser=> done(null, newUser)); // Now when the account has been created, send this data onto the passport middleware on the auth route (we'll create it soon) and that middleware will send this data back to the cookie-fyer which will then cookie-fy our data and store it in a cookie

    })
  }
)
Enter fullscreen mode Exit fullscreen mode

Read the comments to understand to code. I've explained line by line

Now similarly we are gonna create the Login Strategy. It's even easier.

Write these on LoginStrategy.js file

const Strategy = require('passport-local'); //to create login signup strategy
const User = require('../../models/User'); //to save or retrieve user data
const bcrypt = require('bcryptjs'); //to hash or verify passwords, to know more see my prev blog





module.exports = LoginStrategy = new Strategy(
  {
    // overriding default username with email
    usernameField: 'email',  //as explained passport uses username and password to login by default, we are overriding it so that it uses email and password for logging in
    passwordField: 'password',
    passReqToCallback: true, //it'll allow us use req on the following callback function
  },



  (req, email, password, done)=>{
    User.findOne({ 'local.email': email }, (err, user)=>{ //finding the user with that email

      // if there is any error while finding, finish the process right here and send back the error to our error handler
      if(err) return done(err);



      // if there is no account with that email then let the user know that there is no account with this email
      if(!user) return done({ msg: `No user found`}, null);



      // if password does not match, let the user know that he typed wrong passwords
      const isPasswordValid = bcrypt.compareSync(password, user.local.password); //it is comparing the plain password with the saved hashed password to see if they match, to know more about it see my previous blog, I've explained in detail
      if(!isPasswordValid) return done({ msg: `Invalid Credentials` }, null);




      // if everything is OK, send the user data to the password middleware on the auth route that will then send the user data onto the cookie-fyer that will then cookie-fy and store the data on a cookie
      return done(null, user)

    })
  }
)
Enter fullscreen mode Exit fullscreen mode

Read the comments to understand each line of code.

Step 6. Passport Config

Now open the passportConfig.js file and write these

const passport = require('passport'); //our main package for creating login signup system
const User = require('../models/User'); //to save or retrieve 
user data




const LoginStrategy = require('./passportStrategies/LoginStrategy');
const SignupStrategy = require('./passportStrategies/SignupStrategy');






/* ------------------------------------
.     SERIALIZE AND DESERIALIZE
----------------------------------------- */
//this is our cookie-fyer machine, it'll take the user data and cookie-fy it and store it on a cookie, here we will only cookie-fy the id of the user because we do not want to store his email and password on the cookie because if we do and if hackers find this cookie then it'll be a disaster. ha ha, I think you got my point
passport.serializeUser((user, done)=>{
  done(null, user.id);
});


//this is the de-cookie-fyer machine. When a user with the cookie comes to our website, it asks them to show him the cookie so that it knows that the user is already logged in. Then it will de-code the cookie and get that id we stored out of the cookie and find the user who has this id, then it will retrieve it's data and store in in a user object and it will attach it on our req object. so now if he is logged in we can access his data with req.user amazing right?
passport.deserializeUser((id, done)=>{
  User.findById(id).then(user=> done(null, user));
});





/* ------------------------------------
.               STRATEGIES
----------------------------------------- */
//here we are using those strategies we created
passport.use('local-signup', SignupStrategy); //we are also giving them name so that we can reference them by name later
passport.use('local-login', LoginStrategy); //same thing here too


Enter fullscreen mode Exit fullscreen mode

Now the 1st part done. Now we just need to create the routes and then use it on our frontend. let's go!

Step 7. Creating routes

let's create a folder on our root level named routes and create a file called authRoute.js inside it.

Now write these inside the authRoute.js file

const router = require('express').Router(); //this is the router that'll create the routes for us
const passport = require('passport'); //this is our main package for login signup system




/* --------------------------------------
.                 LOGOUT
------------------------------------------- */
//this is a route for logging out. It'll log out the users and then send back a message to let them know that they are successfully logged out
router.get('/logout', (req, res)=>{
  req.logOut();
  res.json({ msg: `Logged out` }); 
});




/* --------------------------------------
.          GET LOGGED IN USER 
------------------------------------------- */
//this is a route to get logged in user data 
router.get('/user', (req, res)=>{
   if(req.user) { //if user is logged in, user data will be stored on req.user
       res.json({ user: req.user });
   } else { //if user is not logged in, req.user will not exist
       res.json({ msg: "Please log in to access this data" });
   }
});




/* --------------------------------------
.                 SIGNUP
------------------------------------------- */
router.post('/signup', (req, res, next)=>{
  passport.authenticate('local-signup', (err, user, info)=>{ //this is our passport authenticating middleware I was talking about
    // if there is any error (including the error I defined on the Strategy), send back the error with that error message to the user
    if(err) return res.status(400).json(err);

    //if there is no error in sign up, it'll create their account. so now log them in
    req.logIn(user, (err)=>{
      // if there is any error while logging in, send the error message
      if(err) return res.status(500).json({ msg: `Oops, something went wrong` });



      // if everything is OK, return the user onto the Cookie-fyer
      return res.json(user);
    })



  })(req, res, next)
})



























/* --------------------------------------
.                 LOGIN
------------------------------------------- */
router.post('/login', (req, res, next)=>{
  passport.authenticate('local-login', (err, user, info)=>{ //this is the passport middleware I was talking about
    // if there is any error (including the error I defined on the Strategy) send back the error message to the user
    if(err) return res.status(400).json(err);


  //if there is no error, log them in
   req.logIn(user, (err)=>{
    //  if there is any error while logging in, send back the error message to the user
    if(err) return res.status(500).json({ msg: `Oops, something went wrong`});



    // if everything is OK, send the user data onto the Cookie-fyer
    return res.json(user);
   }) 
  })(req, res, next)
})





module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Now we are almost done! Yippie. We just need a frontend to interact with our login signup system.

I'm gonna use basic HTML, you can use whatever your want, be it react, angular, vue or whatever. Everything is same.

Step 8. Create the FrontEND

Create a folder called client on our root level. Then create index.html. You can also create external js and other external css files here and reference it from the index.html. I'm gonna keep it simple and going with the default styles and not any extra styles.

Let's create a login and a signup form inside index.html.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1" />


    <title>LOGIN SIGNUP</title>
  </head>
  <body>

<form id="signupForm" >
  <input id="signupEmail" type="email" required/>
  <input id="signupPassword" type="password" required/>
  <input id="signupUsername" type="text" />
  <button>Sign up</button>
</form>

<form id="loginForm" >
  <input id="loginEmail" type="email" required/>
  <input id="loginPassword" type="password" required/>
  <button>Log in</button>
</form>




    <script>
const signupForm = document.querySelector('#signupForm');
const loginForm = document.querySelector('#loginForm');

const signupEmail= document.querySelector('#signupEmail');
const signupPassword= document.querySelector('#signupPassword');
const signupUsername= document.querySelector('#signupUsername');

const loginEmail= document.querySelector('#loginEmail');
const loginPassword= document.querySelector('#loginPassword');



//signup form (if you don't know how fetch works see my prev blog, I explained in detail)
signupForm.addEventListener('submit', async (e)=>{
   e.preventDefault();

   const response = await fetch('/signup', {
         method: 'POST',
         headers: {
            'Content-Type': 'application/json'
         },
         body: JSON.stringify({
              email: signupEmail.value,
              password: signupPassword.value,
              username: signupUsername.value
        })

   });

   const data = await data.json();
   console.log(data);
});


//login form
loginForm.addEventListener('submit', async (e)=>{
   e.preventDefault();

   const response = await fetch('/login', {
         method: 'POST',
         headers: {
            'Content-Type': 'application/json'
         },
         body: JSON.stringify({
              email: loginEmail.value,
              password: loginPassword.value
        })

   });

   const data = await data.json();
   console.log(data);
});
    </script>
  </body>
</html>

Enter fullscreen mode Exit fullscreen mode

Congrats we just created a complete Login Signup System. Read the comments to understand each line of code. Now let me go over it one more time.

What is happening on the code?

We made a signup POST request to /signup url from our frontend giving email, password and username. You can send more data too. I just kept it simple.

Now our server is listening for requests on /signup route. She found this request and told, "Hey Passport, this is about signup. You handle this for me please". So now Passport takes over it, it grabs email and password (if we did not override the default username with email it would have grabbed username and password) and sends it to our strategy. On the strategy, if in signup, we checked to see that the email is not already taken, if yes, it will send back an erro message saying "email already taken" or something. You can show it on your frontend. Now if it's not taken, after successfully creating the account it'll cookie-fy the user id and attach the cookie to our req object. so every time we make a new request we'll already be logged in.

Now same for log in strategy. We'll check if there is the account and also check the passwords to match. if any error, it'll send back error message. If not it'll log them in and cookie-fy.

When they log out, it'll destroy the cookie and you'll be logged out.

Now let's test our app.

TESTING OUR APP

Go signup and got to localhost:5000/user, you'll see the user data. Now go to localhost:5000/logout to log out. Now go again to localhost:5000/user. You'll no longer see the user data because you already logged out. Now log in and then go to localhost:5000/user, you'll see user data. Log out again by going to localhost:5000/logout and you'll be logged out and you'll not see user data. Amazing right?

Congrats, you just created your very first user login and signup system!!! Yippie!

Now get ready for more!!

If you have any questions or If you are stuck

Feel free to reach out to me. You can also contact me on LinkedIN https://www.linkedin.com/in/silvenleaf/ or on Twitter (as @silvenleaf ).

If you wanna know more about me, this is my portfolio website SilvenLEAF.github.io

I'd LOVE to be your friend, feel FREE to reach out to me!!

NEXT BLOG is coming on 28th Nov, 2020

on Signup/Login with Google Github and Linkedin Series**

Next Blogs DATE

  • Nov 28th and Nov 5th 2020, on Signup/Login with Google Github and Linkedin Series**

  • Nov 28th 2020, How to create Login with Google

  • Nov 28th 2020, How to create Login with Github

  • Nov 28th 2020, How to create Login with LinkedIn

  • Nov 28th 2020, How to create Login with Twitter

  • Nov 30th 2020, Password Reset Series (with Node.js and React)

If this blog was helpful to you,

PLEASE give a LIKE and share,

It'd mean a lot to me. Thanks

Prev Blog


Change CSS variables with 1 JavaScript line

Next Blog

Coming on 28th Nov

Alt Text

Top comments (2)

Collapse
 
kimdex profile image
kimdex

how to deploy on live

Collapse
 
silvenleaf profile image
SilvenLEAF

Hi there @kdtrrs thanks for your question. To deploy live you can use Heroku or anything. You might find heroku easier. Simply create an account, create an app, connect your github repo and choose auto deploy and that's all. You are set. Every time you push to your remote, it'll redeploy. So updatiny it is super easy, also if using MERN or other stack, you can simply write a heroku script to automatically create the build of your app. You can find the guide on the web or YTube too. Maybe if you want I can write a blog on it in the future. Wish you an incredible weekend my lovely friend!!