diff --git a/src/auth/DatabaseAuthenticator.js b/src/auth/DatabaseAuthenticator.js index cebb038a8..72568de05 100644 --- a/src/auth/DatabaseAuthenticator.js +++ b/src/auth/DatabaseAuthenticator.js @@ -10,6 +10,7 @@ class DatabaseAuthenticator { * @param {object} options Authenticator options. * @param {string} options.baseUrl The auth0 account URL. * @param {string} [options.clientId] Default client ID. + * @param {string} [options.proxy] Add the `superagent-proxy` dependency and specify a proxy url eg 'https://myproxy.com:1234' * @param {OAuthAuthenticator} oauth OAuthAuthenticator instance. */ constructor(options, oauth) { @@ -29,6 +30,7 @@ class DatabaseAuthenticator { const clientOptions = { errorFormatter: { message: 'message', name: 'error' }, headers: options.headers, + proxy: options.proxy, }; this.oauth = oauth; diff --git a/src/auth/OAuthAuthenticator.js b/src/auth/OAuthAuthenticator.js index d7d7c31d1..709f561d3 100644 --- a/src/auth/OAuthAuthenticator.js +++ b/src/auth/OAuthAuthenticator.js @@ -35,6 +35,7 @@ class OAuthAuthenticator { * @param {string} [options.clientAssertionSigningKey] Private key used to sign the client assertion JWT. * @param {string} [options.clientAssertionSigningAlg] Default 'RS256'. * @param {boolean} [options.__bypassIdTokenValidation] Whether the id_token should be validated or not + * @param {string} [options.proxy] Add the `superagent-proxy` dependency and specify a proxy url eg 'https://myproxy.com:1234' */ constructor(options) { if (!options) { @@ -54,6 +55,7 @@ class OAuthAuthenticator { errorCustomizer: SanitizedError, errorFormatter: { message: 'message', name: 'error' }, headers: options.headers, + proxy: options.proxy, }; this.oauth = new RestClient(`${options.baseUrl}/oauth/:type`, clientOptions); diff --git a/src/auth/PasswordlessAuthenticator.js b/src/auth/PasswordlessAuthenticator.js index a6be10d6b..c933f4fe9 100644 --- a/src/auth/PasswordlessAuthenticator.js +++ b/src/auth/PasswordlessAuthenticator.js @@ -29,6 +29,7 @@ class PasswordlessAuthenticator { * @param {string} [options.clientSecret] Default client secret. * @param {string} [options.clientAssertionSigningKey] Private key used to sign the client assertion JWT. * @param {string} [options.clientAssertionSigningAlg] Default 'RS256'. + * @param {string} [options.proxy] Add the `superagent-proxy` dependency and specify a proxy url eg 'https://myproxy.com:1234' * @param {OAuthAuthenticator} oauth OAuthAuthenticator instance. */ constructor(options, oauth) { @@ -48,6 +49,7 @@ class PasswordlessAuthenticator { const clientOptions = { errorFormatter: { message: 'message', name: 'error' }, headers: options.headers, + proxy: options.proxy, }; this.oauth = oauth; diff --git a/src/auth/index.js b/src/auth/index.js index c2a4412ea..d2dc7e284 100644 --- a/src/auth/index.js +++ b/src/auth/index.js @@ -42,6 +42,7 @@ class AuthenticationClient { * @param {string} [options.supportedAlgorithms] Algorithms that your application expects to receive * @param {boolean} [options.__bypassIdTokenValidation] Whether the id_token should be validated or not * @param {object} [options.headers] Additional headers that will be added to the outgoing requests. + * @param {string} [options.proxy] Add the `superagent-proxy` dependency and specify a proxy url eg 'https://myproxy.com:1234' */ constructor(options) { if (!options || typeof options !== 'object') { @@ -67,6 +68,7 @@ class AuthenticationClient { baseUrl: util.format(BASE_URL_FORMAT, options.domain), supportedAlgorithms: options.supportedAlgorithms, __bypassIdTokenValidation: options.__bypassIdTokenValidation, + proxy: options.proxy, }; if (options.telemetry !== false) { diff --git a/src/management/ManagementTokenProvider.js b/src/management/ManagementTokenProvider.js index 4b12d048c..78a2fe757 100644 --- a/src/management/ManagementTokenProvider.js +++ b/src/management/ManagementTokenProvider.js @@ -19,6 +19,7 @@ class ManagementTokenProvider { * @param {boolean} [options.enableCache=true] Enabled or Disable Cache * @param {number} [options.cacheTTLInSeconds] By default the `expires_in` value will be used to determine the cached time of the token, this can be overridden. * @param {object} [options.headers] Additional headers that will be added to the outgoing requests. + * @param {string} [options.proxy] Add the `superagent-proxy` dependency and specify a proxy url eg 'https://myproxy.com:1234' */ constructor(options) { if (!options || typeof options !== 'object') { @@ -74,6 +75,7 @@ class ManagementTokenProvider { telemetry: this.options.telemetry, clientInfo: this.options.clientInfo, headers: this.options.headers, + proxy: this.options.proxy, }; this.authenticationClient = new AuthenticationClient(authenticationClientOptions); diff --git a/test/auth/database-auth.tests.js b/test/auth/database-auth.tests.js index c1bfb82a5..4165b7d79 100644 --- a/test/auth/database-auth.tests.js +++ b/test/auth/database-auth.tests.js @@ -1,5 +1,8 @@ const { expect } = require('chai'); const nock = require('nock'); +const sinon = require('sinon'); +const { Client } = require('rest-facade'); +const proxyquire = require('proxyquire'); const DOMAIN = 'tenant.auth0.com'; const API_URL = `https://${DOMAIN}`; @@ -181,6 +184,41 @@ describe('DatabaseAuthenticator', () => { await this.authenticator.signIn(userData); expect(request.isDone()).to.be.true; }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/DatabaseAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator( + { + ...validOptions, + proxy: 'http://proxy', + }, + new OAuth(validOptions) + ); + + return authenticator.signIn(userData).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); describe('#signUp', () => { @@ -273,6 +311,38 @@ describe('DatabaseAuthenticator', () => { await this.authenticator.signUp(userData); expect(request.isDone()).to.be.true; }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/DatabaseAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator( + { ...validOptions, proxy: 'http://proxy' }, + new OAuth(validOptions) + ); + + return authenticator.signUp(userData).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); describe('#changePassword', () => { @@ -368,6 +438,38 @@ describe('DatabaseAuthenticator', () => { await this.authenticator.changePassword(userData); expect(request.isDone()).to.be.true; }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/DatabaseAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator( + { ...validOptions, proxy: 'http://proxy' }, + new OAuth(validOptions) + ); + + return authenticator.changePassword(userData).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); describe('#requestChangePasswordEmail', () => { @@ -450,5 +552,37 @@ describe('DatabaseAuthenticator', () => { await this.authenticator.requestChangePasswordEmail(userData); expect(request.isDone()).to.be.true; }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/DatabaseAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator( + { ...validOptions, proxy: 'http://proxy' }, + new OAuth(validOptions) + ); + + return authenticator.requestChangePasswordEmail(userData).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); }); diff --git a/test/auth/oauth.tests.js b/test/auth/oauth.tests.js index 22608673e..7387e722d 100644 --- a/test/auth/oauth.tests.js +++ b/test/auth/oauth.tests.js @@ -1,6 +1,8 @@ const { expect } = require('chai'); const nock = require('nock'); const sinon = require('sinon'); +const { Client } = require('rest-facade'); +const proxyquire = require('proxyquire'); const DOMAIN = 'tenant.auth0.com'; const API_URL = `https://${DOMAIN}`; @@ -246,6 +248,35 @@ describe('OAuthAuthenticator', () => { }) .catch(done); }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/OAuthAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator({ ...validOptions, proxy: 'http://proxy' }); + + return authenticator.signIn(userData).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); describe('#passwordGrant', () => { @@ -431,6 +462,35 @@ describe('OAuthAuthenticator', () => { }) .catch(done); }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/OAuthAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator({ ...validOptions, proxy: 'http://proxy' }); + + return authenticator.passwordGrant(userData).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); describe('#refreshToken', () => { @@ -529,6 +589,35 @@ describe('OAuthAuthenticator', () => { }) .catch(done); }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/OAuthAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator({ ...validOptions, proxy: 'http://proxy' }); + + return authenticator.refreshToken(userData).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); describe('#socialSignIn', () => { @@ -653,6 +742,35 @@ describe('OAuthAuthenticator', () => { done(); }); }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/OAuthAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator({ ...validOptions, proxy: 'http://proxy' }); + + return authenticator.socialSignIn(userData).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); describe('#clientCredentials', () => { @@ -785,6 +903,35 @@ describe('OAuthAuthenticator', () => { expect(originalRequestData.client_secret).to.not.equal(CLIENT_SECRET); }); }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/OAuthAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator({ ...validOptions, proxy: 'http://proxy' }); + + return authenticator.clientCredentialsGrant(options).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); describe('#authorizationCodeGrant', () => { @@ -929,5 +1076,34 @@ describe('OAuthAuthenticator', () => { }) .catch(done); }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/OAuthAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator({ ...validOptions, proxy: 'http://proxy' }); + + return authenticator.authorizationCodeGrant(data).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); }); diff --git a/test/auth/passwordless.tests.js b/test/auth/passwordless.tests.js index 6175ee2c8..68e44c825 100644 --- a/test/auth/passwordless.tests.js +++ b/test/auth/passwordless.tests.js @@ -1,5 +1,8 @@ const { expect } = require('chai'); const nock = require('nock'); +const sinon = require('sinon'); +const { Client } = require('rest-facade'); +const proxyquire = require('proxyquire'); const DOMAIN = 'tenant.auth0.com'; const API_URL = `https://${DOMAIN}`; @@ -254,6 +257,38 @@ describe('PasswordlessAuthenticator', () => { }) .catch(done); }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/PasswordlessAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator( + { ...validOptions, proxy: 'http://proxy' }, + new OAuth(validOptions) + ); + + return authenticator.signIn(userData, options).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); describe('/oauth/token', () => { @@ -488,6 +523,38 @@ describe('PasswordlessAuthenticator', () => { }) .catch(done); }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/PasswordlessAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator( + { ...validOptions, proxy: 'http://proxy' }, + new OAuth(validOptions) + ); + + return authenticator.signIn(userData, options).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); }); @@ -668,6 +735,38 @@ describe('PasswordlessAuthenticator', () => { }) .catch(done); }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/PasswordlessAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator( + { ...validOptions, proxy: 'http://proxy' }, + new OAuth(validOptions) + ); + + return authenticator.sendEmail(userData, options).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); describe('#sendSMS', () => { @@ -816,5 +915,37 @@ describe('PasswordlessAuthenticator', () => { }) .catch(done); }); + + it('should make request with proxy', async () => { + nock.cleanAll(); + + const spy = sinon.spy(); + + class MockClient extends Client { + constructor(...args) { + spy(...args); + super(...args); + } + } + const MockAuthenticator = proxyquire(`../../src/auth/PasswordlessAuthenticator`, { + 'rest-facade': { + Client: MockClient, + }, + }); + + const request = nock(API_URL).post(path).reply(200); + + const authenticator = new MockAuthenticator( + { ...validOptions, proxy: 'http://proxy' }, + new OAuth(validOptions) + ); + + return authenticator.sendSMS(userData, options).then(() => { + sinon.assert.calledWithMatch(spy, API_URL, { + proxy: 'http://proxy', + }); + expect(request.isDone()).to.be.true; + }); + }); }); });