diff --git a/lib/auth-client.js b/lib/auth-client.js index d6b5a77f..82fa23ff 100644 --- a/lib/auth-client.js +++ b/lib/auth-client.js @@ -1,5 +1,6 @@ 'use strict'; +const Joi = require('joi'); const qs = require('querystring'); const util = require('./util'); @@ -49,6 +50,25 @@ const config = require('./config'); * development mode to enable the mock vehicle brand. */ function AuthClient(options) { + + const schema = Joi.object().keys({ + clientId: Joi.string().guid({ + version: [ + 'uuidv4', + ], + }).required(), + clientSecret: Joi.string().guid({ + version: [ + 'uuidv4', + ], + }).required(), + redirectUri: Joi.string().uri().required(), + scope: Joi.array().items(Joi.string()), + development: Joi.boolean(), + }); + + Joi.assert(options, schema); + this.clientId = options.clientId; this.clientSecret = options.clientSecret; this.redirectUri = options.redirectUri; diff --git a/package.json b/package.json index 4ee8e71c..c1ec42fe 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ }, "dependencies": { "bluebird": "^3.5.1", + "joi": "^13.4.0", "lodash": "^4.17.5", "request": "^2.83.0", "request-promise": "^4.2.2" diff --git a/test/lib/auth-client.js b/test/lib/auth-client.js index 361a4888..e1315815 100644 --- a/test/lib/auth-client.js +++ b/test/lib/auth-client.js @@ -6,17 +6,21 @@ const nock = require('nock'); const AuthClient = require('../../lib/auth-client'); +const CLIENT_ID = '4cf82729-4275-46d9-9255-8437ba777151'; +const INVALID_CLIENT_ID = '4cf82729-4275-46d9-9255-87ba151'; +const CLIENT_SECRET = '4cf82729-4275-46d9-9255-8437ba777151'; + test('constructor', function(t) { const client = new AuthClient({ - clientId: 'CLIENT_ID', - clientSecret: 'CLIENT_SECRET', + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, redirectUri: 'https://insurance.co/callback', scope: ['read_odometer', 'read_vehicle_info'], }); - t.is(client.clientId, 'CLIENT_ID'); - t.is(client.clientSecret, 'CLIENT_SECRET'); + t.is(client.clientId, CLIENT_ID); + t.is(client.clientSecret, CLIENT_SECRET); t.is(client.redirectUri, 'https://insurance.co/callback'); t.deepEqual(client.scope, ['read_odometer', 'read_vehicle_info']); t.is(client.development, false); @@ -24,36 +28,73 @@ test('constructor', function(t) { }); -test('constructor - development', function(t) { +test('constructor - missing required parameter', function(t) { - const client = new AuthClient({ - clientId: 'CLIENT_ID', - clientSecret: 'CLIENT_SECRET', + t.throws(() => new AuthClient({ + clientId: 'f3266b17-961d-4295-8544-054c7bd94fbb', redirectUri: 'https://insurance.co/callback', scope: ['read_odometer', 'read_vehicle_info'], - development: true, - }); + })); - t.is(client.clientId, 'CLIENT_ID'); - t.is(client.clientSecret, 'CLIENT_SECRET'); - t.is(client.redirectUri, 'https://insurance.co/callback'); - t.deepEqual(client.scope, ['read_odometer', 'read_vehicle_info']); - t.is(client.development, true); - t.true('request' in client); +}); + +test('constructor - invalid uuid parameter', function(t) { + + t.throws(() => new AuthClient({ + clientId: INVALID_CLIENT_ID, + clientSecret: CLIENT_SECRET, + redirectUri: 'https://insurance.co/callback', + scope: ['read_odometer', 'read_vehicle_info'], + })); + +}); + +test('constructor - invalid scope parameter', function(t) { + + t.throws(() => new AuthClient({ + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, + redirectUri: 'https://insurance.co/callback', + scope: 'read_odometer', + })); + +}); + +test('constructor - invalid development parameter', function(t) { + + t.throws(() => new AuthClient({ + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, + redirectUri: 'https://insurance.co/callback', + scope: ['read_odometer', 'read_vehicle_info'], + development: 'truthsies', + })); + +}); + +test('iOS and Android redirect uri', function(t) { + + t.notThrows(() => new AuthClient({ + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, + redirectUri: 'sc4a1b01e5-0497-417c-a30e-6df6ba33ba46://callback', + scope: ['read_odometer', 'read_vehicle_info'], + })); }); test('getAuthUrl - simple', function(t) { const client = new AuthClient({ - clientId: 'CLIENT_ID', + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, redirectUri: 'https://insurance.co/callback', scope: ['read_odometer', 'read_vehicle_info'], }); const actual = client.getAuthUrl(); let expected = 'https://connect.smartcar.com/oauth/authorize?'; - expected += 'response_type=code&client_id=CLIENT_ID'; + expected += `response_type=code&client_id=${CLIENT_ID}`; expected += '&redirect_uri=https%3A%2F%2Finsurance.co%2Fcallback'; expected += '&approval_prompt=auto'; expected += '&scope=read_odometer%20read_vehicle_info'; @@ -65,7 +106,8 @@ test('getAuthUrl - simple', function(t) { test('getAuthUrl - no scope', function(t) { const client = new AuthClient({ - clientId: 'CLIENT_ID', + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, redirectUri: 'https://insurance.co/callback', }); @@ -76,7 +118,7 @@ test('getAuthUrl - no scope', function(t) { }); let expected = 'https://connect.smartcar.com/oauth/authorize?'; - expected += 'response_type=code&client_id=CLIENT_ID'; + expected += `response_type=code&client_id=${CLIENT_ID}`; expected += '&redirect_uri=https%3A%2F%2Finsurance.co%2Fcallback'; expected += '&approval_prompt=force'; expected += '&state=fakestate'; @@ -88,7 +130,8 @@ test('getAuthUrl - no scope', function(t) { test('getAuthUrl - state & approval prompt', function(t) { const client = new AuthClient({ - clientId: 'CLIENT_ID', + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, redirectUri: 'https://insurance.co/callback', scope: ['read_odometer', 'read_vehicle_info'], }); @@ -100,7 +143,7 @@ test('getAuthUrl - state & approval prompt', function(t) { }); let expected = 'https://connect.smartcar.com/oauth/authorize?'; - expected += 'response_type=code&client_id=CLIENT_ID'; + expected += `response_type=code&client_id=${CLIENT_ID}`; expected += '&redirect_uri=https%3A%2F%2Finsurance.co%2Fcallback'; expected += '&approval_prompt=force'; expected += '&scope=read_odometer%20read_vehicle_info'; @@ -113,7 +156,8 @@ test('getAuthUrl - state & approval prompt', function(t) { test('getAuthUrl - development mode', function(t) { const client = new AuthClient({ - clientId: 'CLIENT_ID', + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, redirectUri: 'https://insurance.co/callback', scope: ['read_odometer', 'read_vehicle_info'], development: true, @@ -126,7 +170,7 @@ test('getAuthUrl - development mode', function(t) { }); let expected = 'https://connect.smartcar.com/oauth/authorize?'; - expected += 'response_type=code&client_id=CLIENT_ID'; + expected += `response_type=code&client_id=${CLIENT_ID}`; expected += '&redirect_uri=https%3A%2F%2Finsurance.co%2Fcallback'; expected += '&approval_prompt=force'; expected += '&scope=read_odometer%20read_vehicle_info'; @@ -140,7 +184,8 @@ test('getAuthUrl - development mode', function(t) { test('getAuthUrl - development mode false', function(t) { const client = new AuthClient({ - clientId: 'CLIENT_ID', + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, redirectUri: 'https://insurance.co/callback', scope: ['read_odometer', 'read_vehicle_info'], development: false, @@ -153,7 +198,7 @@ test('getAuthUrl - development mode false', function(t) { }); let expected = 'https://connect.smartcar.com/oauth/authorize?'; - expected += 'response_type=code&client_id=CLIENT_ID'; + expected += `response_type=code&client_id=${CLIENT_ID}`; expected += '&redirect_uri=https%3A%2F%2Finsurance.co%2Fcallback'; expected += '&approval_prompt=force'; expected += '&scope=read_odometer%20read_vehicle_info'; @@ -166,8 +211,8 @@ test('getAuthUrl - development mode false', function(t) { test('exchangeCode', async function(t) { const client = new AuthClient({ - clientId: 'CLIENT_ID', - clientSecret: 'CLIENT_SECRET', + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, redirectUri: 'https://insurance.co/callback', }); @@ -179,8 +224,8 @@ test('exchangeCode', async function(t) { redirect_uri: 'https://insurance.co/callback', }) .basicAuth({ - user: 'CLIENT_ID', - pass: 'CLIENT_SECRET', + user: CLIENT_ID, + pass: CLIENT_SECRET, }) .reply(200, { access_token: 'access', @@ -204,8 +249,8 @@ test('exchangeCode', async function(t) { test('exchangeRefreshToken', async function(t) { const client = new AuthClient({ - clientId: 'CLIENT_ID', - clientSecret: 'CLIENT_SECRET', + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, redirectUri: 'https://insurance.co/callback', }); @@ -216,8 +261,8 @@ test('exchangeRefreshToken', async function(t) { grant_type: 'refresh_token', }) .basicAuth({ - user: 'CLIENT_ID', - pass: 'CLIENT_SECRET', + user: CLIENT_ID, + pass: CLIENT_SECRET, }) .reply(200, { access_token: 'access',