@boxyhq/saml-jackson 1.2.1 → 1.3.0
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.
- package/dist/controller/admin.d.ts +4 -4
- package/dist/controller/admin.js +6 -6
- package/dist/controller/api.d.ts +448 -204
- package/dist/controller/api.js +547 -376
- package/dist/controller/connection/oidc.d.ts +18 -0
- package/dist/controller/connection/oidc.js +145 -0
- package/dist/controller/connection/saml.d.ts +14 -0
- package/dist/controller/connection/saml.js +168 -0
- package/dist/controller/logout.d.ts +3 -3
- package/dist/controller/logout.js +14 -14
- package/dist/controller/oauth.d.ts +26 -8
- package/dist/controller/oauth.js +368 -118
- package/dist/controller/utils.d.ts +10 -2
- package/dist/controller/utils.js +88 -1
- package/dist/directory-sync/DirectoryUsers.js +4 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +26 -14
- package/dist/loadConnection.d.ts +3 -0
- package/dist/{read-config.js → loadConnection.js} +13 -12
- package/dist/opentelemetry/metrics.js +12 -12
- package/dist/saml/x509.d.ts +4 -4
- package/dist/saml/x509.js +38 -42
- package/dist/typings.d.ts +110 -34
- package/package.json +14 -14
- package/dist/read-config.d.ts +0 -3
@@ -1,8 +1,9 @@
|
|
1
|
-
import type { OAuthErrorHandlerParams } from '../typings';
|
1
|
+
import type { ConnectionType, OAuthErrorHandlerParams, OIDCSSOConnection, SAMLSSOConnectionWithEncodedMetadata, SAMLSSOConnectionWithRawMetadata } from '../typings';
|
2
2
|
import * as jose from 'jose';
|
3
3
|
export declare enum IndexNames {
|
4
4
|
EntityID = "entityID",
|
5
|
-
TenantProduct = "tenantProduct"
|
5
|
+
TenantProduct = "tenantProduct",
|
6
|
+
OIDCProviderClientID = "OIDCProviderClientID"
|
6
7
|
}
|
7
8
|
export declare const storeNamespacePrefix: {
|
8
9
|
dsync: {
|
@@ -29,3 +30,10 @@ export declare function isJWSKeyPairLoaded(jwsKeyPair: {
|
|
29
30
|
export declare const importJWTPublicKey: (key: string, jwsAlg: string) => Promise<jose.KeyLike>;
|
30
31
|
export declare const exportPublicKeyJWK: (key: jose.KeyLike) => Promise<jose.JWK>;
|
31
32
|
export declare const generateJwkThumbprint: (jwk: jose.JWK) => Promise<string>;
|
33
|
+
export declare const validateSSOConnection: (body: SAMLSSOConnectionWithRawMetadata | SAMLSSOConnectionWithEncodedMetadata | OIDCSSOConnection, strategy: ConnectionType) => void;
|
34
|
+
export declare const validateRedirectUrl: ({ redirectUrlList, defaultRedirectUrl }: {
|
35
|
+
redirectUrlList: any;
|
36
|
+
defaultRedirectUrl: any;
|
37
|
+
}) => void;
|
38
|
+
export declare const extractRedirectUrls: (urls: string[] | string) => string[];
|
39
|
+
export declare const extractHostName: (url: string) => string | null;
|
package/dist/controller/utils.js
CHANGED
@@ -35,7 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
36
36
|
};
|
37
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
38
|
-
exports.generateJwkThumbprint = exports.exportPublicKeyJWK = exports.importJWTPublicKey = exports.isJWSKeyPairLoaded = exports.loadJWSPrivateKey = exports.createRandomSecret = exports.getErrorMessage = exports.OAuthErrorResponse = exports.validateAbsoluteUrl = exports.relayStatePrefix = exports.storeNamespacePrefix = exports.IndexNames = void 0;
|
38
|
+
exports.extractHostName = exports.extractRedirectUrls = exports.validateRedirectUrl = exports.validateSSOConnection = exports.generateJwkThumbprint = exports.exportPublicKeyJWK = exports.importJWTPublicKey = exports.isJWSKeyPairLoaded = exports.loadJWSPrivateKey = exports.createRandomSecret = exports.getErrorMessage = exports.OAuthErrorResponse = exports.validateAbsoluteUrl = exports.relayStatePrefix = exports.storeNamespacePrefix = exports.IndexNames = void 0;
|
39
39
|
const error_1 = require("./error");
|
40
40
|
const redirect = __importStar(require("./oauth/redirect"));
|
41
41
|
const crypto_1 = __importDefault(require("crypto"));
|
@@ -44,6 +44,7 @@ var IndexNames;
|
|
44
44
|
(function (IndexNames) {
|
45
45
|
IndexNames["EntityID"] = "entityID";
|
46
46
|
IndexNames["TenantProduct"] = "tenantProduct";
|
47
|
+
IndexNames["OIDCProviderClientID"] = "OIDCProviderClientID";
|
47
48
|
})(IndexNames = exports.IndexNames || (exports.IndexNames = {}));
|
48
49
|
// The namespace prefix for the database store
|
49
50
|
exports.storeNamespacePrefix = {
|
@@ -120,3 +121,89 @@ const generateJwkThumbprint = (jwk) => __awaiter(void 0, void 0, void 0, functio
|
|
120
121
|
return thumbprint;
|
121
122
|
});
|
122
123
|
exports.generateJwkThumbprint = generateJwkThumbprint;
|
124
|
+
const validateSSOConnection = (body, strategy) => {
|
125
|
+
const { defaultRedirectUrl, redirectUrl, tenant, product, description } = body;
|
126
|
+
const encodedRawMetadata = 'encodedRawMetadata' in body ? body.encodedRawMetadata : undefined;
|
127
|
+
const rawMetadata = 'rawMetadata' in body ? body.rawMetadata : undefined;
|
128
|
+
const oidcDiscoveryUrl = 'oidcDiscoveryUrl' in body ? body.oidcDiscoveryUrl : undefined;
|
129
|
+
const oidcClientId = 'oidcClientId' in body ? body.oidcClientId : undefined;
|
130
|
+
const oidcClientSecret = 'oidcClientSecret' in body ? body.oidcClientSecret : undefined;
|
131
|
+
if (strategy !== 'saml' && strategy !== 'oidc') {
|
132
|
+
throw new error_1.JacksonError(`Strategy: ${strategy} not supported`, 400);
|
133
|
+
}
|
134
|
+
if (strategy === 'saml') {
|
135
|
+
if (!rawMetadata && !encodedRawMetadata) {
|
136
|
+
throw new error_1.JacksonError('Please provide rawMetadata or encodedRawMetadata', 400);
|
137
|
+
}
|
138
|
+
}
|
139
|
+
if (strategy === 'oidc') {
|
140
|
+
if (!oidcClientId) {
|
141
|
+
throw new error_1.JacksonError('Please provide the clientId from OpenID Provider', 400);
|
142
|
+
}
|
143
|
+
if (!oidcClientSecret) {
|
144
|
+
throw new error_1.JacksonError('Please provide the clientSecret from OpenID Provider', 400);
|
145
|
+
}
|
146
|
+
if (!oidcDiscoveryUrl) {
|
147
|
+
throw new error_1.JacksonError('Please provide the discoveryUrl for the OpenID Provider', 400);
|
148
|
+
}
|
149
|
+
}
|
150
|
+
if (!defaultRedirectUrl) {
|
151
|
+
throw new error_1.JacksonError('Please provide a defaultRedirectUrl', 400);
|
152
|
+
}
|
153
|
+
if (!redirectUrl) {
|
154
|
+
throw new error_1.JacksonError('Please provide redirectUrl', 400);
|
155
|
+
}
|
156
|
+
if (!tenant) {
|
157
|
+
throw new error_1.JacksonError('Please provide tenant', 400);
|
158
|
+
}
|
159
|
+
if (!product) {
|
160
|
+
throw new error_1.JacksonError('Please provide product', 400);
|
161
|
+
}
|
162
|
+
if (description && description.length > 100) {
|
163
|
+
throw new error_1.JacksonError('Description should not exceed 100 characters', 400);
|
164
|
+
}
|
165
|
+
};
|
166
|
+
exports.validateSSOConnection = validateSSOConnection;
|
167
|
+
const validateRedirectUrl = ({ redirectUrlList, defaultRedirectUrl }) => {
|
168
|
+
if (redirectUrlList) {
|
169
|
+
if (redirectUrlList.length > 100) {
|
170
|
+
throw new error_1.JacksonError('Exceeded maximum number of allowed redirect urls', 400);
|
171
|
+
}
|
172
|
+
for (const url of redirectUrlList) {
|
173
|
+
(0, exports.validateAbsoluteUrl)(url, 'redirectUrl is invalid');
|
174
|
+
}
|
175
|
+
}
|
176
|
+
if (defaultRedirectUrl) {
|
177
|
+
(0, exports.validateAbsoluteUrl)(defaultRedirectUrl, 'defaultRedirectUrl is invalid');
|
178
|
+
}
|
179
|
+
};
|
180
|
+
exports.validateRedirectUrl = validateRedirectUrl;
|
181
|
+
const extractRedirectUrls = (urls) => {
|
182
|
+
if (!urls) {
|
183
|
+
return [];
|
184
|
+
}
|
185
|
+
if (typeof urls === 'string') {
|
186
|
+
if (urls.startsWith('[')) {
|
187
|
+
// redirectUrl is a stringified array
|
188
|
+
return JSON.parse(urls);
|
189
|
+
}
|
190
|
+
// redirectUrl is a single URL
|
191
|
+
return [urls];
|
192
|
+
}
|
193
|
+
// redirectUrl is an array of URLs
|
194
|
+
return urls;
|
195
|
+
};
|
196
|
+
exports.extractRedirectUrls = extractRedirectUrls;
|
197
|
+
const extractHostName = (url) => {
|
198
|
+
try {
|
199
|
+
const pUrl = new URL(url);
|
200
|
+
if (pUrl.hostname.startsWith('www.')) {
|
201
|
+
return pUrl.hostname.substring(4);
|
202
|
+
}
|
203
|
+
return pUrl.hostname;
|
204
|
+
}
|
205
|
+
catch (err) {
|
206
|
+
return null;
|
207
|
+
}
|
208
|
+
};
|
209
|
+
exports.extractHostName = extractHostName;
|
@@ -143,6 +143,10 @@ class DirectoryUsers {
|
|
143
143
|
this.users.setTenantAndProduct(directory.tenant, directory.product);
|
144
144
|
// Get the user
|
145
145
|
const { data: user } = userId ? yield this.users.get(userId) : { data: null };
|
146
|
+
// Delete password if exists in the body
|
147
|
+
if (body && 'password' in body) {
|
148
|
+
delete body['password'];
|
149
|
+
}
|
146
150
|
if (user) {
|
147
151
|
switch (method) {
|
148
152
|
case 'GET':
|
package/dist/index.d.ts
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
import type { DirectorySync, JacksonOption } from './typings';
|
2
2
|
import { AdminController } from './controller/admin';
|
3
|
-
import {
|
3
|
+
import { ConnectionAPIController } from './controller/api';
|
4
4
|
import { OAuthController } from './controller/oauth';
|
5
5
|
import { HealthCheckController } from './controller/health-check';
|
6
6
|
import { LogoutController } from './controller/logout';
|
7
7
|
import { OidcDiscoveryController } from './controller/oidc-discovery';
|
8
8
|
import { SPSAMLConfig } from './controller/sp-config';
|
9
9
|
export declare const controllers: (opts: JacksonOption) => Promise<{
|
10
|
-
apiController:
|
10
|
+
apiController: ConnectionAPIController;
|
11
|
+
connectionAPIController: ConnectionAPIController;
|
11
12
|
oauthController: OAuthController;
|
12
13
|
adminController: AdminController;
|
13
14
|
logoutController: LogoutController;
|
package/dist/index.js
CHANGED
@@ -29,7 +29,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.controllers = void 0;
|
30
30
|
const db_1 = __importDefault(require("./db/db"));
|
31
31
|
const defaultDb_1 = __importDefault(require("./db/defaultDb"));
|
32
|
-
const
|
32
|
+
const loadConnection_1 = __importDefault(require("./loadConnection"));
|
33
33
|
const admin_1 = require("./controller/admin");
|
34
34
|
const api_1 = require("./controller/api");
|
35
35
|
const oauth_1 = require("./controller/oauth");
|
@@ -47,8 +47,13 @@ const defaultOpts = (opts) => {
|
|
47
47
|
throw new Error('samlPath is required');
|
48
48
|
}
|
49
49
|
newOpts.scimPath = newOpts.scimPath || '/api/scim/v2.0';
|
50
|
+
if (!newOpts.oidcPath) {
|
51
|
+
throw new Error('oidcPath is required');
|
52
|
+
}
|
50
53
|
newOpts.samlAudience = newOpts.samlAudience || 'https://saml.boxyhq.com';
|
51
|
-
|
54
|
+
// path to folder containing static IdP connections that will be preloaded. This is useful for self-hosted deployments that only have to support a single tenant (or small number of known tenants).
|
55
|
+
newOpts.preLoadedConnection = newOpts.preLoadedConnection || '';
|
56
|
+
newOpts.preLoadedConfig = newOpts.preLoadedConfig || ''; // for backwards compatibility
|
52
57
|
newOpts.idpEnabled = newOpts.idpEnabled === true;
|
53
58
|
(0, defaultDb_1.default)(newOpts);
|
54
59
|
newOpts.clientSecretVerifier = newOpts.clientSecretVerifier || 'dummy';
|
@@ -60,43 +65,50 @@ const defaultOpts = (opts) => {
|
|
60
65
|
const controllers = (opts) => __awaiter(void 0, void 0, void 0, function* () {
|
61
66
|
opts = defaultOpts(opts);
|
62
67
|
const db = yield db_1.default.new(opts.db);
|
63
|
-
const
|
68
|
+
const connectionStore = db.store('saml:config');
|
64
69
|
const sessionStore = db.store('oauth:session', opts.db.ttl);
|
65
70
|
const codeStore = db.store('oauth:code', opts.db.ttl);
|
66
71
|
const tokenStore = db.store('oauth:token', opts.db.ttl);
|
67
72
|
const healthCheckStore = db.store('_health:check');
|
68
|
-
const
|
69
|
-
const adminController = new admin_1.AdminController({
|
73
|
+
const connectionAPIController = new api_1.ConnectionAPIController({ connectionStore });
|
74
|
+
const adminController = new admin_1.AdminController({ connectionStore });
|
70
75
|
const healthCheckController = new health_check_1.HealthCheckController({ healthCheckStore });
|
71
76
|
yield healthCheckController.init();
|
72
77
|
const oauthController = new oauth_1.OAuthController({
|
73
|
-
|
78
|
+
connectionStore,
|
74
79
|
sessionStore,
|
75
80
|
codeStore,
|
76
81
|
tokenStore,
|
77
82
|
opts,
|
78
83
|
});
|
79
84
|
const logoutController = new logout_1.LogoutController({
|
80
|
-
|
85
|
+
connectionStore,
|
81
86
|
sessionStore,
|
82
87
|
opts,
|
83
88
|
});
|
84
89
|
const directorySync = yield (0, directory_sync_1.default)({ db, opts });
|
85
90
|
const oidcDiscoveryController = new oidc_discovery_1.OidcDiscoveryController({ opts });
|
86
91
|
const spConfig = new sp_config_1.SPSAMLConfig(opts);
|
87
|
-
// write pre-loaded
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
92
|
+
// write pre-loaded connections if present
|
93
|
+
const preLoadedConnection = opts.preLoadedConnection || opts.preLoadedConfig;
|
94
|
+
if (preLoadedConnection && preLoadedConnection.length > 0) {
|
95
|
+
const connections = yield (0, loadConnection_1.default)(preLoadedConnection);
|
96
|
+
for (const connection of connections) {
|
97
|
+
if ('oidcDiscoveryUrl' in connection) {
|
98
|
+
yield connectionAPIController.createOIDCConnection(connection);
|
99
|
+
}
|
100
|
+
else {
|
101
|
+
yield connectionAPIController.createSAMLConnection(connection);
|
102
|
+
}
|
103
|
+
console.log(`loaded connection for tenant "${connection.tenant}" and product "${connection.product}"`);
|
93
104
|
}
|
94
105
|
}
|
95
106
|
const type = opts.db.engine === 'sql' && opts.db.type ? ' Type: ' + opts.db.type : '';
|
96
107
|
console.log(`Using engine: ${opts.db.engine}.${type}`);
|
97
108
|
return {
|
98
109
|
spConfig,
|
99
|
-
apiController,
|
110
|
+
apiController: connectionAPIController,
|
111
|
+
connectionAPIController,
|
100
112
|
oauthController,
|
101
113
|
adminController,
|
102
114
|
logoutController,
|
@@ -0,0 +1,3 @@
|
|
1
|
+
import { OIDCSSOConnection, SAMLSSOConnectionWithEncodedMetadata, SAMLSSOConnectionWithRawMetadata } from './typings';
|
2
|
+
declare const loadConnection: (preLoadedConnection: string) => Promise<(SAMLSSOConnectionWithEncodedMetadata | SAMLSSOConnectionWithRawMetadata | OIDCSSOConnection)[]>;
|
3
|
+
export default loadConnection;
|
@@ -34,22 +34,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
35
35
|
const fs = __importStar(require("fs"));
|
36
36
|
const path = __importStar(require("path"));
|
37
|
-
const
|
38
|
-
if (
|
39
|
-
|
37
|
+
const loadConnection = (preLoadedConnection) => __awaiter(void 0, void 0, void 0, function* () {
|
38
|
+
if (preLoadedConnection.startsWith('./')) {
|
39
|
+
preLoadedConnection = path.resolve(process.cwd(), preLoadedConnection);
|
40
40
|
}
|
41
|
-
const files = yield fs.promises.readdir(
|
42
|
-
const
|
41
|
+
const files = yield fs.promises.readdir(preLoadedConnection);
|
42
|
+
const connections = [];
|
43
43
|
for (const idx in files) {
|
44
44
|
const file = files[idx];
|
45
45
|
if (file.endsWith('.js')) {
|
46
|
-
const { default:
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
const { default: connection, } = yield Promise.resolve().then(() => __importStar(require(/* webpackIgnore: true */ path.join(preLoadedConnection, file))));
|
47
|
+
if (!('oidcDiscoveryUrl' in connection)) {
|
48
|
+
const rawMetadata = yield fs.promises.readFile(path.join(preLoadedConnection, path.parse(file).name + '.xml'), 'utf8');
|
49
|
+
connection.encodedRawMetadata = Buffer.from(rawMetadata, 'utf8').toString('base64');
|
50
|
+
}
|
51
|
+
connections.push(connection);
|
51
52
|
}
|
52
53
|
}
|
53
|
-
return
|
54
|
+
return connections;
|
54
55
|
});
|
55
|
-
exports.default =
|
56
|
+
exports.default = loadConnection;
|
@@ -3,29 +3,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.increment = void 0;
|
4
4
|
const api_metrics_1 = require("@opentelemetry/api-metrics");
|
5
5
|
const counters = {
|
6
|
-
|
7
|
-
name: 'jackson.
|
8
|
-
description: 'Number of
|
6
|
+
createConnection: {
|
7
|
+
name: 'jackson.connection.create',
|
8
|
+
description: 'Number of IdP connection create requests',
|
9
9
|
},
|
10
|
-
|
11
|
-
name: 'jackson.
|
12
|
-
description: 'Number of
|
10
|
+
getConnections: {
|
11
|
+
name: 'jackson.connections.get',
|
12
|
+
description: 'Number of IdP connections get requests',
|
13
13
|
},
|
14
|
-
|
15
|
-
name: 'jackson.
|
16
|
-
description: 'Number of
|
14
|
+
deleteConnections: {
|
15
|
+
name: 'jackson.connections.delete',
|
16
|
+
description: 'Number of IdP connections delete requests',
|
17
17
|
},
|
18
18
|
oauthAuthorize: {
|
19
19
|
name: 'jackson.oauth.authorize',
|
20
|
-
description: 'Number of
|
20
|
+
description: 'Number of oauth authorize requests',
|
21
21
|
},
|
22
22
|
oauthToken: {
|
23
23
|
name: 'jackson.oauth.token',
|
24
|
-
description: 'Number of
|
24
|
+
description: 'Number of oauth token requests',
|
25
25
|
},
|
26
26
|
oauthUserInfo: {
|
27
27
|
name: 'jackson.oauth.userinfo',
|
28
|
-
description: 'Number of
|
28
|
+
description: 'Number of oauth user info requests',
|
29
29
|
},
|
30
30
|
};
|
31
31
|
const createCounter = (action) => {
|
package/dist/saml/x509.d.ts
CHANGED
package/dist/saml/x509.js
CHANGED
@@ -22,50 +22,46 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
22
22
|
__setModuleDefault(result, mod);
|
23
23
|
return result;
|
24
24
|
};
|
25
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
26
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
27
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
28
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
29
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
30
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
31
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
32
|
-
});
|
33
|
-
};
|
34
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
35
|
-
const
|
36
|
-
const
|
37
|
-
const
|
38
|
-
|
39
|
-
const
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
26
|
+
const forge = __importStar(require("node-forge"));
|
27
|
+
const pki = forge.pki;
|
28
|
+
const generate = () => {
|
29
|
+
const today = new Date();
|
30
|
+
const keys = pki.rsa.generateKeyPair(2048);
|
31
|
+
const cert = pki.createCertificate();
|
32
|
+
cert.publicKey = keys.publicKey;
|
33
|
+
cert.serialNumber = '01';
|
34
|
+
cert.validity.notBefore = new Date();
|
35
|
+
cert.validity.notAfter = new Date(today.setFullYear(today.getFullYear() + 10));
|
36
|
+
const attrs = [
|
37
|
+
{
|
38
|
+
name: 'commonName',
|
39
|
+
value: 'BoxyHQ Jackson',
|
40
|
+
},
|
41
|
+
];
|
42
|
+
cert.setSubject(attrs);
|
43
|
+
cert.setIssuer(attrs);
|
44
|
+
cert.setExtensions([
|
45
|
+
{
|
46
|
+
name: 'basicConstraints',
|
47
|
+
cA: false,
|
48
|
+
},
|
49
|
+
{
|
50
|
+
name: 'keyUsage',
|
51
|
+
keyCertSign: false,
|
52
|
+
digitalSignature: true,
|
53
|
+
nonRepudiation: false,
|
54
|
+
keyEncipherment: false,
|
55
|
+
dataEncipherment: false,
|
56
|
+
},
|
57
|
+
]);
|
58
|
+
// self-sign certificate
|
59
|
+
cert.sign(keys.privateKey, forge.md.sha256.create());
|
60
|
+
return {
|
61
|
+
publicKey: pki.certificateToPem(cert),
|
62
|
+
privateKey: pki.privateKeyToPem(keys.privateKey),
|
63
|
+
};
|
44
64
|
};
|
45
|
-
const generate = () => __awaiter(void 0, void 0, void 0, function* () {
|
46
|
-
const keys = yield crypto.subtle.generateKey(alg, true, ['sign', 'verify']);
|
47
|
-
const extensions = [new x509.BasicConstraintsExtension(false, undefined, true)];
|
48
|
-
extensions.push(new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature, true));
|
49
|
-
if (keys.publicKey) {
|
50
|
-
extensions.push(yield x509.SubjectKeyIdentifierExtension.create(keys.publicKey));
|
51
|
-
}
|
52
|
-
const cert = yield x509.X509CertificateGenerator.createSelfSigned({
|
53
|
-
serialNumber: '01',
|
54
|
-
name: 'CN=BoxyHQ Jackson',
|
55
|
-
notBefore: new Date(),
|
56
|
-
notAfter: new Date('3021/01/01'),
|
57
|
-
signingAlgorithm: alg,
|
58
|
-
keys: keys,
|
59
|
-
extensions,
|
60
|
-
});
|
61
|
-
if (keys.privateKey) {
|
62
|
-
const pkcs8 = yield crypto.subtle.exportKey('pkcs8', keys.privateKey);
|
63
|
-
return {
|
64
|
-
publicKey: cert.toString('pem'),
|
65
|
-
privateKey: x509.PemConverter.encode(pkcs8, 'private key'),
|
66
|
-
};
|
67
|
-
}
|
68
|
-
});
|
69
65
|
exports.default = {
|
70
66
|
generate,
|
71
67
|
};
|
package/dist/typings.d.ts
CHANGED
@@ -1,31 +1,68 @@
|
|
1
1
|
import { type JWK } from 'jose';
|
2
|
-
|
2
|
+
interface SSOConnection {
|
3
3
|
defaultRedirectUrl: string;
|
4
4
|
redirectUrl: string[] | string;
|
5
5
|
tenant: string;
|
6
6
|
product: string;
|
7
7
|
name?: string;
|
8
8
|
description?: string;
|
9
|
-
|
10
|
-
|
9
|
+
}
|
10
|
+
export interface SAMLSSOConnection extends SSOConnection {
|
11
|
+
forceAuthn?: boolean | string;
|
12
|
+
}
|
13
|
+
export interface SAMLSSOConnectionWithRawMetadata extends SAMLSSOConnection {
|
14
|
+
rawMetadata: string;
|
15
|
+
encodedRawMetadata?: never;
|
16
|
+
}
|
17
|
+
export interface SAMLSSOConnectionWithEncodedMetadata extends SAMLSSOConnection {
|
18
|
+
rawMetadata?: never;
|
19
|
+
encodedRawMetadata: string;
|
20
|
+
}
|
21
|
+
export interface OIDCSSOConnection extends SSOConnection {
|
22
|
+
oidcDiscoveryUrl: string;
|
23
|
+
oidcClientId: string;
|
24
|
+
oidcClientSecret: string;
|
25
|
+
}
|
26
|
+
export declare type ConnectionType = 'saml' | 'oidc';
|
27
|
+
declare type ClientIDQuery = {
|
28
|
+
clientID: string;
|
11
29
|
};
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
30
|
+
declare type TenantQuery = {
|
31
|
+
tenant: string;
|
32
|
+
product: string;
|
33
|
+
strategy?: ConnectionType;
|
34
|
+
};
|
35
|
+
export declare type GetConnectionsQuery = ClientIDQuery | TenantQuery;
|
36
|
+
export declare type DelConnectionsQuery = (ClientIDQuery & {
|
37
|
+
clientSecret: string;
|
38
|
+
}) | TenantQuery;
|
39
|
+
export declare type GetConfigQuery = ClientIDQuery | Omit<TenantQuery, 'strategy'>;
|
40
|
+
export declare type DelConfigQuery = (ClientIDQuery & {
|
41
|
+
clientSecret: string;
|
42
|
+
}) | Omit<TenantQuery, 'strategy'>;
|
43
|
+
export interface IConnectionAPIController {
|
44
|
+
config(body: SAMLSSOConnection): Promise<any>;
|
45
|
+
createSAMLConnection(body: SAMLSSOConnectionWithRawMetadata | SAMLSSOConnectionWithEncodedMetadata): Promise<any>;
|
46
|
+
createOIDCConnection(body: OIDCSSOConnection): Promise<any>;
|
47
|
+
updateConfig(body: SAMLSSOConnection & {
|
48
|
+
clientID: string;
|
49
|
+
clientSecret: string;
|
50
|
+
}): Promise<any>;
|
51
|
+
updateSAMLConnection(body: (SAMLSSOConnectionWithRawMetadata | SAMLSSOConnectionWithEncodedMetadata) & {
|
52
|
+
clientID: string;
|
53
|
+
clientSecret: string;
|
54
|
+
}): Promise<any>;
|
55
|
+
updateOIDCConnection(body: OIDCSSOConnection & {
|
56
|
+
clientID: string;
|
57
|
+
clientSecret: string;
|
19
58
|
}): Promise<any>;
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
product?: string;
|
25
|
-
}): Promise<void>;
|
59
|
+
getConnections(body: GetConnectionsQuery): Promise<Array<any>>;
|
60
|
+
getConfig(body: GetConfigQuery): Promise<any>;
|
61
|
+
deleteConnections(body: DelConnectionsQuery): Promise<void>;
|
62
|
+
deleteConfig(body: DelConfigQuery): Promise<void>;
|
26
63
|
}
|
27
64
|
export interface IOAuthController {
|
28
|
-
authorize(body:
|
65
|
+
authorize(body: OAuthReq): Promise<{
|
29
66
|
redirect_url?: string;
|
30
67
|
authorize_form?: string;
|
31
68
|
}>;
|
@@ -33,11 +70,14 @@ export interface IOAuthController {
|
|
33
70
|
redirect_url?: string;
|
34
71
|
app_select_form?: string;
|
35
72
|
}>;
|
73
|
+
oidcAuthzResponse(body: OIDCAuthzResponsePayload): Promise<{
|
74
|
+
redirect_url?: string;
|
75
|
+
}>;
|
36
76
|
token(body: OAuthTokenReq): Promise<OAuthTokenRes>;
|
37
77
|
userInfo(token: string): Promise<Profile>;
|
38
78
|
}
|
39
79
|
export interface IAdminController {
|
40
|
-
|
80
|
+
getAllConnection(pageOffset?: number, pageLimit?: number): any;
|
41
81
|
}
|
42
82
|
export interface IHealthCheckController {
|
43
83
|
status(): Promise<{
|
@@ -70,34 +110,67 @@ export interface IOidcDiscoveryController {
|
|
70
110
|
}>;
|
71
111
|
}
|
72
112
|
export interface OAuthReqBody {
|
113
|
+
state: string;
|
73
114
|
response_type: 'code';
|
74
|
-
client_id: string;
|
75
115
|
redirect_uri: string;
|
76
|
-
state: string;
|
77
|
-
tenant?: string;
|
78
|
-
product?: string;
|
79
|
-
access_type?: string;
|
80
|
-
resource?: string;
|
81
|
-
scope?: string;
|
82
|
-
nonce?: string;
|
83
116
|
code_challenge: string;
|
84
117
|
code_challenge_method: 'plain' | 'S256' | '';
|
85
|
-
|
118
|
+
scope?: string;
|
119
|
+
nonce?: string;
|
86
120
|
idp_hint?: string;
|
121
|
+
prompt?: string;
|
122
|
+
}
|
123
|
+
export interface OAuthReqBodyWithClientId extends OAuthReqBody {
|
124
|
+
client_id: string;
|
125
|
+
}
|
126
|
+
export interface OAuthReqBodyWithTenantProduct extends OAuthReqBody {
|
127
|
+
client_id: 'dummy';
|
128
|
+
tenant: string;
|
129
|
+
product: string;
|
130
|
+
}
|
131
|
+
export interface OAuthReqBodyWithAccessType extends OAuthReqBody {
|
132
|
+
client_id: 'dummy';
|
133
|
+
access_type: string;
|
87
134
|
}
|
135
|
+
export interface OAuthReqBodyWithResource extends OAuthReqBody {
|
136
|
+
client_id: 'dummy';
|
137
|
+
resource: string;
|
138
|
+
}
|
139
|
+
export declare type OAuthReq = OAuthReqBodyWithClientId | OAuthReqBodyWithTenantProduct | OAuthReqBodyWithAccessType | OAuthReqBodyWithResource;
|
88
140
|
export interface SAMLResponsePayload {
|
89
141
|
SAMLResponse: string;
|
90
142
|
RelayState: string;
|
91
143
|
idp_hint?: string;
|
92
144
|
}
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
145
|
+
interface OIDCAuthzResponseSuccess {
|
146
|
+
code: string;
|
147
|
+
state: string;
|
148
|
+
error?: never;
|
149
|
+
error_description?: never;
|
150
|
+
}
|
151
|
+
interface OIDCAuthzResponseError {
|
152
|
+
code?: never;
|
153
|
+
state: string;
|
154
|
+
error: OAuthErrorHandlerParams['error'] | OIDCErrorCodes;
|
155
|
+
error_description?: string;
|
156
|
+
}
|
157
|
+
export declare type OIDCAuthzResponsePayload = OIDCAuthzResponseSuccess | OIDCAuthzResponseError;
|
158
|
+
interface OAuthTokenReqBody {
|
97
159
|
code: string;
|
98
160
|
grant_type: 'authorization_code';
|
99
|
-
redirect_uri
|
161
|
+
redirect_uri: string;
|
162
|
+
}
|
163
|
+
export interface OAuthTokenReqWithCodeVerifier extends OAuthTokenReqBody {
|
164
|
+
code_verifier: string;
|
165
|
+
client_id?: never;
|
166
|
+
client_secret?: never;
|
167
|
+
}
|
168
|
+
export interface OAuthTokenReqWithCredentials extends OAuthTokenReqBody {
|
169
|
+
code_verifier?: never;
|
170
|
+
client_id: string;
|
171
|
+
client_secret: string;
|
100
172
|
}
|
173
|
+
export declare type OAuthTokenReq = OAuthTokenReqWithCodeVerifier | OAuthTokenReqWithCredentials;
|
101
174
|
export interface OAuthTokenRes {
|
102
175
|
access_token: string;
|
103
176
|
id_token?: string;
|
@@ -154,8 +227,10 @@ export interface DatabaseOption {
|
|
154
227
|
export interface JacksonOption {
|
155
228
|
externalUrl: string;
|
156
229
|
samlPath: string;
|
230
|
+
oidcPath: string;
|
157
231
|
samlAudience?: string;
|
158
232
|
preLoadedConfig?: string;
|
233
|
+
preLoadedConnection?: string;
|
159
234
|
idpEnabled?: boolean;
|
160
235
|
db: DatabaseOption;
|
161
236
|
clientSecretVerifier?: string;
|
@@ -189,7 +264,7 @@ interface Metadata {
|
|
189
264
|
loginType: 'idp' | 'sp';
|
190
265
|
provider: string;
|
191
266
|
}
|
192
|
-
export interface
|
267
|
+
export interface SAMLConnection {
|
193
268
|
idpMetadata: Metadata;
|
194
269
|
certs: {
|
195
270
|
privateKey: string;
|
@@ -198,11 +273,12 @@ export interface SAMLConfig {
|
|
198
273
|
defaultRedirectUrl: string;
|
199
274
|
}
|
200
275
|
export interface OAuthErrorHandlerParams {
|
201
|
-
error: 'invalid_request' | 'access_denied' | 'unauthorized_client' | 'unsupported_response_type' | 'invalid_scope' | 'server_error' | 'temporarily_unavailable';
|
276
|
+
error: 'invalid_request' | 'access_denied' | 'unauthorized_client' | 'unsupported_response_type' | 'invalid_scope' | 'server_error' | 'temporarily_unavailable' | OIDCErrorCodes;
|
202
277
|
error_description: string;
|
203
278
|
redirect_uri: string;
|
204
279
|
state?: string;
|
205
280
|
}
|
281
|
+
export declare type OIDCErrorCodes = 'interaction_required' | 'login_required' | 'account_selection_required' | 'consent_required' | 'invalid_request_uri' | 'invalid_request_object' | 'request_not_supported' | 'request_uri_not_supported' | 'registration_not_supported';
|
206
282
|
export interface ISPSAMLConfig {
|
207
283
|
get(): {
|
208
284
|
acsUrl: string;
|