User signin/signup using Phone number, Otp in MERN stack

Chaduvula Prasanth
August 23rd, 2020 · 1 min read

User signin/signup using Phone number, Otp in MERN stack

After creating node app using express generator we will use jwt auth for token and msg91 . We will register as startup and get free credentials for send otp

Our backend Node app folder stracture

1bin
2controllers
3models
4modules
5node_modules
6public
7views
8app.js
9package.json
10package-lock.json

Install mongoose to connect database with you app

1npm i mongoose --save

In your app.js

1var createError = require("http-errors");
2var express = require("express");
3var path = require("path");
4var cookieParser = require("cookie-parser");
5var logger = require("morgan");
6const bodyParser = require("body-parser");
7var mongoose = require("mongoose");
8var app = express();
9
10//setup your mongo database address here
11var mongoServer = "mongodb+srv://***********************.mongodb.net/test?retryWrites=true&w=majority";
12
13// connect your mongo
14mongoose.connect(
15 mongoServer,
16 {
17 useNewUrlParser: true,
18 useUnifiedTopology: true,
19 useFindAndModify: false,
20 useCreateIndex: true
21 },
22 err => {
23 console.log(err ? err : "mongodb connected");
24 }
25);
26
27// view engine setup
28app.set("views", path.join(__dirname, "views"));
29app.set("view engine", "ejs");
30
31// routes
32var usersRouter = require("./routes/users");
33
34// middleware
35app.use(logger("dev"));
36app.use(express.json());
37app.use(express.urlencoded({ extended: false }));
38app.use(cookieParser());
39app.use(bodyParser.urlencoded({ extended: false }));
40app.use(bodyParser.json());
41app.use(express.static(path.join(__dirname, "public")));
42
43// routes middleware
44app.use("/api/v1/users", usersRouter);
45
46// catch 404 and forward to error handler
47app.use(function(req, res, next) {
48 next(createError(404));
49});
50
51// error handler
52app.use(function(err, req, res, next) {
53 // set locals, only providing error in development
54 res.locals.message = err.message;
55 res.locals.error = req.app.get("env") === "development" ? err : {};
56
57 // render the error page
58 res.status(err.status || 500);
59 res.json({ err });
60});
61
62module.exports = app;

Create a user model inside models/user.js Take user name and phone number where phone number should be unique Inside models/user.js

1var mongoose = require("mongoose");
2var Schema = mongoose.Schema;
3
4var userSchema = new Schema(
5 {
6 name: {
7 type: String
8 },
9 phoneNumber: {
10 type: Number,
11 unique: true,
12 required: true
13 },
14 },
15 { timestamps: true }
16);
17
18module.exports = mongoose.model("User", userSchema);

Before working on our user controller install these packages

Install jsonwebtoken to use jwt token for user auth

1npm i jsonwebtoken --save

Install Msg91 sendotp package to handle send/verify otp

1npm i sendotp --save

After installing these packages, your package.json look like

1{
2 "name": "backend",
3 "version": "0.0.0",
4 "private": true,
5 "scripts": {
6 "start": "node ./bin/www"
7 },
8 "dependencies": {
9 "body-parser": "^1.19.0",
10 "cookie-parser": "~1.4.4",
11 "debug": "~2.6.9",
12 "ejs": "~2.6.1",
13 "express": "~4.16.1",
14 "http-errors": "~1.6.3",
15 "jsonwebtoken": "^8.5.1",
16 "mongoose": "^5.9.1",
17 "morgan": "~1.9.1",
18 "sendotp": "^1.2.9"
19 }
20}

Let’s go to our controllers/userController.js

Make methods or actions inside controllers/userController.js to handle all the logic for signin, signup, sendotp, verifyotp

Below SENDOTP method recieves the user phone number sends a otp to the phone number

Below VERIFYOTP method is to verify entered otp matched with sendotp, if otp matches then we will check whether the user exist in the database or not. If user exists then we will do successfull signin otherwise we will do successfull signup. If entered otp not matched then we will send error message. Pass your Msg91 OTP credintial and SENDERID ( you will get these in the Msg91 dashboard) to userController.js

This all things happens inside controllers/userController.js

1var User = require("../models/user");
2var jwt = require("jsonwebtoken");
3const SendOtp = require("sendotp");
4
5// pass your msg91 otp creditials SendOtp
6const sendOtp = new SendOtp("****otpcredentials****");
7
8// send otp for sending otp to entered phone number and also pass message sender name like app name from your credintials
9const SENDOTP = (req,res) => {
10 sendOtp.send(req.body.phoneNumber, "***senderID***", (err, data) => {
11 if (err) return res.json({ err });
12 data.type == "success"
13 ? res.json({ success: true })
14 : res.json({ success: false });
15 });
16}
17
18// verify otp to verify entered otp matched with sentotp or not
19const VERIFYOTP = (res,res) => {
20 sendOtp.verify(req.body.phoneNumber, req.body.otp, function(err, data) {
21 if (err) return res.json({ err });
22 if (data.type == "success") {
23 let { phoneNumber } = req.body;
24 User.findOne({ phoneNumber }, (err, user) => {
25 if (err) return res.json({ err });
26 if (!user) {
27 // user signup
28 User.create(req.body, (err, user) => {
29 if (err) return res.json({ err });
30 jwt.sign(
31 {
32 userId: user._id,
33 phoneNumber: user.phoneNumber
34 },
35 "thisissecret",
36 (err, signuptoken) => {
37 if (err) return res.json({ err });
38 res.json({
39 success: true,
40 signuptoken,
41 userId: user._id,
42 message: "registered successfully"
43 });
44 }
45 );
46 });
47 }
48 if (user) {
49 // user signin
50 jwt.sign(
51 {
52 userId: user._id,
53 phoneNumber: user.phoneNumber
54 },
55 "thisissecret",
56 (err, logintoken) => {
57 if (err) return res.json({ err });
58 res.json({ logintoken, userId: user._id });
59 }
60 );
61 }
62 });
63 }
64 if (data.type == "error") res.json({ success: false, message: data.message });
65 });
66}
67module.exports = { SENDOTP, VERIFYOTP }

Inside routes/users.js

1var express = require("express");
2var userController = require("../controllers/userController");
3var userauth = require("../modules/userAuth");
4var router = express.Router();
5
6// send otp
7router.post("/sendotp", userController.SENDOTP);
8
9// verify otp
10router.post("/verifyotp", userController.VERIFYOTP);
11
12module.exports = router;

Now let’s handle your frontend React.js

Make component Auth.jsx inside src folder to handle user signup/signin based on the phone number

Install validator npm package to check whether the entered phone number is valid or not

1npm i validator --save

Here in the Auth.jsx we are handling both phone number and otp entry pages.

Before doing the fetch request, add a proxy to React package.json file for our backend. our backend runs on 3000 so, add this "proxy": "http://localhost:3000" to package.json In React

Inside your src/Auth.jsx

1import React from 'react';
2import validator from 'validator';
3import { NavLink, withRouter } from 'react-router-dom';
4
5class Auth extends React.Component {
6 constructor() {
7 super();
8 this.state = {
9 dispalyPhonePage: true,
10 phoneNumber: '',
11 otp: '',
12 invalid: '',
13 msg: ''
14 };
15 }
16
17 handleChange = event => {
18 this.setState({ invalid: '', msg: '' });
19 let { name, value } = event.target;
20 this.setState({ [name]: value });
21 };
22
23 editPhoneNo = () => {
24 this.setState({ dispalyPhonePage: true });
25 };
26
27 handleSendOtp = () => {
28 this.setState({ dispalyPhonePage: false });
29 fetch('/api/v1/users/sendotp',
30 {
31 method: 'POST',
32 headers: {
33 'Content-Type': 'application/json'
34 },
35 body: JSON.stringify({
36 phoneNumber: this.state.phoneNumber
37 })
38 }
39 )
40 .then(res => res.json())
41 .then(data => {
42 if (data.success) {
43 this.setState({ dispalyPhonePage: false });
44 } else {
45 this.setState({ dispalyPhonePage: true });
46 }
47 });
48 };
49
50 submitPhoneNo = e => {
51 e.preventDefault();
52 if(this.state.phoneNumber) {
53 validator.isMobilePhone(this.state.phoneNumber)
54 ? this.handleSendOtp()
55 : this.setState({ invalid: 'Enter a valid Phone Number' })
56 }
57 else {
58 this.setState({ msg: "Phone Number can't be empty" });
59 }
60 };
61
62 handleVerifyOtp = e => {
63 e.preventDefault();
64 if(this.state.otp){
65 fetch('/api/v1/users/verifyotp',
66 {
67 method: 'POST',
68 headers: {
69 'Content-Type': 'application/json'
70 },
71 body: JSON.stringify({
72 phoneNumber: this.state.phoneNumber,
73 otp: this.state.otp
74 })
75 }
76 )
77 .then(res => res.json())
78 .then(data => {
79 if (data.success) {
80 localStorage.setItem('storiesloggeduser', data.signuptoken);
81 localStorage.setItem('storiesloggeduserid', data.userId);
82 this.props.handleIslogged(true);
83 this.props.history.push('/');
84 }
85 if (!data.success) this.setState({ msg: data.message });
86 if (data.logintoken) {
87 localStorage.setItem('storiesloggeduser', data.logintoken);
88 localStorage.setItem('storiesloggeduserid', data.userId);
89 this.props.handleIslogged(true);
90 this.props.history.push('/');
91 }
92 })
93 }
94 else {
95 this.setState({ msg: "OTP can't be empty" })
96 }
97 };
98
99 displayPhonePage = () => {
100 return (
101 <form onSubmit={this.submitPhoneNo}>
102 <label>{this.state.invalid}</label>
103 <label>{this.state.msg}</label>
104 <label>Enter a phone number</label>
105 <input
106 type="tel"
107 name="phoneNumber"
108 placeholder="8888888888"
109 value={this.state.phoneNumber}
110 onChange={this.handleChange}
111 required
112 />
113 <input type="submit" value="NEXT"/>
114 </form>
115 )
116 }
117
118 displayOtpPage = () => {
119 return (
120 <form onSubmit={this.handleVerifyOtp}>
121 <label>{this.state.invalid}</label>
122 <label>{this.state.msg}</label>
123 <label>Enter OTP</label>
124 <input
125 type="tel"
126 name="otp"
127 placeholder="8432"
128 value={this.state.otp}
129 onChange={this.handleChange}
130 required
131 />
132 <div>
133 <button onClick={this.editPhoneNo}>Edit Phone Number </button>
134 <button onClick={this.handleSendOtp}> Resend OTP </button>
135 </div>
136 <input type="submit" value="NEXT"/>
137 </form>
138 )
139 }
140
141 render() {
142 let { dispalyPhonePage } = this.state;
143 return(
144 <>
145 {dispalyPhonePage ? this.displayPhonePage() : this.displayOtpPage()}
146 </>
147 )
148 }
149}
150export default withRouter(Auth);

I hope above document helps in better understanding of basic work flow of user signin/signup using phone number and otp in Mern stack

Thank You

More articles from Chaduvula Prasanth