From e7321a2e60d0d30db290bf2b52998060b22bbf4d Mon Sep 17 00:00:00 2001 From: Wilson Hobbs Date: Wed, 8 Feb 2017 08:29:38 -0500 Subject: [PATCH 1/7] Add connection options to passwordless --- src/auth/PasswordlessAuthenticator.js | 90 +++++++++++++-------------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/src/auth/PasswordlessAuthenticator.js b/src/auth/PasswordlessAuthenticator.js index bb644f054..d4dcf67ce 100644 --- a/src/auth/PasswordlessAuthenticator.js +++ b/src/auth/PasswordlessAuthenticator.js @@ -1,8 +1,7 @@ -var extend = require('util')._extend; - -var ArgumentError = require('../exceptions').ArgumentError; -var RestClient = require('rest-facade').Client; +var extend = require('util')._extend +var ArgumentError = require('../exceptions').ArgumentError +var RestClient = require('rest-facade').Client /** * @class @@ -17,20 +16,19 @@ var RestClient = require('rest-facade').Client; */ var PasswordlessAuthenticator = function (options, oauth) { if (!options) { - throw new ArgumentError('Missing authenticator options'); + throw new ArgumentError('Missing authenticator options') } if (typeof options !== 'object') { - throw new ArgumentError('The authenticator options must be an object'); + throw new ArgumentError('The authenticator options must be an object') } - var baseUrl = options.baseUrl + '/passwordless/start'; - - this.oauth = oauth; - this.passwordless = new RestClient(baseUrl); - this.clientId = options.clientId; -}; + var baseUrl = options.baseUrl + '/passwordless/start' + this.oauth = oauth + this.passwordless = new RestClient(baseUrl) + this.clientId = options.clientId +} /** * Sign in with the given user credentials. @@ -75,30 +73,30 @@ var PasswordlessAuthenticator = function (options, oauth) { PasswordlessAuthenticator.prototype.signIn = function (userData, cb) { var defaultFields = { client_id: this.clientId - }; - var data = extend(defaultFields, userData); + } + var data = extend(defaultFields, userData) // Don't let the user override the connection nor the grant type. - data.connection = 'sms'; - data.grant_type = 'password'; + if (!data.connection) data.connection = 'sms' + + data.grant_type = 'password' if (!userData || typeof userData !== 'object') { - throw new ArgumentError('Missing user data object'); + throw new ArgumentError('Missing user data object') } if (typeof data.username !== 'string' - || data.username.trim().length === 0) { - throw new ArgumentError('username field (phone number) is required'); + || data.username.trim().length === 0) { + throw new ArgumentError('username field (phone number) is required') } if (typeof data.password !== 'string' - || data.password.trim().length === 0) { - throw new ArgumentError('password field (verification code) is required'); + || data.password.trim().length === 0) { + throw new ArgumentError('password field (verification code) is required') } - return this.oauth.signIn(data, cb); -}; - + return this.oauth.signIn(data, cb) +} /** * Start passwordless flow sending an email. @@ -148,33 +146,32 @@ PasswordlessAuthenticator.prototype.signIn = function (userData, cb) { PasswordlessAuthenticator.prototype.sendEmail = function (userData, cb) { var defaultFields = { client_id: this.clientId - }; - var data = extend(defaultFields, userData); + } + var data = extend(defaultFields, userData) // Don't let the user override the connection nor the grant type. - data.connection = 'email'; + data.connection = 'email' if (!userData || typeof userData !== 'object') { - throw new ArgumentError('Missing user data object'); + throw new ArgumentError('Missing user data object') } if (typeof data.email !== 'string' - || data.email.trim().length === 0) { - throw new ArgumentError('email field is required'); + || data.email.trim().length === 0) { + throw new ArgumentError('email field is required') } if (typeof data.send !== 'string' - || data.send.trim().length === 0) { - throw new ArgumentError('send field is required'); + || data.send.trim().length === 0) { + throw new ArgumentError('send field is required') } if (cb && cb instanceof Function) { - return this.passwordless.create(data, cb); + return this.passwordless.create(data, cb) } - return this.passwordless.create(data); -}; - + return this.passwordless.create(data) +} /** * Start passwordless flow sending an SMS. @@ -209,27 +206,26 @@ PasswordlessAuthenticator.prototype.sendEmail = function (userData, cb) { PasswordlessAuthenticator.prototype.sendSMS = function (userData, cb) { var defaultFields = { client_id: this.clientId - }; - var data = extend(defaultFields, userData); + } + var data = extend(defaultFields, userData) // Don't let the user override the connection nor the grant type. - data.connection = 'sms'; + data.connection = 'sms' if (!userData || typeof userData !== 'object') { - throw new ArgumentError('Missing user data object'); + throw new ArgumentError('Missing user data object') } if (typeof data.phone_number !== 'string' - || data.phone_number.trim().length === 0) { - throw new ArgumentError('phone_number field is required'); + || data.phone_number.trim().length === 0) { + throw new ArgumentError('phone_number field is required') } if (cb && cb instanceof Function) { - return this.passwordless.create(data, cb); + return this.passwordless.create(data, cb) } - return this.passwordless.create(data); -}; - + return this.passwordless.create(data) +} -module.exports = PasswordlessAuthenticator; +module.exports = PasswordlessAuthenticator From cde1e6794ce16d5eec36346b52099feb41933b05 Mon Sep 17 00:00:00 2001 From: Wilson Hobbs Date: Wed, 8 Feb 2017 08:33:12 -0500 Subject: [PATCH 2/7] Adds validation to connection --- src/auth/PasswordlessAuthenticator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/PasswordlessAuthenticator.js b/src/auth/PasswordlessAuthenticator.js index d4dcf67ce..8e2b2aeb2 100644 --- a/src/auth/PasswordlessAuthenticator.js +++ b/src/auth/PasswordlessAuthenticator.js @@ -77,7 +77,7 @@ PasswordlessAuthenticator.prototype.signIn = function (userData, cb) { var data = extend(defaultFields, userData) // Don't let the user override the connection nor the grant type. - if (!data.connection) data.connection = 'sms' + if (!data.connection || (data.connection !== 'email' && data.connection !== 'sms')) data.connection = 'sms' data.grant_type = 'password' From f7613de556a35df821034c84ea95ca19a1eb416b Mon Sep 17 00:00:00 2001 From: Wilson Hobbs Date: Wed, 8 Feb 2017 08:43:38 -0500 Subject: [PATCH 3/7] Added email tests --- src/auth/PasswordlessAuthenticator.js | 2 +- test/auth/passwordless.tests.js | 495 +++++++++++++------------- 2 files changed, 240 insertions(+), 257 deletions(-) diff --git a/src/auth/PasswordlessAuthenticator.js b/src/auth/PasswordlessAuthenticator.js index 8e2b2aeb2..6e1de1642 100644 --- a/src/auth/PasswordlessAuthenticator.js +++ b/src/auth/PasswordlessAuthenticator.js @@ -77,7 +77,7 @@ PasswordlessAuthenticator.prototype.signIn = function (userData, cb) { var data = extend(defaultFields, userData) // Don't let the user override the connection nor the grant type. - if (!data.connection || (data.connection !== 'email' && data.connection !== 'sms')) data.connection = 'sms' + if (!data.connection || (data.connection !== 'email' && data.connection !== 'sms')) { data.connection = 'sms' } data.grant_type = 'password' diff --git a/test/auth/passwordless.tests.js b/test/auth/passwordless.tests.js index 9d6eed319..de4f95b44 100644 --- a/test/auth/passwordless.tests.js +++ b/test/auth/passwordless.tests.js @@ -1,622 +1,605 @@ -var expect = require('chai').expect; -var extend = require('util')._extend; -var nock = require('nock'); +var expect = require('chai').expect +var extend = require('util')._extend +var nock = require('nock') // Constants. -var SRC_DIR = '../../src'; -var DOMAIN = 'tenant.auth0.com'; -var API_URL = 'https://' + DOMAIN; -var CLIENT_ID = 'TEST_CLIENT_ID'; +var SRC_DIR = '../../src' +var DOMAIN = 'tenant.auth0.com' +var API_URL = 'https://' + DOMAIN +var CLIENT_ID = 'TEST_CLIENT_ID' -var ArgumentError = require(SRC_DIR + '/exceptions').ArgumentError; -var Authenticator = require(SRC_DIR + '/auth/PasswordlessAuthenticator'); -var OAuth = require(SRC_DIR + '/auth/OAuthAuthenticator'); +var ArgumentError = require(SRC_DIR + '/exceptions').ArgumentError +var Authenticator = require(SRC_DIR + '/auth/PasswordlessAuthenticator') +var OAuth = require(SRC_DIR + '/auth/OAuthAuthenticator') var validOptions = { baseUrl: API_URL, clientId: CLIENT_ID -}; - +} describe('PasswordlessAuthenticator', function () { - afterEach(function () { - nock.cleanAll(); - }); - + nock.cleanAll() + }) describe('#constructor', function () { it('should require an options object', function () { expect(Authenticator) - .to.throw(ArgumentError, 'Missing authenticator options'); + .to.throw(ArgumentError, 'Missing authenticator options') expect(Authenticator.bind(null, 1)) - .to.throw(ArgumentError, 'The authenticator options must be an object'); + .to.throw(ArgumentError, 'The authenticator options must be an object') expect(Authenticator.bind(null, validOptions)) - .to.not.throw(ArgumentError); - }); - }); - + .to.not.throw(ArgumentError) + }) + }) describe('instance', function () { - var methods = ['signIn', 'sendEmail', 'sendSMS']; - var oauth = new OAuth(validOptions); - var authenticator = new Authenticator(validOptions, oauth); + var methods = ['signIn', 'sendEmail', 'sendSMS'] + var oauth = new OAuth(validOptions) + var authenticator = new Authenticator(validOptions, oauth) methods.forEach(function (method) { it('should have a ' + method + ' method', function () { expect(authenticator[method]) .to.exist - .to.be.an.instanceOf(Function); - }); - }); - }); - + .to.be.an.instanceOf(Function) + }) + }) + }) describe('#signIn', function () { - var path = '/oauth/ro'; + var path = '/oauth/ro' var userData = { username: 'username', password: 'pwd' - }; + } beforeEach(function () { - var oauth = new OAuth(validOptions); - this.authenticator = new Authenticator(validOptions, oauth); + var oauth = new OAuth(validOptions) + this.authenticator = new Authenticator(validOptions, oauth) this.request = nock(API_URL) .post(path) - .reply(200); - }); - + .reply(200) + }) it('should require an object as first argument', function () { expect(this.authenticator.signIn) - .to.throw(ArgumentError, 'Missing user data object'); - }); - + .to.throw(ArgumentError, 'Missing user data object') + }) it('should require a phone number', function () { - var auth = this.authenticator; - var userData = { password: 'password' }; - var signIn = auth.signIn.bind(auth, userData); + var auth = this.authenticator + var userData = { password: 'password' } + var signIn = auth.signIn.bind(auth, userData) expect(signIn) - .to.throw(ArgumentError, 'username field (phone number) is required'); - }); - + .to.throw(ArgumentError, 'username field (phone number) is required') + }) it('should require a verification code', function () { - var auth = this.authenticator; - var userData = { username: 'username' }; - var signIn = auth.signIn.bind(auth, userData); + var auth = this.authenticator + var userData = { username: 'username' } + var signIn = auth.signIn.bind(auth, userData) expect(signIn) - .to.throw(ArgumentError, 'password field (verification code) is required'); - }); - + .to.throw(ArgumentError, 'password field (verification code) is required') + }) it('should accept a callback', function (done) { this .authenticator - .signIn(userData, done.bind(null, null)); - }); - + .signIn(userData, done.bind(null, null)) + }) it('should return a promise when no callback is provided', function (done) { this .authenticator .signIn(userData) .then(done.bind(null, null)) - .catch(done.bind(null, null)); - }); - + .catch(done.bind(null, null)) + }) it('should perform a POST request to ' + path, function (done) { - var request = this.request; + var request = this.request this .authenticator .signIn(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('should include the user data in the request', function (done) { - nock.cleanAll(); + nock.cleanAll() var request = nock(API_URL) .post(path, function (body) { for (var property in userData) { if (userData[property] !== body[property]) { - return false; + return false } } - return true; + return true }) - .reply(200); + .reply(200) this .authenticator .signIn(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('should include the Auth0 client ID in the request', function (done) { - nock.cleanAll(); + nock.cleanAll() var request = nock(API_URL) .post(path, function (body) { - return body.client_id === CLIENT_ID; + return body.client_id === CLIENT_ID }) - .reply(200); + .reply(200) this .authenticator .signIn(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('should use SMS connection', function (done) { - nock.cleanAll(); + nock.cleanAll() var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'sms'; + return body.connection === 'sms' }) - .reply(200); + .reply(200) this .authenticator .signIn(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); + .catch(done) + }) + it('should use email connection', function (done) { + nock.cleanAll() + var data = extend({ connection: 'email' }, userData) + var request = nock(API_URL) + .post(path, function (body) { + return body.connection === 'email' + }) + .reply(200) - it('shouldn\'t allow the user to specify the connection', function (done) { - nock.cleanAll(); + this + .authenticator + .signIn(data) + .then(function () { + expect(request.isDone()) + .to.be.true + + done() + }) + .catch(done) + }) - var data = extend({ connection: 'TEST_CONNECTION' }, userData); + it('should allow the user to specify the connection as sms or email', function (done) { + nock.cleanAll() + + var data = extend({ connection: 'TEST_CONNECTION' }, userData) var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'sms'; + return body.connection === 'sms' || body.connection === 'email' }) - .reply(200); + .reply(200) this .authenticator .signIn(data) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('should use password as grant type', function (done) { - nock.cleanAll(); + nock.cleanAll() var request = nock(API_URL) .post(path, function (body) { - return body.grant_type === 'password'; + return body.grant_type === 'password' }) - .reply(200); + .reply(200) this .authenticator .signIn(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('should use the openid scope', function (done) { - nock.cleanAll(); + nock.cleanAll() var request = nock(API_URL) .post(path, function (body) { - return body.scope === 'openid'; + return body.scope === 'openid' }) - .reply(200); + .reply(200) this .authenticator .signIn(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - }); - + .catch(done) + }) + }) describe('#sendEmail', function () { - var path = '/passwordless/start'; + var path = '/passwordless/start' var userData = { email: 'email@domain.com', send: 'link' - }; + } beforeEach(function () { - var oauth = new OAuth(validOptions); - this.authenticator = new Authenticator(validOptions, oauth); + var oauth = new OAuth(validOptions) + this.authenticator = new Authenticator(validOptions, oauth) this.request = nock(API_URL) .post(path) - .reply(200); - }); - + .reply(200) + }) it('should require an object as first argument', function () { expect(this.authenticator.sendEmail) - .to.throw(ArgumentError, 'Missing user data object'); - }); - + .to.throw(ArgumentError, 'Missing user data object') + }) it('should require an email', function () { - var auth = this.authenticator; - var userData = {}; - var sendEmail = auth.sendEmail.bind(auth, userData); + var auth = this.authenticator + var userData = {} + var sendEmail = auth.sendEmail.bind(auth, userData) expect(sendEmail) - .to.throw(ArgumentError, 'email field is required'); - }); - + .to.throw(ArgumentError, 'email field is required') + }) it('should require the send field', function () { - var auth = this.authenticator; - var userData = { email: 'email@domain.com' }; - var sendEmail = auth.sendEmail.bind(auth, userData); + var auth = this.authenticator + var userData = { email: 'email@domain.com' } + var sendEmail = auth.sendEmail.bind(auth, userData) expect(sendEmail) - .to.throw(ArgumentError, 'send field is required'); - }); - + .to.throw(ArgumentError, 'send field is required') + }) it('should accept a callback', function (done) { this .authenticator - .sendEmail(userData, done.bind(null, null)); - }); - + .sendEmail(userData, done.bind(null, null)) + }) it('should return a promise when no callback is provided', function (done) { this .authenticator .sendEmail(userData) .then(done.bind(null, null)) - .catch(done.bind(null, null)); - }); - + .catch(done.bind(null, null)) + }) it('should perform a POST request to ' + path, function (done) { - var request = this.request; + var request = this.request this .authenticator .sendEmail(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('should include the user data in the request', function (done) { - nock.cleanAll(); + nock.cleanAll() var request = nock(API_URL) .post(path, function (body) { for (var property in userData) { if (userData[property] !== body[property]) { - return false; + return false } } - return true; + return true }) - .reply(200); + .reply(200) this .authenticator .sendEmail(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('should include the Auth0 client ID in the request', function (done) { - nock.cleanAll(); + nock.cleanAll() var request = nock(API_URL) .post(path, function (body) { - return body.client_id === CLIENT_ID; + return body.client_id === CLIENT_ID }) - .reply(200); + .reply(200) this .authenticator .sendEmail(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('should use the email connection', function (done) { - nock.cleanAll(); + nock.cleanAll() var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'email'; + return body.connection === 'email' }) - .reply(200); + .reply(200) this .authenticator .sendEmail(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('should use the specified send type', function (done) { - nock.cleanAll(); + nock.cleanAll() - var data = extend({}, userData); + var data = extend({}, userData) var request = nock(API_URL) .post(path, function (body) { - return body.send === 'code'; + return body.send === 'code' }) - .reply(200); + .reply(200) - data.send = 'code'; + data.send = 'code' this .authenticator .sendEmail(data) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('shouldn\'t allow the user to specify the connection', function (done) { - nock.cleanAll(); + nock.cleanAll() - var data = extend({ connection: 'TEST_CONNECTION' }, userData); + var data = extend({ connection: 'TEST_CONNECTION' }, userData) var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'email'; + return body.connection === 'email' }) - .reply(200); + .reply(200) this .authenticator .sendEmail(data) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - }); - + .catch(done) + }) + }) describe('#sendSMS', function () { - var path = '/passwordless/start'; + var path = '/passwordless/start' var userData = { phone_number: '12345678' - }; + } beforeEach(function () { - var oauth = new OAuth(validOptions); - this.authenticator = new Authenticator(validOptions, oauth); + var oauth = new OAuth(validOptions) + this.authenticator = new Authenticator(validOptions, oauth) this.request = nock(API_URL) .post(path) - .reply(200); - }); - + .reply(200) + }) it('should require an object as first argument', function () { expect(this.authenticator.sendSMS) - .to.throw(ArgumentError, 'Missing user data object'); - }); - + .to.throw(ArgumentError, 'Missing user data object') + }) it('should require a phone number', function () { - var auth = this.authenticator; - var userData = {}; - var sendSMS = auth.sendSMS.bind(auth, userData); + var auth = this.authenticator + var userData = {} + var sendSMS = auth.sendSMS.bind(auth, userData) expect(sendSMS) - .to.throw(ArgumentError, 'phone_number field is required'); - }); + .to.throw(ArgumentError, 'phone_number field is required') + }) it('should accept a callback', function (done) { this .authenticator - .sendSMS(userData, done.bind(null, null)); - }); - + .sendSMS(userData, done.bind(null, null)) + }) it('should return a promise when no callback is provided', function (done) { this .authenticator .sendSMS(userData) .then(done.bind(null, null)) - .catch(done.bind(null, null)); - }); - + .catch(done.bind(null, null)) + }) it('should perform a POST request to ' + path, function (done) { - var request = this.request; + var request = this.request this .authenticator .sendSMS(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('should include the user data in the request', function (done) { - nock.cleanAll(); + nock.cleanAll() var request = nock(API_URL) .post(path, function (body) { for (var property in userData) { if (userData[property] !== body[property]) { - return false; + return false } } - return true; + return true }) - .reply(200); + .reply(200) this .authenticator .sendSMS(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('should include the Auth0 client ID in the request', function (done) { - nock.cleanAll(); + nock.cleanAll() var request = nock(API_URL) .post(path, function (body) { - return body.client_id === CLIENT_ID; + return body.client_id === CLIENT_ID }) - .reply(200); + .reply(200) this .authenticator .sendSMS(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('should use the sms connection', function (done) { - nock.cleanAll(); + nock.cleanAll() var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'sms'; + return body.connection === 'sms' }) - .reply(200); + .reply(200) this .authenticator .sendSMS(userData) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - + .catch(done) + }) it('shouldn\'t allow the user to specify the connection', function (done) { - nock.cleanAll(); + nock.cleanAll() - var data = extend({ connection: 'TEST_CONNECTION' }, userData); + var data = extend({ connection: 'TEST_CONNECTION' }, userData) var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'sms'; + return body.connection === 'sms' }) - .reply(200); + .reply(200) this .authenticator .sendSMS(data) .then(function () { expect(request.isDone()) - .to.be.true; + .to.be.true - done(); + done() }) - .catch(done); - }); - }); -}); + .catch(done) + }) + }) +}) From eb4c9c8858a32f55d47fa31abf9f7bea8cf52852 Mon Sep 17 00:00:00 2001 From: Wilson Hobbs Date: Wed, 8 Feb 2017 08:54:24 -0500 Subject: [PATCH 4/7] Update docs --- src/auth/PasswordlessAuthenticator.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/auth/PasswordlessAuthenticator.js b/src/auth/PasswordlessAuthenticator.js index 6e1de1642..7dd584c83 100644 --- a/src/auth/PasswordlessAuthenticator.js +++ b/src/auth/PasswordlessAuthenticator.js @@ -65,6 +65,7 @@ var PasswordlessAuthenticator = function (options, oauth) { * @param {Object} userData User credentials object. * @param {String} userData.username Username. * @param {String} userData.password Password. + * @param {String} [userData.connection=sms] Connection string: "sms" or "email". * @param {String} [userData.client_id] Client ID. * @param {Function} [cb] Method callback. * From 4b575a8cdffa71187e8eab8a733f4a373b7e71b3 Mon Sep 17 00:00:00 2001 From: Wilson Hobbs Date: Thu, 6 Jul 2017 22:39:16 -0400 Subject: [PATCH 5/7] fix formatting --- src/auth/PasswordlessAuthenticator.js | 81 +++-- test/auth/passwordless.tests.js | 450 +++++++++++++------------- 2 files changed, 263 insertions(+), 268 deletions(-) diff --git a/src/auth/PasswordlessAuthenticator.js b/src/auth/PasswordlessAuthenticator.js index 4411d6062..eea2b4024 100644 --- a/src/auth/PasswordlessAuthenticator.js +++ b/src/auth/PasswordlessAuthenticator.js @@ -1,7 +1,7 @@ -var extend = require('util')._extend +var extend = require('util')._extend; -var ArgumentError = require('../exceptions').ArgumentError -var RestClient = require('rest-facade').Client +var ArgumentError = require('../exceptions').ArgumentError; +var RestClient = require('rest-facade').Client; /** * @class @@ -16,11 +16,11 @@ var RestClient = require('rest-facade').Client */ var PasswordlessAuthenticator = function (options, oauth) { if (!options) { - throw new ArgumentError('Missing authenticator options') + throw new ArgumentError('Missing authenticator options'); } if (typeof options !== 'object') { - throw new ArgumentError('The authenticator options must be an object') + throw new ArgumentError('The authenticator options must be an object'); } /** @@ -37,11 +37,6 @@ var PasswordlessAuthenticator = function (options, oauth) { this.clientId = options.clientId; }; - this.oauth = oauth - this.passwordless = new RestClient(baseUrl) - this.clientId = options.clientId -} - /** * Sign in with the given user credentials. * @@ -56,13 +51,13 @@ var PasswordlessAuthenticator = function (options, oauth) { * var data = { * username: '{PHONE_NUMBER}', * password: '{VERIFICATION_CODE}' - * }; + * } * * auth0.passwordless.signIn(data, function (err) { * if (err) { * // Handle error. * } - * }); + * }) * * @example * The user data object has the following structure. @@ -86,30 +81,30 @@ var PasswordlessAuthenticator = function (options, oauth) { PasswordlessAuthenticator.prototype.signIn = function (userData, cb) { var defaultFields = { client_id: this.clientId - } - var data = extend(defaultFields, userData) + }; + var data = extend(defaultFields, userData); // Don't let the user override the connection nor the grant type. - if (!data.connection || (data.connection !== 'email' && data.connection !== 'sms')) { data.connection = 'sms' } + if (!data.connection || (data.connection !== 'email' && data.connection !== 'sms')) { data.connection = 'sms'; } - data.grant_type = 'password' + data.grant_type = 'password'; if (!userData || typeof userData !== 'object') { - throw new ArgumentError('Missing user data object') + throw new ArgumentError('Missing user data object'); } if (typeof data.username !== 'string' || data.username.trim().length === 0) { - throw new ArgumentError('username field (phone number) is required') + throw new ArgumentError('username field (phone number) is required'); } if (typeof data.password !== 'string' || data.password.trim().length === 0) { - throw new ArgumentError('password field (verification code) is required') + throw new ArgumentError('password field (verification code) is required'); } - return this.oauth.signIn(data, cb) -} + return this.oauth.signIn(data, cb); +}; /** * Start passwordless flow sending an email. @@ -141,13 +136,13 @@ PasswordlessAuthenticator.prototype.signIn = function (userData, cb) { * email: '{EMAIL}', * send: 'link', * authParams: {} // Optional auth params. - * }; + * } * * auth0.passwordless.sendEmail(data, function (err) { * if (err) { * // Handle error. * } - * }); + * }) * * @param {Object} userData User account data. * @param {String} userData.email User email address. @@ -159,32 +154,32 @@ PasswordlessAuthenticator.prototype.signIn = function (userData, cb) { PasswordlessAuthenticator.prototype.sendEmail = function (userData, cb) { var defaultFields = { client_id: this.clientId - } - var data = extend(defaultFields, userData) + }; + var data = extend(defaultFields, userData); // Don't let the user override the connection nor the grant type. - data.connection = 'email' + data.connection = 'email'; if (!userData || typeof userData !== 'object') { - throw new ArgumentError('Missing user data object') + throw new ArgumentError('Missing user data object'); } if (typeof data.email !== 'string' || data.email.trim().length === 0) { - throw new ArgumentError('email field is required') + throw new ArgumentError('email field is required'); } if (typeof data.send !== 'string' || data.send.trim().length === 0) { - throw new ArgumentError('send field is required') + throw new ArgumentError('send field is required'); } if (cb && cb instanceof Function) { - return this.passwordless.create(data, cb) + return this.passwordless.create(data, cb); } - return this.passwordless.create(data) -} + return this.passwordless.create(data); +}; /** * Start passwordless flow sending an SMS. @@ -201,13 +196,13 @@ PasswordlessAuthenticator.prototype.sendEmail = function (userData, cb) { * * var data = { * phone_number: '{PHONE}' - * }; + * } * * auth0.passwordless.sendSMS(data, function (err) { * if (err) { * // Handle error. * } - * }); + * }) * * @param {Object} userData User account data. * @param {String} userData.phone_number User phone number. @@ -219,26 +214,26 @@ PasswordlessAuthenticator.prototype.sendEmail = function (userData, cb) { PasswordlessAuthenticator.prototype.sendSMS = function (userData, cb) { var defaultFields = { client_id: this.clientId - } - var data = extend(defaultFields, userData) + }; + var data = extend(defaultFields, userData); // Don't let the user override the connection nor the grant type. - data.connection = 'sms' + data.connection = 'sms'; if (!userData || typeof userData !== 'object') { - throw new ArgumentError('Missing user data object') + throw new ArgumentError('Missing user data object'); } if (typeof data.phone_number !== 'string' || data.phone_number.trim().length === 0) { - throw new ArgumentError('phone_number field is required') + throw new ArgumentError('phone_number field is required'); } if (cb && cb instanceof Function) { - return this.passwordless.create(data, cb) + return this.passwordless.create(data, cb); } - return this.passwordless.create(data) -} + return this.passwordless.create(data); +}; -module.exports = PasswordlessAuthenticator +module.exports = PasswordlessAuthenticator; diff --git a/test/auth/passwordless.tests.js b/test/auth/passwordless.tests.js index 11d9d7d82..c4a1cb8d2 100644 --- a/test/auth/passwordless.tests.js +++ b/test/auth/passwordless.tests.js @@ -1,12 +1,12 @@ -var expect = require('chai').expect -var extend = require('util')._extend -var nock = require('nock') +var expect = require('chai').expect; +var extend = require('util')._extend; +var nock = require('nock'); // Constants. -var SRC_DIR = '../../src' -var DOMAIN = 'tenant.auth0.com' -var API_URL = 'https://' + DOMAIN -var CLIENT_ID = 'TEST_CLIENT_ID' +var SRC_DIR = '../../src'; +var DOMAIN = 'tenant.auth0.com'; +var API_URL = 'https://' + DOMAIN; +var CLIENT_ID = 'TEST_CLIENT_ID'; var ArgumentError = require('rest-facade').ArgumentError; var Authenticator = require(SRC_DIR + '/auth/PasswordlessAuthenticator'); @@ -15,591 +15,591 @@ var OAuth = require(SRC_DIR + '/auth/OAuthAuthenticator'); var validOptions = { baseUrl: API_URL, clientId: CLIENT_ID -} +}; describe('PasswordlessAuthenticator', function () { afterEach(function () { - nock.cleanAll() - }) + nock.cleanAll(); + }); describe('#constructor', function () { it('should require an options object', function () { expect(Authenticator) - .to.throw(ArgumentError, 'Missing authenticator options') + .to.throw(ArgumentError, 'Missing authenticator options'); expect(Authenticator.bind(null, 1)) - .to.throw(ArgumentError, 'The authenticator options must be an object') + .to.throw(ArgumentError, 'The authenticator options must be an object'); expect(Authenticator.bind(null, validOptions)) - .to.not.throw(ArgumentError) - }) - }) + .to.not.throw(ArgumentError); + }); + }); describe('instance', function () { - var methods = ['signIn', 'sendEmail', 'sendSMS'] - var oauth = new OAuth(validOptions) - var authenticator = new Authenticator(validOptions, oauth) + var methods = ['signIn', 'sendEmail', 'sendSMS']; + var oauth = new OAuth(validOptions); + var authenticator = new Authenticator(validOptions, oauth); methods.forEach(function (method) { it('should have a ' + method + ' method', function () { expect(authenticator[method]) .to.exist - .to.be.an.instanceOf(Function) - }) - }) - }) + .to.be.an.instanceOf(Function); + }); + }); + }); describe('#signIn', function () { - var path = '/oauth/ro' + var path = '/oauth/ro'; var userData = { username: 'username', password: 'pwd' - } + }; beforeEach(function () { - var oauth = new OAuth(validOptions) - this.authenticator = new Authenticator(validOptions, oauth) + var oauth = new OAuth(validOptions); + this.authenticator = new Authenticator(validOptions, oauth); this.request = nock(API_URL) .post(path) - .reply(200) - }) + .reply(200); + }); it('should require an object as first argument', function () { expect(this.authenticator.signIn) - .to.throw(ArgumentError, 'Missing user data object') - }) + .to.throw(ArgumentError, 'Missing user data object'); + }); it('should require a phone number', function () { - var auth = this.authenticator - var userData = { password: 'password' } - var signIn = auth.signIn.bind(auth, userData) + var auth = this.authenticator; + var userData = { password: 'password' }; + var signIn = auth.signIn.bind(auth, userData); expect(signIn) - .to.throw(ArgumentError, 'username field (phone number) is required') - }) + .to.throw(ArgumentError, 'username field (phone number) is required'); + }); it('should require a verification code', function () { - var auth = this.authenticator - var userData = { username: 'username' } - var signIn = auth.signIn.bind(auth, userData) + var auth = this.authenticator; + var userData = { username: 'username' }; + var signIn = auth.signIn.bind(auth, userData); expect(signIn) - .to.throw(ArgumentError, 'password field (verification code) is required') - }) + .to.throw(ArgumentError, 'password field (verification code) is required'); + }); it('should accept a callback', function (done) { this .authenticator - .signIn(userData, done.bind(null, null)) - }) + .signIn(userData, done.bind(null, null)); + }); it('should return a promise when no callback is provided', function (done) { this .authenticator .signIn(userData) .then(done.bind(null, null)) - .catch(done.bind(null, null)) - }) + .catch(done.bind(null, null)); + }); it('should perform a POST request to ' + path, function (done) { - var request = this.request + var request = this.request; this .authenticator .signIn(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should include the user data in the request', function (done) { - nock.cleanAll() + nock.cleanAll(); var request = nock(API_URL) .post(path, function (body) { for (var property in userData) { if (userData[property] !== body[property]) { - return false + return false; } } - return true + return true; }) - .reply(200) + .reply(200); this .authenticator .signIn(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should include the Auth0 client ID in the request', function (done) { - nock.cleanAll() + nock.cleanAll(); var request = nock(API_URL) .post(path, function (body) { - return body.client_id === CLIENT_ID + return body.client_id === CLIENT_ID; }) - .reply(200) + .reply(200); this .authenticator .signIn(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should use SMS connection', function (done) { - nock.cleanAll() + nock.cleanAll(); var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'sms' + return body.connection === 'sms'; }) - .reply(200) + .reply(200); this .authenticator .signIn(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should use email connection', function (done) { - nock.cleanAll() - var data = extend({ connection: 'email' }, userData) + nock.cleanAll(); + var data = extend({ connection: 'email' }, userData); var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'email' + return body.connection === 'email'; }) - .reply(200) + .reply(200); this .authenticator .signIn(data) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should allow the user to specify the connection as sms or email', function (done) { - nock.cleanAll() + nock.cleanAll(); - var data = extend({ connection: 'TEST_CONNECTION' }, userData) + var data = extend({ connection: 'TEST_CONNECTION' }, userData); var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'sms' || body.connection === 'email' + return body.connection === 'sms' || body.connection === 'email'; }) - .reply(200) + .reply(200); this .authenticator .signIn(data) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should use password as grant type', function (done) { - nock.cleanAll() + nock.cleanAll(); var request = nock(API_URL) .post(path, function (body) { - return body.grant_type === 'password' + return body.grant_type === 'password'; }) - .reply(200) + .reply(200); this .authenticator .signIn(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should use the openid scope', function (done) { - nock.cleanAll() + nock.cleanAll(); var request = nock(API_URL) .post(path, function (body) { - return body.scope === 'openid' + return body.scope === 'openid'; }) - .reply(200) + .reply(200); this .authenticator .signIn(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) - }) + .catch(done); + }); + }); describe('#sendEmail', function () { - var path = '/passwordless/start' + var path = '/passwordless/start'; var userData = { email: 'email@domain.com', send: 'link' - } + }; beforeEach(function () { - var oauth = new OAuth(validOptions) - this.authenticator = new Authenticator(validOptions, oauth) + var oauth = new OAuth(validOptions); + this.authenticator = new Authenticator(validOptions, oauth); this.request = nock(API_URL) .post(path) - .reply(200) - }) + .reply(200); + }); it('should require an object as first argument', function () { expect(this.authenticator.sendEmail) - .to.throw(ArgumentError, 'Missing user data object') - }) + .to.throw(ArgumentError, 'Missing user data object'); + }); it('should require an email', function () { - var auth = this.authenticator - var userData = {} - var sendEmail = auth.sendEmail.bind(auth, userData) + var auth = this.authenticator; + var userData = {}; + var sendEmail = auth.sendEmail.bind(auth, userData); expect(sendEmail) - .to.throw(ArgumentError, 'email field is required') - }) + .to.throw(ArgumentError, 'email field is required'); + }); it('should require the send field', function () { - var auth = this.authenticator - var userData = { email: 'email@domain.com' } - var sendEmail = auth.sendEmail.bind(auth, userData) + var auth = this.authenticator; + var userData = { email: 'email@domain.com' }; + var sendEmail = auth.sendEmail.bind(auth, userData); expect(sendEmail) - .to.throw(ArgumentError, 'send field is required') - }) + .to.throw(ArgumentError, 'send field is required'); + }); it('should accept a callback', function (done) { this .authenticator - .sendEmail(userData, done.bind(null, null)) - }) + .sendEmail(userData, done.bind(null, null)); + }); it('should return a promise when no callback is provided', function (done) { this .authenticator .sendEmail(userData) .then(done.bind(null, null)) - .catch(done.bind(null, null)) - }) + .catch(done.bind(null, null)); + }); it('should perform a POST request to ' + path, function (done) { - var request = this.request + var request = this.request; this .authenticator .sendEmail(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should include the user data in the request', function (done) { - nock.cleanAll() + nock.cleanAll(); var request = nock(API_URL) .post(path, function (body) { for (var property in userData) { if (userData[property] !== body[property]) { - return false + return false; } } - return true + return true; }) - .reply(200) + .reply(200); this .authenticator .sendEmail(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should include the Auth0 client ID in the request', function (done) { - nock.cleanAll() + nock.cleanAll(); var request = nock(API_URL) .post(path, function (body) { - return body.client_id === CLIENT_ID + return body.client_id === CLIENT_ID; }) - .reply(200) + .reply(200); this .authenticator .sendEmail(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should use the email connection', function (done) { - nock.cleanAll() + nock.cleanAll(); var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'email' + return body.connection === 'email'; }) - .reply(200) + .reply(200); this .authenticator .sendEmail(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should use the specified send type', function (done) { - nock.cleanAll() + nock.cleanAll(); - var data = extend({}, userData) + var data = extend({}, userData); var request = nock(API_URL) .post(path, function (body) { - return body.send === 'code' + return body.send === 'code'; }) - .reply(200) + .reply(200); - data.send = 'code' + data.send = 'code'; this .authenticator .sendEmail(data) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); - it('shouldn\'t allow the user to specify the connection', function (done) { - nock.cleanAll() + it("shouldn't allow the user to specify the connection", function (done) { + nock.cleanAll(); - var data = extend({ connection: 'TEST_CONNECTION' }, userData) + var data = extend({ connection: 'TEST_CONNECTION' }, userData); var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'email' + return body.connection === 'email'; }) - .reply(200) + .reply(200); this .authenticator .sendEmail(data) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) - }) + .catch(done); + }); + }); describe('#sendSMS', function () { - var path = '/passwordless/start' + var path = '/passwordless/start'; var userData = { phone_number: '12345678' - } + }; beforeEach(function () { - var oauth = new OAuth(validOptions) - this.authenticator = new Authenticator(validOptions, oauth) + var oauth = new OAuth(validOptions); + this.authenticator = new Authenticator(validOptions, oauth); this.request = nock(API_URL) .post(path) - .reply(200) - }) + .reply(200); + }); it('should require an object as first argument', function () { expect(this.authenticator.sendSMS) - .to.throw(ArgumentError, 'Missing user data object') - }) + .to.throw(ArgumentError, 'Missing user data object'); + }); it('should require a phone number', function () { - var auth = this.authenticator - var userData = {} - var sendSMS = auth.sendSMS.bind(auth, userData) + var auth = this.authenticator; + var userData = {}; + var sendSMS = auth.sendSMS.bind(auth, userData); expect(sendSMS) - .to.throw(ArgumentError, 'phone_number field is required') - }) + .to.throw(ArgumentError, 'phone_number field is required'); + }); it('should accept a callback', function (done) { this .authenticator - .sendSMS(userData, done.bind(null, null)) - }) + .sendSMS(userData, done.bind(null, null)); + }); it('should return a promise when no callback is provided', function (done) { this .authenticator .sendSMS(userData) .then(done.bind(null, null)) - .catch(done.bind(null, null)) - }) + .catch(done.bind(null, null)); + }); it('should perform a POST request to ' + path, function (done) { - var request = this.request + var request = this.request; this .authenticator .sendSMS(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should include the user data in the request', function (done) { - nock.cleanAll() + nock.cleanAll(); var request = nock(API_URL) .post(path, function (body) { for (var property in userData) { if (userData[property] !== body[property]) { - return false + return false; } } - return true + return true; }) - .reply(200) + .reply(200); this .authenticator .sendSMS(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should include the Auth0 client ID in the request', function (done) { - nock.cleanAll() + nock.cleanAll(); var request = nock(API_URL) .post(path, function (body) { - return body.client_id === CLIENT_ID + return body.client_id === CLIENT_ID; }) - .reply(200) + .reply(200); this .authenticator .sendSMS(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); it('should use the sms connection', function (done) { - nock.cleanAll() + nock.cleanAll(); var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'sms' + return body.connection === 'sms'; }) - .reply(200) + .reply(200); this .authenticator .sendSMS(userData) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) + .catch(done); + }); - it('shouldn\'t allow the user to specify the connection', function (done) { - nock.cleanAll() + it("shouldn't allow the user to specify the connection", function (done) { + nock.cleanAll(); - var data = extend({ connection: 'TEST_CONNECTION' }, userData) + var data = extend({ connection: 'TEST_CONNECTION' }, userData); var request = nock(API_URL) .post(path, function (body) { - return body.connection === 'sms' + return body.connection === 'sms'; }) - .reply(200) + .reply(200); this .authenticator .sendSMS(data) .then(function () { expect(request.isDone()) - .to.be.true + .to.be.true; - done() + done(); }) - .catch(done) - }) - }) -}) + .catch(done); + }); + }); +}); From 8310d399b837bce5441393b57049c5803a4825ba Mon Sep 17 00:00:00 2001 From: Wilson Hobbs Date: Thu, 6 Jul 2017 22:41:38 -0400 Subject: [PATCH 6/7] I don't want to change too much --- src/auth/PasswordlessAuthenticator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/PasswordlessAuthenticator.js b/src/auth/PasswordlessAuthenticator.js index eea2b4024..9b25432a3 100644 --- a/src/auth/PasswordlessAuthenticator.js +++ b/src/auth/PasswordlessAuthenticator.js @@ -1,6 +1,6 @@ var extend = require('util')._extend; -var ArgumentError = require('../exceptions').ArgumentError; +var ArgumentError = require('rest-facade').ArgumentError; var RestClient = require('rest-facade').Client; /** From 5f9e66bd51d9d2220a969076722939c4e1685c73 Mon Sep 17 00:00:00 2001 From: Wilson Hobbs Date: Mon, 11 Dec 2017 14:41:46 -0500 Subject: [PATCH 7/7] refactor: fix style errors --- src/auth/PasswordlessAuthenticator.js | 28 +++++++++-------- test/auth/passwordless.tests.js | 43 +++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/src/auth/PasswordlessAuthenticator.js b/src/auth/PasswordlessAuthenticator.js index 9b25432a3..5759b7685 100644 --- a/src/auth/PasswordlessAuthenticator.js +++ b/src/auth/PasswordlessAuthenticator.js @@ -3,6 +3,7 @@ var extend = require('util')._extend; var ArgumentError = require('rest-facade').ArgumentError; var RestClient = require('rest-facade').Client; + /** * @class * Handles authenticator with passwordless flows, e.g. SMS, Touch ID, etc. @@ -37,6 +38,7 @@ var PasswordlessAuthenticator = function (options, oauth) { this.clientId = options.clientId; }; + /** * Sign in with the given user credentials. * @@ -51,13 +53,13 @@ var PasswordlessAuthenticator = function (options, oauth) { * var data = { * username: '{PHONE_NUMBER}', * password: '{VERIFICATION_CODE}' - * } + * }; * * auth0.passwordless.signIn(data, function (err) { * if (err) { * // Handle error. * } - * }) + * }); * * @example * The user data object has the following structure. @@ -86,7 +88,6 @@ PasswordlessAuthenticator.prototype.signIn = function (userData, cb) { // Don't let the user override the connection nor the grant type. if (!data.connection || (data.connection !== 'email' && data.connection !== 'sms')) { data.connection = 'sms'; } - data.grant_type = 'password'; if (!userData || typeof userData !== 'object') { @@ -94,18 +95,19 @@ PasswordlessAuthenticator.prototype.signIn = function (userData, cb) { } if (typeof data.username !== 'string' - || data.username.trim().length === 0) { + || data.username.trim().length === 0) { throw new ArgumentError('username field (phone number) is required'); } if (typeof data.password !== 'string' - || data.password.trim().length === 0) { + || data.password.trim().length === 0) { throw new ArgumentError('password field (verification code) is required'); } return this.oauth.signIn(data, cb); }; + /** * Start passwordless flow sending an email. * @@ -136,13 +138,13 @@ PasswordlessAuthenticator.prototype.signIn = function (userData, cb) { * email: '{EMAIL}', * send: 'link', * authParams: {} // Optional auth params. - * } + * }; * * auth0.passwordless.sendEmail(data, function (err) { * if (err) { * // Handle error. * } - * }) + * }); * * @param {Object} userData User account data. * @param {String} userData.email User email address. @@ -165,12 +167,12 @@ PasswordlessAuthenticator.prototype.sendEmail = function (userData, cb) { } if (typeof data.email !== 'string' - || data.email.trim().length === 0) { + || data.email.trim().length === 0) { throw new ArgumentError('email field is required'); } if (typeof data.send !== 'string' - || data.send.trim().length === 0) { + || data.send.trim().length === 0) { throw new ArgumentError('send field is required'); } @@ -181,6 +183,7 @@ PasswordlessAuthenticator.prototype.sendEmail = function (userData, cb) { return this.passwordless.create(data); }; + /** * Start passwordless flow sending an SMS. * @@ -196,13 +199,13 @@ PasswordlessAuthenticator.prototype.sendEmail = function (userData, cb) { * * var data = { * phone_number: '{PHONE}' - * } + * }; * * auth0.passwordless.sendSMS(data, function (err) { * if (err) { * // Handle error. * } - * }) + * }); * * @param {Object} userData User account data. * @param {String} userData.phone_number User phone number. @@ -225,7 +228,7 @@ PasswordlessAuthenticator.prototype.sendSMS = function (userData, cb) { } if (typeof data.phone_number !== 'string' - || data.phone_number.trim().length === 0) { + || data.phone_number.trim().length === 0) { throw new ArgumentError('phone_number field is required'); } @@ -236,4 +239,5 @@ PasswordlessAuthenticator.prototype.sendSMS = function (userData, cb) { return this.passwordless.create(data); }; + module.exports = PasswordlessAuthenticator; diff --git a/test/auth/passwordless.tests.js b/test/auth/passwordless.tests.js index c4a1cb8d2..a8aeb3c2e 100644 --- a/test/auth/passwordless.tests.js +++ b/test/auth/passwordless.tests.js @@ -17,11 +17,14 @@ var validOptions = { clientId: CLIENT_ID }; + describe('PasswordlessAuthenticator', function () { + afterEach(function () { nock.cleanAll(); }); + describe('#constructor', function () { it('should require an options object', function () { expect(Authenticator) @@ -35,6 +38,7 @@ describe('PasswordlessAuthenticator', function () { }); }); + describe('instance', function () { var methods = ['signIn', 'sendEmail', 'sendSMS']; var oauth = new OAuth(validOptions); @@ -49,6 +53,7 @@ describe('PasswordlessAuthenticator', function () { }); }); + describe('#signIn', function () { var path = '/oauth/ro'; var userData = { @@ -64,11 +69,13 @@ describe('PasswordlessAuthenticator', function () { .reply(200); }); + it('should require an object as first argument', function () { expect(this.authenticator.signIn) .to.throw(ArgumentError, 'Missing user data object'); }); + it('should require a phone number', function () { var auth = this.authenticator; var userData = { password: 'password' }; @@ -78,6 +85,7 @@ describe('PasswordlessAuthenticator', function () { .to.throw(ArgumentError, 'username field (phone number) is required'); }); + it('should require a verification code', function () { var auth = this.authenticator; var userData = { username: 'username' }; @@ -87,12 +95,14 @@ describe('PasswordlessAuthenticator', function () { .to.throw(ArgumentError, 'password field (verification code) is required'); }); + it('should accept a callback', function (done) { this .authenticator .signIn(userData, done.bind(null, null)); }); + it('should return a promise when no callback is provided', function (done) { this .authenticator @@ -101,6 +111,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done.bind(null, null)); }); + it('should perform a POST request to ' + path, function (done) { var request = this.request; @@ -116,6 +127,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should include the user data in the request', function (done) { nock.cleanAll(); @@ -143,6 +155,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should include the Auth0 client ID in the request', function (done) { nock.cleanAll(); @@ -164,6 +177,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should use SMS connection', function (done) { nock.cleanAll(); @@ -185,6 +199,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should use email connection', function (done) { nock.cleanAll(); var data = extend({ connection: 'email' }, userData); @@ -206,6 +221,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should allow the user to specify the connection as sms or email', function (done) { nock.cleanAll(); @@ -228,6 +244,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should use password as grant type', function (done) { nock.cleanAll(); @@ -249,6 +266,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should use the openid scope', function (done) { nock.cleanAll(); @@ -271,6 +289,7 @@ describe('PasswordlessAuthenticator', function () { }); }); + describe('#sendEmail', function () { var path = '/passwordless/start'; var userData = { @@ -286,11 +305,13 @@ describe('PasswordlessAuthenticator', function () { .reply(200); }); + it('should require an object as first argument', function () { expect(this.authenticator.sendEmail) .to.throw(ArgumentError, 'Missing user data object'); }); + it('should require an email', function () { var auth = this.authenticator; var userData = {}; @@ -300,6 +321,7 @@ describe('PasswordlessAuthenticator', function () { .to.throw(ArgumentError, 'email field is required'); }); + it('should require the send field', function () { var auth = this.authenticator; var userData = { email: 'email@domain.com' }; @@ -309,12 +331,14 @@ describe('PasswordlessAuthenticator', function () { .to.throw(ArgumentError, 'send field is required'); }); + it('should accept a callback', function (done) { this .authenticator .sendEmail(userData, done.bind(null, null)); }); + it('should return a promise when no callback is provided', function (done) { this .authenticator @@ -323,6 +347,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done.bind(null, null)); }); + it('should perform a POST request to ' + path, function (done) { var request = this.request; @@ -338,6 +363,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should include the user data in the request', function (done) { nock.cleanAll(); @@ -365,6 +391,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should include the Auth0 client ID in the request', function (done) { nock.cleanAll(); @@ -386,6 +413,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should use the email connection', function (done) { nock.cleanAll(); @@ -407,6 +435,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should use the specified send type', function (done) { nock.cleanAll(); @@ -431,7 +460,8 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); - it("shouldn't allow the user to specify the connection", function (done) { + + it('shouldn\'t allow the user to specify the connection', function (done) { nock.cleanAll(); var data = extend({ connection: 'TEST_CONNECTION' }, userData); @@ -454,6 +484,7 @@ describe('PasswordlessAuthenticator', function () { }); }); + describe('#sendSMS', function () { var path = '/passwordless/start'; var userData = { @@ -468,11 +499,13 @@ describe('PasswordlessAuthenticator', function () { .reply(200); }); + it('should require an object as first argument', function () { expect(this.authenticator.sendSMS) .to.throw(ArgumentError, 'Missing user data object'); }); + it('should require a phone number', function () { var auth = this.authenticator; var userData = {}; @@ -488,6 +521,7 @@ describe('PasswordlessAuthenticator', function () { .sendSMS(userData, done.bind(null, null)); }); + it('should return a promise when no callback is provided', function (done) { this .authenticator @@ -496,6 +530,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done.bind(null, null)); }); + it('should perform a POST request to ' + path, function (done) { var request = this.request; @@ -511,6 +546,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should include the user data in the request', function (done) { nock.cleanAll(); @@ -538,6 +574,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should include the Auth0 client ID in the request', function (done) { nock.cleanAll(); @@ -559,6 +596,7 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); + it('should use the sms connection', function (done) { nock.cleanAll(); @@ -580,7 +618,8 @@ describe('PasswordlessAuthenticator', function () { .catch(done); }); - it("shouldn't allow the user to specify the connection", function (done) { + + it('shouldn\'t allow the user to specify the connection', function (done) { nock.cleanAll(); var data = extend({ connection: 'TEST_CONNECTION' }, userData);