diff --git a/common/models/user.js b/common/models/user.js index 4892fc0a..8a5fdc97 100644 --- a/common/models/user.js +++ b/common/models/user.js @@ -437,18 +437,18 @@ module.exports = function(User) { * Verify a user's identity by sending them a confirmation email. * * ```js - * var options = { + * var verifyOptions = { * type: 'email', - * to: user.email, + * from: noreply@example.com, * template: 'verify.ejs', * redirect: '/', * tokenGenerator: function (user, cb) { cb("random-token"); } * }; * - * user.verify(options, next); + * user.verify(verifyOptions, options, next); * ``` * - * @options {Object} options + * @options {Object} verifyOptions * @property {String} type Must be 'email'. * @property {String} to Email address to which verification email is sent. * @property {String} from Sender email addresss, for example @@ -468,130 +468,157 @@ module.exports = function(User) { * callback function. This function should NOT add the token to the user * object, instead simply execute the callback with the token! User saving * and email sending will be handled in the `verify()` method. - * @callback {Function} fn Callback function. + * @param {Object} options remote context options. + * @callback {Function} cb Callback function. * @param {Error} err Error object. * @param {Object} object Contains email, token, uid. * @promise */ - User.prototype.verify = function(options, fn) { - fn = fn || utils.createPromiseCallback(); + User.prototype.verify = function(verifyOptions, options, cb) { + if (cb === undefined && typeof options === 'function') { + cb = options; + options = undefined; + } + cb = cb || utils.createPromiseCallback(); var user = this; var userModel = this.constructor; var registry = userModel.registry; - var pkName = userModel.definition.idName() || 'id'; - assert(typeof options === 'object', 'options required when calling user.verify()'); - assert(options.type, 'You must supply a verification type (options.type)'); - assert(options.type === 'email', 'Unsupported verification type'); - assert(options.to || this.email, - 'Must include options.to when calling user.verify() ' + - 'or the user must have an email property'); - assert(options.from, 'Must include options.from when calling user.verify()'); - options.redirect = options.redirect || '/'; + // final assertion is performed once all options are assigned + assert(typeof verifyOptions === 'object', + 'verifyOptions object param required when calling user.verify()'); + + // Set a default template generation function if none provided + verifyOptions.templateFn = verifyOptions.templateFn || createVerificationEmailBody; + // Set a default token generation function if none provided + verifyOptions.generateVerificationToken = verifyOptions.generateVerificationToken || + User.generateVerificationToken; + // Set a default mailer function if none provided + verifyOptions.mailer = verifyOptions.mailer || userModel.email || + registry.getModelByType(loopback.Email); + + var pkName = userModel.definition.idName() || 'id'; + verifyOptions.redirect = verifyOptions.redirect || '/'; var defaultTemplate = path.join(__dirname, '..', '..', 'templates', 'verify.ejs'); - options.template = path.resolve(options.template || defaultTemplate); - options.user = this; - options.protocol = options.protocol || 'http'; + verifyOptions.template = path.resolve(verifyOptions.template || defaultTemplate); + verifyOptions.user = user; + verifyOptions.protocol = verifyOptions.protocol || 'http'; var app = userModel.app; - options.host = options.host || (app && app.get('host')) || 'localhost'; - options.port = options.port || (app && app.get('port')) || 3000; - options.restApiRoot = options.restApiRoot || (app && app.get('restApiRoot')) || '/api'; + verifyOptions.host = verifyOptions.host || (app && app.get('host')) || 'localhost'; + verifyOptions.port = verifyOptions.port || (app && app.get('port')) || 3000; + verifyOptions.restApiRoot = verifyOptions.restApiRoot || (app && app.get('restApiRoot')) || '/api'; var displayPort = ( - (options.protocol === 'http' && options.port == '80') || - (options.protocol === 'https' && options.port == '443') - ) ? '' : ':' + options.port; + (verifyOptions.protocol === 'http' && verifyOptions.port == '80') || + (verifyOptions.protocol === 'https' && verifyOptions.port == '443') + ) ? '' : ':' + verifyOptions.port; var urlPath = joinUrlPath( - options.restApiRoot, + verifyOptions.restApiRoot, userModel.http.path, userModel.sharedClass.findMethodByName('confirm').http.path ); - options.verifyHref = options.verifyHref || - options.protocol + + verifyOptions.verifyHref = verifyOptions.verifyHref || + verifyOptions.protocol + '://' + - options.host + + verifyOptions.host + displayPort + urlPath + '?' + qs.stringify({ - uid: '' + options.user[pkName], - redirect: options.redirect, + uid: '' + verifyOptions.user[pkName], + redirect: verifyOptions.redirect, }); - options.templateFn = options.templateFn || createVerificationEmailBody; + verifyOptions.to = verifyOptions.to || user.email; + verifyOptions.subject = verifyOptions.subject || g.f('Thanks for Registering'); + verifyOptions.headers = verifyOptions.headers || {}; - // Email model - var Email = - options.mailer || this.constructor.email || registry.getModelByType(loopback.Email); + // assert the verifyOptions params that might have been badly defined + assertVerifyOptions(verifyOptions); - // Set a default token generation function if one is not provided - var tokenGenerator = options.generateVerificationToken || User.generateVerificationToken; - assert(typeof tokenGenerator === 'function', 'generateVerificationToken must be a function'); - - tokenGenerator(user, function(err, token) { - if (err) { return fn(err); } + // argument "options" is passed depending on verifyOptions.generateVerificationToken function requirements + var tokenGenerator = verifyOptions.generateVerificationToken; + if (tokenGenerator.length == 3) { + tokenGenerator(user, options, addTokenToUserAndSave); + } else { + tokenGenerator(user, addTokenToUserAndSave); + } + function addTokenToUserAndSave(err, token) { + if (err) return cb(err); user.verificationToken = token; - user.save(function(err) { - if (err) { - fn(err); - } else { - sendEmail(user); - } + user.save(options, function(err) { + if (err) return cb(err); + sendEmail(user); }); - }); + } // TODO - support more verification types function sendEmail(user) { - options.verifyHref += '&token=' + user.verificationToken; + verifyOptions.verifyHref += '&token=' + user.verificationToken; + verifyOptions.verificationToken = user.verificationToken; - options.verificationToken = user.verificationToken; + verifyOptions.text = verifyOptions.text || g.f('Please verify your email by opening ' + + 'this link in a web browser:\n\t%s', verifyOptions.verifyHref); - options.text = options.text || g.f('Please verify your email by opening ' + - 'this link in a web browser:\n\t%s', options.verifyHref); + verifyOptions.text = verifyOptions.text.replace(/\{href\}/g, verifyOptions.verifyHref); - options.text = options.text.replace(/\{href\}/g, options.verifyHref); + // argument "options" is passed depending on templateFn function requirements + var templateFn = verifyOptions.templateFn; + if (templateFn.length == 3) { + templateFn(verifyOptions, options, setHtmlContentAndSend); + } else { + templateFn(verifyOptions, setHtmlContentAndSend); + } - options.to = options.to || user.email; + function setHtmlContentAndSend(err, html) { + if (err) return cb(err); - options.subject = options.subject || g.f('Thanks for Registering'); + verifyOptions.html = html; - options.headers = options.headers || {}; - - options.templateFn(options, function(err, html) { - if (err) { - fn(err); - } else { - setHtmlContentAndSend(html); - } - }); - - function setHtmlContentAndSend(html) { - options.html = html; - - // Remove options.template to prevent rejection by certain + // Remove verifyOptions.template to prevent rejection by certain // nodemailer transport plugins. - delete options.template; + delete verifyOptions.template; - Email.send(options, function(err, email) { - if (err) { - fn(err); - } else { - fn(null, {email: email, token: user.verificationToken, uid: user[pkName]}); - } - }); + // argument "options" is passed depending on Email.send function requirements + var Email = verifyOptions.mailer; + if (Email.send.length == 3) { + Email.send(verifyOptions, options, handleAfterSend); + } else { + Email.send(verifyOptions, handleAfterSend); + } + + function handleAfterSend(err, email) { + if (err) return cb(err); + cb(null, {email: email, token: user.verificationToken, uid: user[pkName]}); + } } } - return fn.promise; + + return cb.promise; }; - function createVerificationEmailBody(options, cb) { - var template = loopback.template(options.template); - var body = template(options); + function assertVerifyOptions(verifyOptions) { + assert(verifyOptions.type, 'You must supply a verification type (verifyOptions.type)'); + assert(verifyOptions.type === 'email', 'Unsupported verification type'); + assert(verifyOptions.to, 'Must include verifyOptions.to when calling user.verify() ' + + 'or the user must have an email property'); + assert(verifyOptions.from, 'Must include verifyOptions.from when calling user.verify()'); + assert(typeof verifyOptions.templateFn === 'function', + 'templateFn must be a function'); + assert(typeof verifyOptions.generateVerificationToken === 'function', + 'generateVerificationToken must be a function'); + assert(verifyOptions.mailer, 'A mailer function must be provided'); + assert(typeof verifyOptions.mailer.send === 'function', 'mailer.send must be a function '); + } + + function createVerificationEmailBody(verifyOptions, options, cb) { + var template = loopback.template(verifyOptions.template); + var body = template(verifyOptions); cb(null, body); } @@ -603,9 +630,10 @@ module.exports = function(User) { * function will be called with the `user` object as it's context (`this`). * * @param {object} user The User this token is being generated for. - * @param {Function} cb The generator must pass back the new token with this function call + * @param {object} options remote context options. + * @param {Function} cb The generator must pass back the new token with this function call. */ - User.generateVerificationToken = function(user, cb) { + User.generateVerificationToken = function(user, options, cb) { crypto.randomBytes(64, function(err, buf) { cb(err, buf && buf.toString('hex')); }); diff --git a/test/user.test.js b/test/user.test.js index e7da06ec..0592b2d3 100644 --- a/test/user.test.js +++ b/test/user.test.js @@ -8,10 +8,12 @@ var assert = require('assert'); var expect = require('./helpers/expect'); var request = require('supertest'); var loopback = require('../'); -var User, AccessToken; var async = require('async'); var url = require('url'); var extend = require('util')._extend; +var Promise = require('bluebird'); + +var User, AccessToken; describe('User', function() { this.timeout(10000); @@ -30,14 +32,14 @@ describe('User', function() { var validMixedCaseEmailCredentials = {email: 'Foo@bar.com', password: 'bar'}; var invalidCredentials = {email: 'foo1@bar.com', password: 'invalid'}; var incompleteCredentials = {password: 'bar1'}; - var validCredentialsUser, validCredentialsEmailVerifiedUser; + var validCredentialsUser, validCredentialsEmailVerifiedUser, user; // Create a local app variable to prevent clashes with the global // variable shared by all tests. While this should not be necessary if // the tests were written correctly, it turns out that's not the case :( var app = null; - beforeEach(function setupAppAndModels(done) { + beforeEach(function setupAppAndModels() { // override the global app object provided by test/support.js // and create a local one that does not share state with other tests app = loopback({localRegistry: true, loadBuiltinModels: true}); @@ -90,14 +92,13 @@ describe('User', function() { app.use(loopback.token({model: AccessToken})); app.use(loopback.rest()); - User.create(validCredentials, function(err, user) { - if (err) return done(err); - validCredentialsUser = user; - User.create(validCredentialsEmailVerified, function(err, user) { - if (err) return done(err); - validCredentialsEmailVerifiedUser = user; - done(); - }); + // create 2 users: with and without verified email + return Promise.map( + [validCredentials, validCredentialsEmailVerified], + credentials => User.create(credentials) + ).then(users => { + validCredentialsUser = user = users[0]; + validCredentialsEmailVerifiedUser = users[1]; }); }); @@ -1405,21 +1406,23 @@ describe('User', function() { }); describe('Verification', function() { - describe('user.verify(options, fn)', function() { + describe('user.verify(verifyOptions, options, cb)', function() { + const ctxOptions = {testFlag: true}; + let verifyOptions; + + beforeEach(function() { + // reset verifyOptions before each test + verifyOptions = { + type: 'email', + from: 'noreply@example.org', + }; + }); + it('Verify a user\'s email address', function(done) { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '/', - protocol: ctx.req.protocol, - host: ctx.req.get('host'), - }; - - user.verify(options, function(err, result) { + user.verify(verifyOptions, function(err, result) { assert(result.email); assert(result.email.response); assert(result.token); @@ -1445,16 +1448,7 @@ describe('User', function() { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '/', - protocol: ctx.req.protocol, - host: ctx.req.get('host'), - }; - - user.verify(options) + user.verify(verifyOptions) .then(function(result) { assert(result.email); assert(result.email.response); @@ -1484,17 +1478,9 @@ describe('User', function() { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '/', - protocol: ctx.req.protocol, - host: ctx.req.get('host'), - headers: {'message-id': 'custom-header-value'}, - }; + verifyOptions.headers = {'message-id': 'custom-header-value'}; - user.verify(options, function(err, result) { + user.verify(verifyOptions, function(err, result) { assert(result.email); assert.equal(result.email.messageId, 'custom-header-value'); @@ -1516,19 +1502,11 @@ describe('User', function() { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '/', - protocol: ctx.req.protocol, - host: ctx.req.get('host'), - templateFn: function(options, cb) { - cb(null, 'custom template - verify url: ' + options.verifyHref); - }, + verifyOptions.templateFn = function(verifyOptions, cb) { + cb(null, 'custom template - verify url: ' + verifyOptions.verifyHref); }; - user.verify(options, function(err, result) { + user.verify(verifyOptions, function(err, result) { assert(result.email); assert(result.email.response); assert(result.token); @@ -1558,17 +1536,9 @@ describe('User', function() { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '/', - protocol: ctx.req.protocol, - host: ctx.req.get('host'), - templateFn: function(options, cb) { - actualVerifyHref = options.verifyHref; - cb(null, 'dummy body'); - }, + verifyOptions.templateFn = function(verifyOptions, cb) { + actualVerifyHref = verifyOptions.verifyHref; + cb(null, 'dummy body'); }; // replace the string id with an object @@ -1579,7 +1549,7 @@ describe('User', function() { }); user.pk = {toString: function() { return idString; }}; - user.verify(options, function(err, result) { + user.verify(verifyOptions, function(err, result) { expect(result.uid).to.exist().and.be.an('object'); expect(result.uid.toString()).to.equal(idString); const parsed = url.parse(actualVerifyHref, true); @@ -1602,26 +1572,18 @@ describe('User', function() { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '/', - protocol: ctx.req.protocol, - host: ctx.req.get('host'), - generateVerificationToken: function(user, cb) { - assert(user); - assert.equal(user.email, 'bar@bat.com'); - assert(cb); - assert.equal(typeof cb, 'function'); - // let's ensure async execution works on this one - process.nextTick(function() { - cb(null, 'token-123456'); - }); - }, + verifyOptions.generateVerificationToken = function(user, cb) { + assert(user); + assert.equal(user.email, 'bar@bat.com'); + assert(cb); + assert.equal(typeof cb, 'function'); + // let's ensure async execution works on this one + process.nextTick(function() { + cb(null, 'token-123456'); + }); }; - user.verify(options, function(err, result) { + user.verify(verifyOptions, function(err, result) { assert(result.email); assert(result.email.response); assert(result.token); @@ -1647,22 +1609,14 @@ describe('User', function() { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '/', - protocol: ctx.req.protocol, - host: ctx.req.get('host'), - generateVerificationToken: function(user, cb) { - // let's ensure async execution works on this one - process.nextTick(function() { - cb(new Error('Fake error')); - }); - }, + verifyOptions.generateVerificationToken = function(user, cb) { + // let's ensure async execution works on this one + process.nextTick(function() { + cb(new Error('Fake error')); + }); }; - user.verify(options, function(err, result) { + user.verify(verifyOptions, function(err, result) { assert(err); assert.equal(err.message, 'Fake error'); assert.equal(result, undefined); @@ -1686,17 +1640,12 @@ describe('User', function() { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '/', - protocol: 'http', + Object.assign(verifyOptions, { host: 'myapp.org', port: 3000, - }; + }); - user.verify(options, function(err, result) { + user.verify(verifyOptions, function(err, result) { var msg = result.email.response.toString('utf-8'); assert(~msg.indexOf('http://myapp.org:3000/')); @@ -1718,17 +1667,12 @@ describe('User', function() { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '/', - protocol: 'http', + Object.assign(verifyOptions, { host: 'myapp.org', port: 80, - }; + }); - user.verify(options, function(err, result) { + user.verify(verifyOptions, function(err, result) { var msg = result.email.response.toString('utf-8'); assert(~msg.indexOf('http://myapp.org/')); @@ -1750,17 +1694,13 @@ describe('User', function() { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '/', - protocol: 'https', + Object.assign(verifyOptions, { host: 'myapp.org', port: 3000, - }; + protocol: 'https', + }); - user.verify(options, function(err, result) { + user.verify(verifyOptions, function(err, result) { var msg = result.email.response.toString('utf-8'); assert(~msg.indexOf('https://myapp.org:3000/')); @@ -1782,17 +1722,13 @@ describe('User', function() { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '/', - protocol: 'https', + Object.assign(verifyOptions, { host: 'myapp.org', + protocol: 'https', port: 443, - }; + }); - user.verify(options, function(err, result) { + user.verify(verifyOptions, function(err, result) { var msg = result.email.response.toString('utf-8'); assert(~msg.indexOf('https://myapp.org/')); @@ -1828,17 +1764,13 @@ describe('User', function() { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - var options = { - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '/', + Object.assign(verifyOptions, { host: 'myapp.org', port: 3000, restApiRoot: '/', - }; + }); - user.verify(options, function(err, result) { + user.verify(verifyOptions, function(err, result) { if (err) return next(err); emailBody = result.email.response.toString('utf-8'); next(); @@ -1858,40 +1790,31 @@ describe('User', function() { }); }); - it('removes "options.template" from Email payload', function() { + it('removes "verifyOptions.template" from Email payload', function() { var MailerMock = { - send: function(options, cb) { cb(null, options); }, + send: function(verifyOptions, cb) { cb(null, verifyOptions); }, }; + verifyOptions.mailer = MailerMock; - return User.create({email: 'user@example.com', password: 'pass'}) - .then(function(user) { - return user.verify({ - type: 'email', - from: 'noreply@example.com', - mailer: MailerMock, - }); - }) + return user.verify(verifyOptions) .then(function(result) { expect(result.email).to.not.have.property('template'); }); }); it('allows hash fragment in redirectUrl', function() { - return User.create({email: 'test@example.com', password: 'pass'}) - .then(user => { - let actualVerifyHref; - return user.verify({ - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '#/some-path?a=1&b=2', - templateFn: (options, cb) => { - actualVerifyHref = options.verifyHref; - cb(null, 'dummy body'); - }, - }) - .then(() => actualVerifyHref); - }) + let actualVerifyHref; + + Object.assign(verifyOptions, { + redirect: '#/some-path?a=1&b=2', + templateFn: (verifyOptions, cb) => { + actualVerifyHref = verifyOptions.verifyHref; + cb(null, 'dummy body'); + }, + }); + + return user.verify(verifyOptions) + .then(() => actualVerifyHref) .then(verifyHref => { var parsed = url.parse(verifyHref, true); expect(parsed.query.redirect, 'redirect query') @@ -1899,36 +1822,107 @@ describe('User', function() { }); }); - it('verify that options.templateFn receives options.verificationToken', function() { - return User.create({email: 'test1@example.com', password: 'pass'}) - .then(user => { - let actualVerificationToken; - return user.verify({ - type: 'email', - to: user.email, - from: 'noreply@myapp.org', - redirect: '#/some-path?a=1&b=2', - templateFn: (options, cb) => { - actualVerificationToken = options.verificationToken; - cb(null, 'dummy body'); - }, - }) - .then(() => actualVerificationToken); - }) + it('verifies that verifyOptions.templateFn receives verifyOptions.verificationToken', + function() { + let actualVerificationToken; + + Object.assign(verifyOptions, { + redirect: '#/some-path?a=1&b=2', + templateFn: (verifyOptions, cb) => { + actualVerificationToken = verifyOptions.verificationToken; + cb(null, 'dummy body'); + }, + }); + + return user.verify(verifyOptions) + .then(() => actualVerificationToken) .then(token => { expect(token).to.exist(); }); }); + + it('forwards the "options" argument to user.save() ' + + 'when adding verification token', function() { + let onBeforeSaveCtx = {}; + + // before save operation hook to capture remote ctx when saving + // verification token in user instance + User.observe('before save', function(ctx, next) { + onBeforeSaveCtx = ctx || {}; + next(); + }); + + return user.verify(verifyOptions, ctxOptions) + .then(() => { + // not checking equality since other properties are added by user.save() + expect(onBeforeSaveCtx.options).to.contain({testFlag: true}); + }); + }); + + it('forwards the "options" argument to a custom templateFn function', function() { + let templateFnOptions; + + // custom templateFn function accepting the options argument + verifyOptions.templateFn = (verifyOptions, options, cb) => { + templateFnOptions = options; + cb(null, 'dummy body'); + }; + + return user.verify(verifyOptions, ctxOptions) + .then(() => { + // not checking equality since other properties are added by user.save() + expect(templateFnOptions).to.contain({testFlag: true}); + }); + }); + + it('forwards the "options" argment to a custom token generator function', function() { + let generateTokenOptions; + + // custom generateVerificationToken function accepting the options argument + verifyOptions.generateVerificationToken = (user, options, cb) => { + generateTokenOptions = options; + cb(null, 'dummy token'); + }; + + return user.verify(verifyOptions, ctxOptions) + .then(() => { + // not checking equality since other properties are added by user.save() + expect(generateTokenOptions).to.contain({testFlag: true}); + }); + }); + + it('forwards the "options" argument to a custom mailer function', function() { + let mailerOptions; + + // custom mailer function accepting the options argument + const mailer = function() {}; + mailer.send = function(verifyOptions, options, cb) { + mailerOptions = options; + cb(null, 'dummy result'); + }; + verifyOptions.mailer = mailer; + + return user.verify(verifyOptions, ctxOptions) + .then(() => { + // not checking equality since other properties are added by user.save() + expect(mailerOptions).to.contain({testFlag: true}); + }); + }); + + function givenUser() { + return User.create({email: 'test@example.com', password: 'pass'}) + .then(u => user = u); + } }); describe('User.confirm(options, fn)', function() { - var options; + var verifyOptions; function testConfirm(testFunc, done) { User.afterRemote('create', function(ctx, user, next) { assert(user, 'afterRemote should include result'); - options = { + verifyOptions = { type: 'email', to: user.email, from: 'noreply@myapp.org', @@ -1937,7 +1931,7 @@ describe('User', function() { host: ctx.req.get('host'), }; - user.verify(options, function(err, result) { + user.verify(verifyOptions, function(err, result) { if (err) return done(err); testFunc(result, done); @@ -1959,7 +1953,7 @@ describe('User', function() { request(app) .get('/test-users/confirm?uid=' + (result.uid) + '&token=' + encodeURIComponent(result.token) + - '&redirect=' + encodeURIComponent(options.redirect)) + '&redirect=' + encodeURIComponent(verifyOptions.redirect)) .expect(302) .end(function(err, res) { if (err) return done(err); @@ -2011,7 +2005,7 @@ describe('User', function() { request(app) .get('/test-users/confirm?uid=' + (result.uid + '_invalid') + '&token=' + encodeURIComponent(result.token) + - '&redirect=' + encodeURIComponent(options.redirect)) + '&redirect=' + encodeURIComponent(verifyOptions.redirect)) .expect(404) .end(function(err, res) { if (err) return done(err); @@ -2030,7 +2024,7 @@ describe('User', function() { request(app) .get('/test-users/confirm?uid=' + result.uid + '&token=' + encodeURIComponent(result.token) + '_invalid' + - '&redirect=' + encodeURIComponent(options.redirect)) + '&redirect=' + encodeURIComponent(verifyOptions.redirect)) .expect(400) .end(function(err, res) { if (err) return done(err);