Skip to content

Commit abed53d

Browse files
committed
Added confirm account functionality.
1 parent 54a883d commit abed53d

File tree

7 files changed

+190
-60
lines changed

7 files changed

+190
-60
lines changed

.env.example

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
11
MONGODB_URL=YourConnectionString
2+
# Example Connection String:-
3+
# mongodb://127.0.0.1:27017/rest-api-nodejs-mongodb
4+
# mongodb://[MongodbHost]:[PORT]/[DatabaseName]
5+
26
JWT_SECRET=YourSecret
3-
JWT_TIMEOUT_DURATION="2 hours"
7+
# Example Secret:- abcdefghijklmnopqrstuvwxyz1234567890
8+
9+
JWT_TIMEOUT_DURATION="2 hours"
10+
# You can place duration available here: https://github.com/auth0/node-jsonwebtoken#usage
11+
# Seach for "expiresIn" option on above link.
12+
13+
EMAIL_SMTP_HOST=YourSMTPHost
14+
EMAIL_SMTP_PORT=YourSMTPPort
15+
EMAIL_SMTP_USERNAME=YourSMTPUsername
16+
EMAIL_SMTP_PASSWORD=YourSMTPPassword
17+
# true for 465, false for other ports
18+
EMAIL_SMTP_SECURE=false

controllers/AuthController.js

Lines changed: 119 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const { sanitizeBody } = require("express-validator");
55
const apiResponse = require("../helpers/apiResponse");
66
const bcrypt = require("bcrypt");
77
const jwt = require("jsonwebtoken");
8+
const mailer = require("../helpers/mailer");
9+
const { constants } = require("../helpers/constants");
810

911
/**
1012
* User registration.
@@ -47,27 +49,42 @@ exports.register = [
4749
}else {
4850
//hash input password
4951
bcrypt.hash(req.body.password,10,function(err, hash) {
52+
// generate OTP for confirmation
53+
let otp = randomNumber(4);
5054
// Create User object with escaped and trimmed data
5155
var user = new UserModel(
5256
{
5357
firstName: req.body.firstName,
5458
lastName: req.body.lastName,
5559
email: req.body.email,
56-
password: hash
60+
password: hash,
61+
confirmOTP: otp
5762
}
5863
);
59-
60-
// Save user.
61-
user.save(function (err) {
62-
if (err) { return apiResponse.ErrorResponse(res, err); }
63-
let userData = {
64-
_id: user._id,
65-
firstName: user.firstName,
66-
lastName: user.lastName,
67-
email: user.email
68-
}
69-
return apiResponse.successResponseWithData(res,"Registration Success.", userData);
70-
});
64+
// Html email body
65+
let html = '<p>Please Confirm your Account.</p><p>OTP: '+otp+'</p>';
66+
// Send confirmation email
67+
mailer.send(
68+
constants.confirmEmails.from,
69+
req.body.email,
70+
'Confirm Account',
71+
html
72+
).then(function(response){
73+
// Save user.
74+
user.save(function (err) {
75+
if (err) { return apiResponse.ErrorResponse(res, err); }
76+
let userData = {
77+
_id: user._id,
78+
firstName: user.firstName,
79+
lastName: user.lastName,
80+
email: user.email
81+
}
82+
return apiResponse.successResponseWithData(res,"Registration Success.", userData);
83+
});
84+
}).catch(err => {
85+
console.log(err)
86+
return apiResponse.ErrorResponse(res,err);
87+
}) ;
7188
});
7289
}
7390
} catch (err) {
@@ -101,21 +118,31 @@ exports.login = [
101118
//Compare given password with db's hash.
102119
bcrypt.compare(req.body.password,user.password,function (err,same) {
103120
if(same){
104-
let userData = {
105-
_id: user._id,
106-
firstName: user.firstName,
107-
lastName: user.lastName,
108-
email: user.email,
121+
//Check account confirmation.
122+
if(user.isConfirmed){
123+
// Check User's account active or not.
124+
if(user.status) {
125+
let userData = {
126+
_id: user._id,
127+
firstName: user.firstName,
128+
lastName: user.lastName,
129+
email: user.email,
130+
}
131+
//Prepare JWT token for authentication
132+
const jwtPayload = userData;
133+
const jwtData = {
134+
expiresIn: process.env.JWT_TIMEOUT_DURATION,
135+
};
136+
const secret = process.env.JWT_SECRET;
137+
//Generated JWT token with Payload and secret.
138+
userData.token = jwt.sign(jwtPayload, secret, jwtData);
139+
return apiResponse.successResponseWithData(res,"Login Success.", userData);
140+
}else {
141+
return apiResponse.unauthorizedResponse(res, "Account is not active. Please contact admin.");
142+
}
143+
}else{
144+
return apiResponse.unauthorizedResponse(res, "Account is not confirmed. Please confirm your account.");
109145
}
110-
//Prepare JWT token for authentication
111-
const jwtPayload = userData;
112-
const jwtData = {
113-
expiresIn: process.env.JWT_TIMEOUT_DURATION,
114-
};
115-
const secret = process.env.JWT_SECRET;
116-
//Generated JWT token with Payload and secret.
117-
userData.token = jwt.sign(jwtPayload, secret, jwtData);
118-
return apiResponse.successResponseWithData(res,"Login Success.", userData);
119146
}else{
120147
return apiResponse.unauthorizedResponse(res, "Email or Password wrong.");
121148
}
@@ -128,4 +155,68 @@ exports.login = [
128155
} catch (err) {
129156
return apiResponse.ErrorResponse(res, err);
130157
}
131-
}];
158+
}];
159+
160+
/**
161+
* OTP generator.
162+
*
163+
* @param {intiger} length
164+
*
165+
* @returns {Interger}
166+
*/
167+
function randomNumber(length) {
168+
var text = "";
169+
var possible = "123456789";
170+
for (var i = 0; i < length; i++) {
171+
var sup = Math.floor(Math.random() * possible.length);
172+
text += i > 0 && sup == i ? "0" : possible.charAt(sup);
173+
}
174+
return Number(text);
175+
}
176+
177+
/**
178+
* Verify Confirm otp.
179+
*
180+
* @param {string} email
181+
* @param {string} otp
182+
*
183+
* @returns {Object}
184+
*/
185+
exports.verifyConfirm = [
186+
body("email").isLength({ min: 1 }).trim().withMessage("Email must be specified.")
187+
.isEmail().withMessage("Email must be a valid email address."),
188+
body("otp").isLength({ min: 1 }).trim().withMessage("OTP must be specified."),
189+
sanitizeBody("email").escape(),
190+
sanitizeBody("otp").escape(),
191+
(req, res, next) => {
192+
try {
193+
const errors = validationResult(req);
194+
if (!errors.isEmpty()) {
195+
return apiResponse.validationErrorWithData(res, "Validation Error.", errors.array());
196+
}else {
197+
var query = {email : req.body.email};
198+
UserModel.findOne(query).then(user => {
199+
if (user) {
200+
//Compare given password with db's hash.
201+
if(user.isConfirmed){
202+
//Check account confirmation.
203+
if(user.confirmOTP == req.body.otp){
204+
UserModel.findOneAndUpdate(query, {
205+
name: 'jason bourne'
206+
}, options, callback)
207+
return apiResponse.successResponseWithData(res,"Login Success.", userData);
208+
}else{
209+
return apiResponse.unauthorizedResponse(res, "Otp does not match");
210+
}
211+
}else{
212+
return apiResponse.unauthorizedResponse(res, "Account already confirmed.");
213+
}
214+
}else{
215+
return apiResponse.unauthorizedResponse(res, "Specified email not found.");
216+
}
217+
});
218+
}
219+
} catch (err) {
220+
return apiResponse.ErrorResponse(res, err);
221+
}
222+
}];

helpers/constants.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
exports.constants = {
2+
admin: {
3+
name: "admin",
4+
5+
},
6+
confirmEmails: {
7+
8+
}
9+
}

helpers/mailer.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const nodemailer = require('nodemailer');
2+
3+
// create reusable transporter object using the default SMTP transport
4+
let transporter = nodemailer.createTransport({
5+
host: process.env.EMAIL_SMTP_HOST,
6+
port: process.env.EMAIL_SMTP_PORT,
7+
//secure: process.env.EMAIL_SMTP_SECURE, // lack of ssl commented this. You can uncomment it.
8+
auth: {
9+
user: process.env.EMAIL_SMTP_USERNAME,
10+
pass: process.env.EMAIL_SMTP_PASSWORD
11+
}
12+
});
13+
14+
exports.send = function (from, to, subject, html)
15+
{
16+
// send mail with defined transport object
17+
// visit https://nodemailer.com/ for more options
18+
return transporter.sendMail({
19+
from: from, // sender address e.g. [email protected] or "Fred Foo 👻" <[email protected]>
20+
to: to, // list of receivers e.g. bar@example.com, [email protected]
21+
subject: subject, // Subject line e.g. 'Hello ✔'
22+
//text: text, // plain text body e.g. Hello world?
23+
html: html // html body e.g. '<b>Hello world?</b>'
24+
});
25+
}

models/UserModel.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ var UserSchema = new mongoose.Schema({
55
lastName: {type: String, required: true},
66
email: {type: String, required: true},
77
password: {type: String, required: true},
8+
isConfirmed: {type: Boolean, required: true, default: 0},
9+
confirmOTP: {type: String, required:false},
10+
otpTries: {type: Number, required:false, default: 0},
811
status: {type: Boolean, required: true, default: 1}
912
}, {timestamps: true});
1013

0 commit comments

Comments
 (0)