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
1bin2controllers3models4modules5node_modules6public7views8app.js9package.json10package-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();910//setup your mongo database address here11var mongoServer = "mongodb+srv://***********************.mongodb.net/test?retryWrites=true&w=majority";1213// connect your mongo14mongoose.connect(15 mongoServer,16 {17 useNewUrlParser: true,18 useUnifiedTopology: true,19 useFindAndModify: false,20 useCreateIndex: true21 },22 err => {23 console.log(err ? err : "mongodb connected");24 }25);2627// view engine setup28app.set("views", path.join(__dirname, "views"));29app.set("view engine", "ejs");3031// routes32var usersRouter = require("./routes/users");3334// middleware35app.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")));4243// routes middleware44app.use("/api/v1/users", usersRouter);4546// catch 404 and forward to error handler47app.use(function(req, res, next) {48 next(createError(404));49});5051// error handler52app.use(function(err, req, res, next) {53 // set locals, only providing error in development54 res.locals.message = err.message;55 res.locals.error = req.app.get("env") === "development" ? err : {};5657 // render the error page58 res.status(err.status || 500);59 res.json({ err });60});6162module.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;34var userSchema = new Schema(5 {6 name: {7 type: String8 },9 phoneNumber: {10 type: Number,11 unique: true,12 required: true13 },14 },15 { timestamps: true }16);1718module.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");45// pass your msg91 otp creditials SendOtp6const sendOtp = new SendOtp("****otpcredentials****");78// send otp for sending otp to entered phone number and also pass message sender name like app name from your credintials9const 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}1718// verify otp to verify entered otp matched with sentotp or not19const 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 signup28 User.create(req.body, (err, user) => {29 if (err) return res.json({ err });30 jwt.sign(31 {32 userId: user._id,33 phoneNumber: user.phoneNumber34 },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 signin50 jwt.sign(51 {52 userId: user._id,53 phoneNumber: user.phoneNumber54 },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();56// send otp7router.post("/sendotp", userController.SENDOTP);89// verify otp10router.post("/verifyotp", userController.VERIFYOTP);1112module.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';45class Auth extends React.Component {6 constructor() {7 super();8 this.state = {9 dispalyPhonePage: true,10 phoneNumber: '',11 otp: '',12 invalid: '',13 msg: ''14 };15 }1617 handleChange = event => {18 this.setState({ invalid: '', msg: '' });19 let { name, value } = event.target;20 this.setState({ [name]: value });21 };2223 editPhoneNo = () => {24 this.setState({ dispalyPhonePage: true });25 };2627 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.phoneNumber37 })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 };4950 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 };6162 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.otp74 })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 };9899 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 <input106 type="tel"107 name="phoneNumber"108 placeholder="8888888888"109 value={this.state.phoneNumber}110 onChange={this.handleChange}111 required112 />113 <input type="submit" value="NEXT"/>114 </form>115 )116 }117118 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 <input125 type="tel"126 name="otp"127 placeholder="8432"128 value={this.state.otp}129 onChange={this.handleChange}130 required131 />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 }140141 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