├── .jscsrc ├── LICENSE ├── README.md ├── config ├── auth.js ├── database.js ├── parameters.json ├── routes.js └── server.js ├── gulpfile.js ├── index.js ├── lib ├── mail │ └── service │ │ └── mail.service.js └── user │ ├── controller │ └── auth.controller.js │ ├── entity │ └── user.js │ ├── routes.js │ └── view │ ├── register.mail.ejs │ └── resend-verification.mail.ejs ├── package.json └── test └── auth.controller.spec.js /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "google", 3 | "validateIndentation": 4, 4 | "maximumLineLength": 120, 5 | "excludeFiles": [ 6 | "node_modules/**" 7 | ] 8 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 - Marc HANIN . 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * The names of any contributors may not be used to endorse or promote 12 | products derived from this software without specific prior written 13 | permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hapi Struct 2 | === 3 | 4 | ## Description 5 | 6 | Hapi Struct is a boilerplate for Hapi framework. This boilerplate has been created to help you to create a new project with Hapi. 7 | 8 | ## Required 9 | - **NodeJS** - >= 5.3.0 - To run your app 10 | - **MongoDB** - >= 3.2.0 - NoSQL Database 11 | - **Gulp** - >= 3.9.0 - JavaScript task runner 12 | 13 | ## Project 14 | ### Structure 15 | 16 | ``` 17 | . 18 | ├── index.js * Entry point of the application 19 | ├── gulpfile.js * Gulp configuration 20 | ├── .jscsrc * Jscs configuration 21 | ├── package.json 22 | ├── config/ 23 | | └── auth.js * Authentication configuration 24 | | └── database.js * Database configuration 25 | | └── parameters.json * Application parameters 26 | | └── routes.js * Routes configuration 27 | | └── server.js * Server configuration 28 | ├── lib/ 29 | | ├── mail/ 30 | | | └── service/ 31 | | | └── mailService.js * Mail service 32 | | └── user/ 33 | | ├── controller 34 | | | └── authController.js * Authentication controller 35 | | ├── entity 36 | | | └── user.js * User entity 37 | | ├── view 38 | | | ├── registerMail.ejs * Email sent when a user registration 39 | | | └── resendVerificationMail.ejs * Email sent to resend mail verification 40 | | └── routes.js * Management of the user's route 41 | └── test/ 42 | └── authControllerSpec.js * authController tests 43 | ``` 44 | 45 | ### dependencies 46 | - **bcrypt** - ^0.8.5 - Bcrypt library for NodeJS 47 | - **boom** - ^3.1.1 - Http-friendly error objects 48 | - **ejs** - ^2.3.4 - Embedded JavaScript templates 49 | - **gulp** - ^3.9.0 - The streaming build system 50 | - **gulp-jscs** - ^3.0.2 - Check JavaScript code style with jscs 51 | - **gulp-nodemon** - ^2.0.6 - Gulp implementation for nodemon 52 | - **hapi** - ^12.0.0 - HTTP Server framework 53 | - **hapi-auth-jwt2** - ^5.3.1 - Hapi Authentication Plugin/Scheme using JWT 54 | - **joi** - ^7.1.0 - Object schema validation 55 | - **jsonwebtoken** - ^5.5.4 - JSON Web Token implementation 56 | - **moment** - ^2.11.0 - Parse, validate, manipulate and display dates 57 | - **mongoose** - ^4.3.4 - MongoDB ODM 58 | - **mongoose-auto-increment** - ^5.0.1 - auto-increment any field on any mongoose schema that you wish 59 | - **nodemailer** - ^1.11.0 - Easy as cake e-mail sending from your Node.js applications 60 | 61 | ## How to run this project 62 | 63 | ### Configuration 64 | Firstly, you need to configure the application 65 | ```JSON 66 | // config/parameters.json 67 | 68 | { 69 | "server": { 70 | "host": "127.0.0.1", 71 | "port": 3000 72 | }, 73 | "database": { 74 | "host": "127.0.0.1", 75 | "port": 27017, 76 | "db": "YourDataBaseName", 77 | "username": "YourDataBaseUserName", 78 | "password": "YourDatabasePassword" 79 | }, 80 | "key": { 81 | "privateKey": "YourPrivateKey", 82 | "tokenExpiration": 3600000, 83 | "tokenExpirationDescription": "1 hour" 84 | }, 85 | "mail": { 86 | "email": "senderMail@website.com", 87 | "userName": "YourMailAccount", 88 | "password": "YourMailPassword" 89 | } 90 | } 91 | ``` 92 | 93 | ### Install dependencies 94 | ```sh 95 | npm install 96 | ``` 97 | 98 | ### Run the application 99 | ```sh 100 | npm start 101 | ``` 102 | 103 | ### Run tests 104 | ```sh 105 | npm test 106 | ``` 107 | -------------------------------------------------------------------------------- /config/auth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Dependencies 4 | const HapiAuthJwt = require('hapi-auth-jwt2'); 5 | const Parameters = require('./parameters'); 6 | const Moment = require('moment'); 7 | const User = require('../lib/user/entity/user'); 8 | 9 | function validate(decoded, token, cb) { 10 | let ttl = Parameters.key.tokenExpiration; 11 | let diff = Moment().diff(Moment(token.iat * 1000)); 12 | 13 | if (decoded.authLink) { 14 | if (diff > ttl) { 15 | return cb(null, false); 16 | } 17 | 18 | User.findOne({_id: decoded.id}, (err, user) => { 19 | if (err) { 20 | return cb(err, false); 21 | } else if (!user) { 22 | return cb(null, false); 23 | } else if (user.isVerified) { 24 | return cb(null, true, user); 25 | } else { 26 | return cb(null, false); 27 | } 28 | }); 29 | } else { 30 | return cb(null, false); 31 | } 32 | } 33 | 34 | // Auth jwt plugin 35 | const register = (server, options, next) => { 36 | server.register(HapiAuthJwt, (err) => { 37 | if (err) { 38 | return next(err); 39 | } 40 | 41 | server.auth.strategy('jwt', 'jwt', { 42 | key: Parameters.key.privateKey, 43 | validateFunc: validate 44 | }); 45 | 46 | return next(); 47 | }); 48 | }; 49 | 50 | register.attributes = { 51 | name: 'auth-jwt', 52 | version: '1.0.0' 53 | }; 54 | 55 | module.exports = register; 56 | -------------------------------------------------------------------------------- /config/database.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Dependencies 4 | const Mongoose = require('mongoose'); 5 | 6 | // Configurations 7 | const parameters = require('./parameters.json'); 8 | 9 | Mongoose.connect('mongodb://' + parameters.database.host + '/' + parameters.database.db); 10 | 11 | const db = Mongoose.connection; 12 | db.on('error', console.error.bind(console, 'Connection error')); 13 | db.once('open', () => { 14 | console.log('Connection with database succeeded'); 15 | }); 16 | 17 | module.exports = db; 18 | -------------------------------------------------------------------------------- /config/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "host": "127.0.0.1", 4 | "port": 3000 5 | }, 6 | "database": { 7 | "host": "127.0.0.1", 8 | "port": 27017, 9 | "db": "YourDataBaseName", 10 | "username": "YourDataBaseUserName", 11 | "password": "YourDatabasePassword" 12 | }, 13 | "key": { 14 | "privateKey": "YourPrivateKey", 15 | "tokenExpiration": 3600000, 16 | "tokenExpirationDescription": "1 hour" 17 | }, 18 | "mail": { 19 | "email": "senderMail@website.com", 20 | "userName": "YourMailAccount", 21 | "password": "YourMailPassword" 22 | }, 23 | "tests": { 24 | "facticeUser": { 25 | "email": "senderMail@website.com", 26 | "password": "YourPassword" 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /config/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const UserRoutes = require('../lib/user/routes'); 4 | let routes = []; 5 | 6 | module.exports = routes.concat(UserRoutes); 7 | 8 | -------------------------------------------------------------------------------- /config/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Dependencies 4 | const Hapi = require('hapi'); 5 | 6 | // Configurations files 7 | const parameters = require('./parameters'); 8 | const auth = require('./auth'); 9 | const routes = require('./routes'); 10 | 11 | const server = new Hapi.Server(); 12 | const plugins = []; 13 | 14 | server.connection({ 15 | host: parameters.server.host, 16 | port: parameters.server.port 17 | }); 18 | 19 | // Plugins management 20 | plugins.push({register: auth}); 21 | server.register(plugins, (err) => { 22 | if (err) { 23 | throw err; 24 | } 25 | console.log('server plugins was successfull loaded'); 26 | }); 27 | 28 | server.route(routes); 29 | 30 | module.exports = server; 31 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const Gulp = require('gulp'); 2 | const Jscs = require('gulp-jscs'); 3 | const Nodemon = require('gulp-nodemon'); 4 | 5 | const jsFiles = [ 6 | "config/**/*.js", 7 | "lib/**/*.js", 8 | "index.js" 9 | ]; 10 | 11 | Gulp.task('jscs', () => { 12 | return Gulp.src(jsFiles) 13 | .pipe(Jscs()) 14 | .pipe(Jscs.reporter()) 15 | .pipe(Jscs.reporter('fail')); 16 | }); 17 | 18 | Gulp.task('start', ['jscs'] ,() => { 19 | Nodemon({ 20 | script: 'index.js', 21 | ext: 'js html', 22 | tasks: ['jscs'] 23 | }) 24 | }); 25 | 26 | Gulp.task('default', ['start']); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let server = require('./config/server'); 4 | 5 | server.start((err) => { 6 | if (err) { 7 | throw err; 8 | } 9 | console.log('Server running at ' + server.info.uri); 10 | }); 11 | -------------------------------------------------------------------------------- /lib/mail/service/mail.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Fs = require('fs'); 4 | const NodeMailer = require('nodemailer'); 5 | const Parameters = require('../../../config/parameters').mail; 6 | const Ejs = require('ejs'); 7 | 8 | const gmailTransport = NodeMailer.createTransport({ 9 | host: 'smtp.gmail.com', 10 | port: 25, 11 | auth: { 12 | user: Parameters.userName, 13 | pass: Parameters.password 14 | } 15 | }); 16 | 17 | exports.getMailTemplate = (path) => { 18 | return Fs.readFileSync(path, 'utf8'); 19 | }; 20 | 21 | exports.sendHtmlEmail = (subject, templateFile, email, datas) => { 22 | let template = Ejs.compile(templateFile.toString()); 23 | let mailOptions = { 24 | from: Parameters.email, 25 | to: email, 26 | subject: subject, 27 | html: template(datas) 28 | }; 29 | 30 | gmailTransport.sendMail(mailOptions, (err, res) => { 31 | if (err) { 32 | console.log(err); 33 | throw err; 34 | } 35 | gmailTransport.close(); 36 | }); 37 | }; 38 | -------------------------------------------------------------------------------- /lib/user/controller/auth.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Joi = require('joi'); 4 | const Boom = require('boom'); 5 | const User = require('../entity/user'); 6 | const Jwt = require('jsonwebtoken'); 7 | const Parameters = require('../../../config/parameters'); 8 | const MailService = require('../../mail/service/mail.service'); 9 | const Bcrypt = require('bcrypt'); 10 | const Moment = require('moment'); 11 | 12 | const _privateKey = Parameters.key.privateKey; 13 | 14 | exports.signup = { 15 | description: 'User registration', 16 | validate: { 17 | payload: { 18 | email: Joi.string().email().required(), 19 | password: Joi.string().required() 20 | } 21 | }, 22 | handler: (request, reply) => { 23 | let email = request.payload.email; 24 | let password = request.payload.password; 25 | Bcrypt.genSalt(10, (err, salt) => { 26 | if (err) { 27 | return reply(Boom.internal()); 28 | } 29 | Bcrypt.hash(password, salt, (err, hash) => { 30 | if (err) { 31 | return reply(Boom.internal()); 32 | } 33 | let user = new User({ 34 | email: email, 35 | password: hash 36 | }); 37 | user.save((err, user) => { 38 | if (!err) { 39 | let tokenData = { 40 | email: user.email, 41 | scope: [user.scope], 42 | id: user._id 43 | }; 44 | let token = Jwt.sign(tokenData, _privateKey); 45 | try { 46 | let templateFile = MailService.getMailTemplate('./lib/user/view/register.mail.ejs'); 47 | MailService 48 | .sendHtmlEmail('Welcome on Hapi-Struct', templateFile, user.email, {token: token}); 49 | return reply({message: 'Please confirm your email address'}); 50 | } catch (e) { 51 | console.log(e); 52 | return reply(Boom.internal()); 53 | } 54 | } else if (11000 === err.code || 11001 === err.code) { 55 | return reply(Boom.forbidden('please provide another user email')); 56 | } else { 57 | return reply(Boom.forbidden(err)); 58 | } 59 | }); 60 | }); 61 | }); 62 | } 63 | }; 64 | 65 | exports.emailConfirmationHandle = { 66 | description: 'User email confirmation', 67 | validate: { 68 | query: { 69 | token: Joi.string().required() 70 | } 71 | }, 72 | handler: (request, reply) => { 73 | let token = request.query.token; 74 | Jwt.verify(token, _privateKey, (err, decoded) => { 75 | if (decoded === undefined) { 76 | return reply(Boom.badRequest('Invalid verification link')); 77 | } 78 | let ttl = Parameters.key.tokenExpiration; 79 | let diff = Moment().diff(Moment(decoded.iat * 1000)); 80 | if (diff > ttl) { 81 | return reply(Boom.badRequest('The token expired')); 82 | } else if (decoded.id) { 83 | User.findOne({_id: decoded.id}, (err, user) => { 84 | if (err) { 85 | return reply(Boom.internal()); 86 | } else if (!user) { 87 | return reply(Boom.badRequest('Invalid verification link')); 88 | } else { 89 | user.isVerified = true; 90 | user.save((err) => { 91 | if (err) { 92 | return reply(Boom.internal()); 93 | } else { 94 | return reply({message: 'Your account has been verified'}); 95 | } 96 | }); 97 | } 98 | }); 99 | } else { 100 | return reply(Boom.badRequest('Invalid verification link')); 101 | } 102 | }); 103 | } 104 | }; 105 | 106 | exports.signin = { 107 | description: 'User signin process', 108 | validate: { 109 | payload: { 110 | email: Joi.string().email().required(), 111 | password: Joi.string().required() 112 | } 113 | }, 114 | handler: (request, reply) => { 115 | let email = request.payload.email; 116 | let password = request.payload.password; 117 | 118 | User.findOne({email: email}, (err, user) => { 119 | if (err) { 120 | return reply(Boom.internal('Error retrieving user')); 121 | } 122 | if (user && user.isVerified) { 123 | Bcrypt.compare(password, user.password, (err, res) => { 124 | if (err) { 125 | return reply(Boom.internal('Bcrypt comparison error')); 126 | } 127 | if (res) { 128 | let tokenData = { 129 | email: user.email, 130 | scope: [user.scope], 131 | id: user._id, 132 | authLink: true 133 | }; 134 | let token = Jwt.sign(tokenData, _privateKey); 135 | return reply({token: token}); 136 | } else { 137 | return reply(Boom.badRequest('Bad credentials')); 138 | } 139 | }); 140 | } else if (user && !user.isVerified) { 141 | return reply(Boom.forbidden('You must verify your email address')); 142 | } else { 143 | return reply(Boom.badRequest('Bad credentials')); 144 | } 145 | }); 146 | } 147 | }; 148 | 149 | exports.resendVerification = { 150 | description: 'Resend email verification link', 151 | validate: { 152 | payload: { 153 | email: Joi.string().email().required() 154 | } 155 | }, 156 | handler: (request, reply) => { 157 | let email = request.payload.email; 158 | 159 | User.findOne({email: email}, (err, user) => { 160 | if (err) { 161 | return reply(Boom.internal('Error retrieving user')); 162 | } 163 | if (user && !user.isVerified) { 164 | let tokenData = { 165 | email: user.email, 166 | scope: [user.scope], 167 | id: user.id 168 | }; 169 | let token = Jwt.sign(tokenData, _privateKey); 170 | try { 171 | let templateFile = MailService.getMailTemplate('./lib/user/view/resend-verification.mail.ejs'); 172 | MailService.sendHtmlEmail('Verification link', templateFile, user.email, {token: token}); 173 | return reply({ 174 | message: 'Verification link was resent' 175 | }); 176 | } catch (e) { 177 | console.log(e); 178 | return reply(Boom.internal()); 179 | } 180 | } else if (!user) { 181 | return reply(Boom.notFound('No user with this email was found')); 182 | } else { 183 | return reply( 184 | Boom.badRequest('Email is already verified')); 185 | } 186 | }); 187 | } 188 | }; 189 | -------------------------------------------------------------------------------- /lib/user/entity/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Dependencies 4 | const Mongoose = require('mongoose'); 5 | const AutoIncrement = require('mongoose-auto-increment'); 6 | const Database = require('../../../config/database'); 7 | 8 | AutoIncrement.initialize(Database); 9 | 10 | const UserSchema = new Mongoose.Schema({ 11 | email: { 12 | type: String, 13 | unique: true, 14 | required: true 15 | }, 16 | password: { 17 | type: String, 18 | required: true 19 | }, 20 | scope: { 21 | type: String, 22 | enum: ['Customer'], 23 | default: ['Customer'], 24 | required: true 25 | }, 26 | isVerified: { 27 | type: Boolean, 28 | default: false 29 | } 30 | }); 31 | 32 | UserSchema.plugin(AutoIncrement.plugin, { 33 | model: 'User', 34 | field: '_id' 35 | }); 36 | 37 | module.exports = Mongoose.model('user', UserSchema); 38 | -------------------------------------------------------------------------------- /lib/user/routes.js: -------------------------------------------------------------------------------- 1 | const AuthController = require('./controller/auth.controller'); 2 | 3 | module.exports = [ 4 | {path: '/api/auth/signup', method: 'POST', config: AuthController.signup}, 5 | {path: '/api/auth/confirmation', method: 'GET', config: AuthController.emailConfirmationHandle}, 6 | {path: '/api/auth/signin', method: 'POST', config: AuthController.signin}, 7 | {path: '/api/auth/resendverification', method: 'POST', config: AuthController.resendVerification} 8 | ]; 9 | -------------------------------------------------------------------------------- /lib/user/view/register.mail.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Welcome email 5 | 6 | 7 | 8 |

Welcole on Hapi-Struct

9 |

10 | You can confirm your account with this link 11 |

12 | 13 | -------------------------------------------------------------------------------- /lib/user/view/resend-verification.mail.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Welcome email 5 | 6 | 7 | 8 |

Verification Link

9 |

10 | You can confirm your account with this link 11 |

12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hapi-struct", 3 | "version": "1.0.1", 4 | "description": "Boilerplate for Hapi api", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/mocha/bin/mocha", 8 | "start": "./node_modules/gulp/bin/gulp.js" 9 | }, 10 | "author": "Marc Hanin ", 11 | "license": "MIT", 12 | "dependencies": { 13 | "bcrypt": "^0.8.5", 14 | "boom": "^3.1.1", 15 | "ejs": "^2.3.4", 16 | "gulp": "^3.9.0", 17 | "gulp-jscs": "^3.0.2", 18 | "gulp-nodemon": "^2.0.6", 19 | "hapi": "^12.0.0", 20 | "hapi-auth-jwt2": "^5.3.1", 21 | "joi": "^7.1.0", 22 | "jsonwebtoken": "^5.5.4", 23 | "moment": "^2.11.0", 24 | "mongoose": "^4.3.4", 25 | "mongoose-auto-increment": "^5.0.1", 26 | "nodemailer": "^1.11.0" 27 | }, 28 | "devDependencies": { 29 | "chai": "^3.4.1", 30 | "mocha": "^2.3.4", 31 | "superagent": "^1.6.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/auth.controller.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Assert = require('chai').assert; 4 | const Parameters = require('../config/parameters'); 5 | const Server = require('../config/server'); 6 | const Superagent = require('superagent'); 7 | const Jwt = require('jsonwebtoken'); 8 | const User = require('../lib/user/entity/user'); 9 | 10 | const _serverPath = "http://localhost:" + Parameters.server.port; 11 | const _privateKey = Parameters.key.privateKey; 12 | const _facticeUser = { 13 | email: Parameters.tests.facticeUser.email, 14 | password: Parameters.tests.facticeUser.password 15 | }; 16 | 17 | describe('auth controller', () => { 18 | 19 | before((done) => { 20 | User.findOne({email: _facticeUser.email}, (err, user) => { 21 | if (err) { 22 | throw err; 23 | } 24 | if (user) { 25 | user.remove((err) => { 26 | if (err) { 27 | throw err; 28 | } 29 | return done(); 30 | }) 31 | } else { 32 | return done(); 33 | } 34 | }); 35 | }); 36 | 37 | beforeEach((done) => { 38 | Server.start((err) => { 39 | return done(err); 40 | }); 41 | }); 42 | 43 | describe('Signup', () => { 44 | it('signup', (done) => { 45 | Superagent 46 | .post(_serverPath + "/api/auth/signup") 47 | .send({email: _facticeUser.email, password: _facticeUser.password}) 48 | .end((err, res) => { 49 | if (err) { 50 | throw err; 51 | } 52 | Assert.equal(res.status, 200); 53 | return done(); 54 | }); 55 | }); 56 | }); 57 | 58 | describe('Resend verification', () => { 59 | it('resend verification', (done) => { 60 | Superagent 61 | .post(_serverPath + "/api/auth/resendverification") 62 | .send({email: _facticeUser.email}) 63 | .end((err, res) => { 64 | if (err) { 65 | throw err; 66 | } 67 | Assert.equal(res.status, 200); 68 | return done(); 69 | }); 70 | }); 71 | }); 72 | 73 | describe('Signin', () => { 74 | it('Bad signin', (done) => { 75 | Superagent 76 | .post(_serverPath + "/api/auth/signin") 77 | .send({email: 'ahdezferzzergergezrg@nodejs.com', password: 'badPassword'}) 78 | .end((err, res) => { 79 | if (err) { 80 | Assert.equal(res.status, 400); 81 | Assert.equal(err.message, 'Bad Request'); 82 | return done(); 83 | } 84 | return done(new Error("No error for bad signin")); 85 | }); 86 | }); 87 | 88 | it('Email not validated', (done) => { 89 | Superagent 90 | .post(_serverPath + "/api/auth/signin") 91 | .send({email: _facticeUser.email, password: _facticeUser.password}) 92 | .end((err, res) => { 93 | if (err) { 94 | Assert.equal(res.status, 403); 95 | Assert.equal(err.message, 'Forbidden'); 96 | return done(); 97 | } 98 | return done(new Error("No error for email not validated")); 99 | }); 100 | }); 101 | }); 102 | 103 | describe('Verification', () => { 104 | it('Email verification', (done) => { 105 | User.findOne({email: _facticeUser.email}, (err, user) => { 106 | if (err) { 107 | throw err; 108 | } 109 | if (!user) { 110 | return done(new Error("No user found")); 111 | } else { 112 | let tokenData = { 113 | email: user.email, 114 | scope: [user.scope], 115 | id: user._id 116 | }; 117 | let token = Jwt.sign(tokenData, _privateKey); 118 | Superagent 119 | .get(_serverPath + "/api/auth/confirmation") 120 | .query({token: token}) 121 | .end((err, res) => { 122 | Assert.equal(res.status, 200); 123 | return done(); 124 | }); 125 | } 126 | }); 127 | }); 128 | 129 | it('Bad token for email validation', (done) => { 130 | Superagent 131 | .get(_serverPath + "/api/auth/confirmation") 132 | .query({token: "MyBeautifulBadToken"}) 133 | .end((err, res) => { 134 | if (err) { 135 | Assert.equal(res.status, 400); 136 | return done(); 137 | } 138 | return done(new Error("No error for bad token")); 139 | }); 140 | }); 141 | 142 | it('No token for email validation', (done) => { 143 | Superagent 144 | .get(_serverPath + "/api/auth/confirmation") 145 | .end((err, res) => { 146 | if (err) { 147 | Assert.equal(res.status, 400); 148 | return done(); 149 | } 150 | return done(new Error("No error for empty token")); 151 | }); 152 | }); 153 | }); 154 | 155 | afterEach((done) => { 156 | Server.stop((err) => { 157 | return done(err); 158 | }); 159 | }); 160 | 161 | after((done) => { 162 | User.findOne({email: _facticeUser.email}, (err, user) => { 163 | if (err) { 164 | throw err; 165 | } 166 | if (user) { 167 | user.remove((err) => { 168 | if (err) { 169 | throw err; 170 | } 171 | return done(); 172 | }) 173 | } else { 174 | return done(); 175 | } 176 | }); 177 | }); 178 | }); 179 | --------------------------------------------------------------------------------