@boxyhq/saml-jackson 0.2.3-beta.177 → 0.2.3-beta.210

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/ nodemon.json +12 -0
  2. package/.nyc_output/36a3e9e1-42eb-468d-a9ec-8d206fedcd3e.json +1 -0
  3. package/.nyc_output/8c0af85a-b807-45bb-8331-20c3aabe15df.json +1 -0
  4. package/.nyc_output/ad148b90-7401-4df2-959f-3fdcf81a06ec.json +1 -0
  5. package/.nyc_output/processinfo/36a3e9e1-42eb-468d-a9ec-8d206fedcd3e.json +1 -0
  6. package/.nyc_output/processinfo/8c0af85a-b807-45bb-8331-20c3aabe15df.json +1 -0
  7. package/.nyc_output/processinfo/ad148b90-7401-4df2-959f-3fdcf81a06ec.json +1 -0
  8. package/.nyc_output/processinfo/index.json +1 -0
  9. package/package.json +23 -15
  10. package/.eslintrc.js +0 -13
  11. package/prettier.config.js +0 -4
  12. package/src/controller/api.js +0 -167
  13. package/src/controller/error.js +0 -12
  14. package/src/controller/oauth/allowed.js +0 -19
  15. package/src/controller/oauth/code-verifier.js +0 -16
  16. package/src/controller/oauth/redirect.js +0 -18
  17. package/src/controller/oauth.js +0 -321
  18. package/src/controller/utils.js +0 -19
  19. package/src/db/db.js +0 -81
  20. package/src/db/db.test.js +0 -302
  21. package/src/db/encrypter.js +0 -36
  22. package/src/db/mem.js +0 -111
  23. package/src/db/mongo.js +0 -89
  24. package/src/db/redis.js +0 -88
  25. package/src/db/sql/entity/JacksonIndex.js +0 -42
  26. package/src/db/sql/entity/JacksonStore.js +0 -42
  27. package/src/db/sql/entity/JacksonTTL.js +0 -23
  28. package/src/db/sql/model/JacksonIndex.js +0 -9
  29. package/src/db/sql/model/JacksonStore.js +0 -10
  30. package/src/db/sql/model/JacksonTTL.js +0 -8
  31. package/src/db/sql/sql.js +0 -153
  32. package/src/db/store.js +0 -42
  33. package/src/db/utils.js +0 -30
  34. package/src/env.js +0 -39
  35. package/src/index.js +0 -67
  36. package/src/jackson.js +0 -161
  37. package/src/read-config.js +0 -24
  38. package/src/saml/claims.js +0 -40
  39. package/src/saml/saml.js +0 -223
  40. package/src/saml/x509.js +0 -48
  41. package/src/test/api.test.js +0 -186
  42. package/src/test/data/metadata/boxyhq.js +0 -6
  43. package/src/test/data/metadata/boxyhq.xml +0 -30
  44. package/src/test/data/saml_response +0 -1
  45. package/src/test/oauth.test.js +0 -342
package/src/jackson.js DELETED
@@ -1,161 +0,0 @@
1
- const express = require('express');
2
- const cors = require('cors');
3
-
4
- const env = require('./env.js');
5
- const { extractAuthToken } = require('./controller/utils.js');
6
-
7
- let apiController;
8
- let oauthController;
9
-
10
- const oauthPath = '/oauth';
11
- const apiPath = '/api/v1/saml';
12
-
13
- const app = express();
14
-
15
- app.use(express.json());
16
- app.use(express.urlencoded({ extended: true }));
17
-
18
- app.get(oauthPath + '/authorize', async (req, res) => {
19
- try {
20
- const { redirect_url } = await oauthController.authorize(req.query);
21
-
22
- res.redirect(redirect_url);
23
- } catch (err) {
24
- const { message, statusCode = 500 } = err;
25
-
26
- res.status(statusCode).send(message);
27
- }
28
- });
29
-
30
- app.post(env.samlPath, async (req, res) => {
31
- try {
32
- const { redirect_url } = await oauthController.samlResponse(req.body);
33
-
34
- res.redirect(redirect_url);
35
- } catch (err) {
36
- const { message, statusCode = 500 } = err;
37
-
38
- res.status(statusCode).send(message);
39
- }
40
- });
41
-
42
- app.post(oauthPath + '/token', cors(), async (req, res) => {
43
- try {
44
- const result = await oauthController.token(req.body);
45
-
46
- res.json(result);
47
- } catch (err) {
48
- const { message, statusCode = 500 } = err;
49
-
50
- res.status(statusCode).send(message);
51
- }
52
- });
53
-
54
- app.get(oauthPath + '/userinfo', async (req, res) => {
55
- try {
56
- let token = extractAuthToken(req);
57
-
58
- // check for query param
59
- if (!token) {
60
- token = req.query.access_token;
61
- }
62
-
63
- if (!token) {
64
- res.status(401).json({ message: 'Unauthorized' });
65
- }
66
-
67
- const profile = await oauthController.userInfo(token);
68
-
69
- res.json(profile);
70
- } catch (err) {
71
- const { message, statusCode = 500 } = err;
72
-
73
- res.status(statusCode).json({ message });
74
- }
75
- });
76
-
77
- const server = app.listen(env.hostPort, async () => {
78
- console.log(
79
- `🚀 The path of the righteous server: http://${env.hostUrl}:${env.hostPort}`
80
- );
81
-
82
- const ret = await require('./index.js')(env);
83
- apiController = ret.apiController;
84
- oauthController = ret.oauthController;
85
- });
86
-
87
- // Internal routes, recommended not to expose this to the public interface though it would be guarded by API key(s)
88
- let internalApp = app;
89
-
90
- if (env.useInternalServer) {
91
- internalApp = express();
92
-
93
- internalApp.use(express.json());
94
- internalApp.use(express.urlencoded({ extended: true }));
95
- }
96
-
97
- const validateApiKey = (token) => {
98
- return env.apiKeys.includes(token);
99
- };
100
-
101
- internalApp.post(apiPath + '/config', async (req, res) => {
102
- try {
103
- const apiKey = extractAuthToken(req);
104
- if (!validateApiKey(apiKey)) {
105
- res.status(401).send('Unauthorized');
106
- return;
107
- }
108
-
109
- res.json(await apiController.config(req.body));
110
- } catch (err) {
111
- res.status(500).json({
112
- error: err.message,
113
- });
114
- }
115
- });
116
-
117
- internalApp.get(apiPath + '/config', async (req, res) => {
118
- try {
119
- const apiKey = extractAuthToken(req);
120
- if (!validateApiKey(apiKey)) {
121
- res.status(401).send('Unauthorized');
122
- return;
123
- }
124
-
125
- res.json(await apiController.getConfig(req.query));
126
- } catch (err) {
127
- res.status(500).json({
128
- error: err.message,
129
- });
130
- }
131
- });
132
-
133
- internalApp.delete(apiPath + '/config', async (req, res) => {
134
- try {
135
- const apiKey = extractAuthToken(req);
136
- if (!validateApiKey(apiKey)) {
137
- res.status(401).send('Unauthorized');
138
- return;
139
- }
140
- await apiController.deleteConfig(req.body);
141
- res.status(200).end();
142
- } catch (err) {
143
- res.status(500).json({
144
- error: err.message,
145
- });
146
- }
147
- });
148
-
149
- let internalServer = server;
150
- if (env.useInternalServer) {
151
- internalServer = internalApp.listen(env.internalHostPort, async () => {
152
- console.log(
153
- `🚀 The path of the righteous internal server: http://${env.internalHostUrl}:${env.internalHostPort}`
154
- );
155
- });
156
- }
157
-
158
- module.exports = {
159
- server,
160
- internalServer,
161
- };
@@ -1,24 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- module.exports = async function (preLoadedConfig) {
5
- if (preLoadedConfig.startsWith('./')) {
6
- preLoadedConfig = path.resolve(process.cwd(), preLoadedConfig);
7
- }
8
- const files = await fs.promises.readdir(preLoadedConfig);
9
- const configs = [];
10
- for (let idx in files) {
11
- const file = files[idx];
12
- if (file.endsWith('.js')) {
13
- const config = require(path.join(preLoadedConfig, file));
14
- const rawMetadata = await fs.promises.readFile(
15
- path.join(preLoadedConfig, path.parse(file).name + '.xml'),
16
- 'utf8'
17
- );
18
- config.rawMetadata = rawMetadata;
19
- configs.push(config);
20
- }
21
- }
22
-
23
- return configs;
24
- };
@@ -1,40 +0,0 @@
1
- const mapping = [
2
- {
3
- attribute: 'id',
4
- schema:
5
- 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier',
6
- },
7
- {
8
- attribute: 'email',
9
- schema:
10
- 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
11
- },
12
- {
13
- attribute: 'firstName',
14
- schema: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
15
- },
16
- {
17
- attribute: 'lastName',
18
- schema: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
19
- },
20
- ];
21
-
22
- const map = (claims) => {
23
- const profile = {
24
- raw: claims,
25
- };
26
-
27
- mapping.forEach((m) => {
28
- if (claims[m.attribute]) {
29
- profile[m.attribute] = claims[m.attribute];
30
- } else if (claims[m.schema]) {
31
- profile[m.attribute] = claims[m.schema];
32
- }
33
- });
34
-
35
- return profile;
36
- };
37
-
38
- module.exports = {
39
- map,
40
- };
package/src/saml/saml.js DELETED
@@ -1,223 +0,0 @@
1
- const saml = require('@boxyhq/saml20');
2
- const xml2js = require('xml2js');
3
- const rambda = require('rambda');
4
- const thumbprint = require('thumbprint');
5
- const xmlbuilder = require('xmlbuilder');
6
- const crypto = require('crypto');
7
- const xmlcrypto = require('xml-crypto');
8
- const claims = require('./claims');
9
-
10
- const idPrefix = '_';
11
- const authnXPath =
12
- '/*[local-name(.)="AuthnRequest" and namespace-uri(.)="urn:oasis:names:tc:SAML:2.0:protocol"]';
13
- const issuerXPath =
14
- '/*[local-name(.)="Issuer" and namespace-uri(.)="urn:oasis:names:tc:SAML:2.0:assertion"]';
15
-
16
- const signRequest = (xml, signingKey) => {
17
- if (!xml) {
18
- throw new Error('Please specify xml');
19
- }
20
- if (!signingKey) {
21
- throw new Error('Please specify signingKey');
22
- }
23
-
24
- const sig = new xmlcrypto.SignedXml();
25
- sig.signatureAlgorithm = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
26
- sig.signingKey = signingKey;
27
- sig.addReference(
28
- authnXPath,
29
- [
30
- 'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
31
- 'http://www.w3.org/2001/10/xml-exc-c14n#',
32
- ],
33
- 'http://www.w3.org/2001/04/xmlenc#sha256'
34
- );
35
- sig.computeSignature(xml, {
36
- location: { reference: authnXPath + issuerXPath, action: 'after' },
37
- });
38
-
39
- return sig.getSignedXml();
40
- };
41
-
42
- module.exports = {
43
- request: ({
44
- ssoUrl,
45
- entityID,
46
- callbackUrl,
47
- isPassive = false,
48
- forceAuthn = false,
49
- identifierFormat = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
50
- providerName = 'BoxyHQ',
51
- signingKey,
52
- }) => {
53
- const id = idPrefix + crypto.randomBytes(10).toString('hex');
54
- const date = new Date().toISOString();
55
-
56
- const samlReq = {
57
- 'samlp:AuthnRequest': {
58
- '@xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol',
59
- '@ID': id,
60
- '@Version': '2.0',
61
- '@IssueInstant': date,
62
- '@ProtocolBinding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
63
- '@Destination': ssoUrl,
64
- 'saml:Issuer': {
65
- '@xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion',
66
- '#text': entityID,
67
- },
68
- },
69
- };
70
-
71
- if (isPassive) samlReq['samlp:AuthnRequest']['@IsPassive'] = true;
72
-
73
- if (forceAuthn) {
74
- samlReq['samlp:AuthnRequest']['@ForceAuthn'] = true;
75
- }
76
-
77
- samlReq['samlp:AuthnRequest']['@AssertionConsumerServiceURL'] = callbackUrl;
78
-
79
- samlReq['samlp:AuthnRequest']['samlp:NameIDPolicy'] = {
80
- '@xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol',
81
- '@Format': identifierFormat,
82
- '@AllowCreate': 'true',
83
- };
84
-
85
- if (providerName != null) {
86
- samlReq['samlp:AuthnRequest']['@ProviderName'] = providerName;
87
- }
88
-
89
- let xml = xmlbuilder.create(samlReq).end({});
90
- if (signingKey) {
91
- xml = signRequest(xml, signingKey);
92
- }
93
-
94
- return {
95
- id,
96
- request: xml,
97
- };
98
- },
99
-
100
- parseAsync: async (rawAssertion) => {
101
- return new Promise((resolve, reject) => {
102
- saml.parse(rawAssertion, function onParseAsync(err, profile) {
103
- if (err) {
104
- reject(err);
105
- return;
106
- }
107
-
108
- resolve(profile);
109
- });
110
- });
111
- },
112
-
113
- validateAsync: async (rawAssertion, options) => {
114
- return new Promise((resolve, reject) => {
115
- saml.validate(
116
- rawAssertion,
117
- options,
118
- function onValidateAsync(err, profile) {
119
- if (err) {
120
- reject(err);
121
- return;
122
- }
123
-
124
- if (profile && profile.claims) {
125
- // we map claims to our attributes id, email, firstName, lastName where possible. We also map original claims to raw
126
- profile.claims = claims.map(profile.claims);
127
-
128
- // some providers don't return the id in the assertion, we set it to a sha256 hash of the email
129
- if (!profile.claims.id) {
130
- profile.claims.id = crypto
131
- .createHash('sha256')
132
- .update(profile.claims.email)
133
- .digest('hex');
134
- }
135
- }
136
-
137
- resolve(profile);
138
- }
139
- );
140
- });
141
- },
142
-
143
- parseMetadataAsync: async (idpMeta) => {
144
- return new Promise((resolve, reject) => {
145
- xml2js.parseString(
146
- idpMeta,
147
- { tagNameProcessors: [xml2js.processors.stripPrefix] },
148
- (err, res) => {
149
- if (err) {
150
- reject(err);
151
- return;
152
- }
153
-
154
- const entityID = rambda.path('EntityDescriptor.$.entityID', res);
155
- let X509Certificate = null;
156
- let ssoPostUrl = null;
157
- let ssoRedirectUrl = null;
158
- let loginType = 'idp';
159
-
160
- let ssoDes = rambda.pathOr(
161
- null,
162
- 'EntityDescriptor.IDPSSODescriptor',
163
- res
164
- );
165
- if (!ssoDes) {
166
- ssoDes = rambda.pathOr([], 'EntityDescriptor.SPSSODescriptor', res);
167
- if (!ssoDes) {
168
- loginType = 'sp';
169
- }
170
- }
171
-
172
- for (const ssoDesRec of ssoDes) {
173
- const keyDes = ssoDesRec['KeyDescriptor'];
174
- for (const keyDesRec of keyDes) {
175
- if (keyDesRec['$'] && keyDesRec['$'].use === 'signing') {
176
- const ki = keyDesRec['KeyInfo'][0];
177
- const cd = ki['X509Data'][0];
178
- X509Certificate = cd['X509Certificate'][0];
179
- }
180
- }
181
-
182
- const ssoSvc =
183
- ssoDesRec['SingleSignOnService'] ||
184
- ssoDesRec['AssertionConsumerService'] ||
185
- [];
186
- for (const ssoSvcRec of ssoSvc) {
187
- if (
188
- rambda.pathOr('', '$.Binding', ssoSvcRec).endsWith('HTTP-POST')
189
- ) {
190
- ssoPostUrl = rambda.path('$.Location', ssoSvcRec);
191
- } else if (
192
- rambda
193
- .pathOr('', '$.Binding', ssoSvcRec)
194
- .endsWith('HTTP-Redirect')
195
- ) {
196
- ssoRedirectUrl = rambda.path('$.Location', ssoSvcRec);
197
- }
198
- }
199
- }
200
-
201
- const ret = {
202
- sso: {},
203
- };
204
- if (entityID) {
205
- ret.entityID = entityID;
206
- }
207
- if (X509Certificate) {
208
- ret.thumbprint = thumbprint.calculate(X509Certificate);
209
- }
210
- if (ssoPostUrl) {
211
- ret.sso.postUrl = ssoPostUrl;
212
- }
213
- if (ssoRedirectUrl) {
214
- ret.sso.redirectUrl = ssoRedirectUrl;
215
- }
216
- ret.loginType = loginType;
217
-
218
- resolve(ret);
219
- }
220
- );
221
- });
222
- },
223
- };
package/src/saml/x509.js DELETED
@@ -1,48 +0,0 @@
1
- const x509 = require('@peculiar/x509');
2
- const { Crypto } = require('@peculiar/webcrypto');
3
-
4
- const crypto = new Crypto();
5
- x509.cryptoProvider.set(crypto);
6
-
7
- const alg = {
8
- name: 'RSASSA-PKCS1-v1_5',
9
- hash: 'SHA-256',
10
- publicExponent: new Uint8Array([1, 0, 1]),
11
- modulusLength: 2048,
12
- };
13
-
14
- const generate = async () => {
15
- const keys = await crypto.subtle.generateKey(alg, true, ['sign', 'verify']);
16
-
17
- const extensions = [
18
- new x509.BasicConstraintsExtension(false, undefined, true),
19
- ];
20
-
21
- extensions.push(
22
- new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature, true)
23
- );
24
- extensions.push(
25
- await x509.SubjectKeyIdentifierExtension.create(keys.publicKey)
26
- );
27
-
28
- const cert = await x509.X509CertificateGenerator.createSelfSigned({
29
- serialNumber: '01',
30
- name: 'CN=BoxyHQ Jackson',
31
- notBefore: new Date(),
32
- notAfter: new Date('3021/01/01'), // TODO: set shorter expiry and rotate ceritifcates
33
- signingAlgorithm: alg,
34
- keys: keys,
35
- extensions,
36
- });
37
-
38
- const pkcs8 = await crypto.subtle.exportKey('pkcs8', keys.privateKey);
39
-
40
- return {
41
- publicKey: cert.toString('pem'),
42
- privateKey: x509.PemConverter.encode(pkcs8, 'private key'),
43
- };
44
- };
45
-
46
- module.exports = {
47
- generate,
48
- };
@@ -1,186 +0,0 @@
1
- const tap = require('tap');
2
- const path = require('path');
3
- const sinon = require('sinon');
4
- const crypto = require('crypto');
5
-
6
- const readConfig = require('../read-config');
7
- const dbutils = require('../db/utils');
8
-
9
- let apiController;
10
-
11
- const CLIENT_ID = '75edb050796a0eb1cf2cfb0da7245f85bc50baa7';
12
- const PROVIDER = 'accounts.google.com';
13
- const OPTIONS = {
14
- externalUrl: 'https://my-cool-app.com',
15
- samlAudience: 'https://saml.boxyhq.com',
16
- samlPath: '/sso/oauth/saml',
17
- db: {
18
- engine: 'mem',
19
- },
20
- };
21
-
22
- tap.before(async () => {
23
- const controller = await require('../index.js')(OPTIONS);
24
-
25
- apiController = controller.apiController;
26
- });
27
-
28
- tap.teardown(async () => {
29
- process.exit(0);
30
- });
31
-
32
- tap.test('controller/api', async (t) => {
33
- const metadataPath = path.join(__dirname, '/data/metadata');
34
- const config = await readConfig(metadataPath);
35
-
36
- t.test('.config()', async (t) => {
37
- t.test('when required fields are missing or invalid', async (t) => {
38
- t.test('when `rawMetadata` is empty', async (t) => {
39
- const body = Object.assign({}, config[0]);
40
- delete body['rawMetadata'];
41
-
42
- try {
43
- await apiController.config(body);
44
- t.fail('Expecting JacksonError.');
45
- } catch (err) {
46
- t.equal(err.message, 'Please provide rawMetadata');
47
- t.equal(err.statusCode, 400);
48
- }
49
-
50
- t.end();
51
- });
52
-
53
- t.test('when `defaultRedirectUrl` is empty', async (t) => {
54
- const body = Object.assign({}, config[0]);
55
- delete body['defaultRedirectUrl'];
56
-
57
- try {
58
- await apiController.config(body);
59
- t.fail('Expecting JacksonError.');
60
- } catch (err) {
61
- t.equal(err.message, 'Please provide a defaultRedirectUrl');
62
- t.equal(err.statusCode, 400);
63
- }
64
-
65
- t.end();
66
- });
67
-
68
- t.test('when `redirectUrl` is empty', async (t) => {
69
- const body = Object.assign({}, config[0]);
70
- delete body['redirectUrl'];
71
-
72
- try {
73
- await apiController.config(body);
74
- t.fail('Expecting JacksonError.');
75
- } catch (err) {
76
- t.equal(err.message, 'Please provide redirectUrl');
77
- t.equal(err.statusCode, 400);
78
- }
79
-
80
- t.end();
81
- });
82
-
83
- t.test('when `tenant` is empty', async (t) => {
84
- const body = Object.assign({}, config[0]);
85
- delete body['tenant'];
86
-
87
- try {
88
- await apiController.config(body);
89
- t.fail('Expecting JacksonError.');
90
- } catch (err) {
91
- t.equal(err.message, 'Please provide tenant');
92
- t.equal(err.statusCode, 400);
93
- }
94
-
95
- t.end();
96
- });
97
-
98
- t.test('when `product` is empty', async (t) => {
99
- const body = Object.assign({}, config[0]);
100
- delete body['product'];
101
-
102
- try {
103
- await apiController.config(body);
104
- t.fail('Expecting JacksonError.');
105
- } catch (err) {
106
- t.equal(err.message, 'Please provide product');
107
- t.equal(err.statusCode, 400);
108
- }
109
-
110
- t.end();
111
- });
112
-
113
- t.test('when `rawMetadata` is not a valid XML', async (t) => {
114
- const body = Object.assign({}, config[0]);
115
- body['rawMetadata'] = 'not a valid XML';
116
-
117
- try {
118
- await apiController.config(body);
119
- t.fail('Expecting Error.');
120
- } catch (err) {
121
- t.match(err.message, /Non-whitespace before first tag./);
122
- }
123
-
124
- t.end();
125
- });
126
- });
127
-
128
- t.test('when the request is good', async (t) => {
129
- const body = Object.assign({}, config[0]);
130
-
131
- const kdStub = sinon.stub(dbutils, 'keyDigest').returns(CLIENT_ID);
132
- const rbStub = sinon
133
- .stub(crypto, 'randomBytes')
134
- .returns('f3b0f91eb8f4a9f7cc2254e08682d50b05b5d36262929e7f');
135
-
136
- const response = await apiController.config(body);
137
- t.ok(kdStub.called);
138
- t.ok(rbStub.calledOnce);
139
- t.equal(response.client_id, CLIENT_ID);
140
- t.equal(
141
- response.client_secret,
142
- 'f3b0f91eb8f4a9f7cc2254e08682d50b05b5d36262929e7f'
143
- );
144
- t.equal(response.provider, PROVIDER);
145
-
146
- let savedConf = await apiController.getConfig({
147
- clientID: CLIENT_ID,
148
- });
149
- t.equal(savedConf.provider, PROVIDER);
150
- try {
151
- await apiController.deleteConfig({ clientID: CLIENT_ID });
152
- t.fail('Expecting JacksonError.');
153
- } catch (err) {
154
- t.equal(err.message, 'Please provide clientSecret');
155
- t.equal(err.statusCode, 400);
156
- }
157
- try {
158
- await apiController.deleteConfig({
159
- clientID: CLIENT_ID,
160
- clientSecret: 'xxxxx',
161
- });
162
- t.fail('Expecting JacksonError.');
163
- } catch (err) {
164
- t.equal(err.message, 'clientSecret mismatch');
165
- t.equal(err.statusCode, 400);
166
- }
167
- await apiController.deleteConfig({
168
- clientID: CLIENT_ID,
169
- clientSecret: 'f3b0f91eb8f4a9f7cc2254e08682d50b05b5d36262929e7f',
170
- });
171
- savedConf = await apiController.getConfig({
172
- clientID: CLIENT_ID,
173
- });
174
- t.same(savedConf, {}, 'should return empty config');
175
-
176
- dbutils.keyDigest.restore();
177
- crypto.randomBytes.restore();
178
-
179
- t.end();
180
- });
181
-
182
- t.end();
183
- });
184
-
185
- t.end();
186
- });
@@ -1,6 +0,0 @@
1
- module.exports = {
2
- defaultRedirectUrl: 'http://localhost:3000/sso/oauth/completed',
3
- redirectUrl: '["http://localhost:3000"]',
4
- tenant: 'boxyhq.com',
5
- product: 'crm',
6
- };