@boxyhq/saml-jackson 1.2.2 → 1.3.1
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 +460 -204
- package/dist/controller/api.js +561 -377
- package/dist/controller/connection/oidc.d.ts +9 -0
- package/dist/controller/connection/oidc.js +145 -0
- package/dist/controller/connection/saml.d.ts +9 -0
- package/dist/controller/connection/saml.js +174 -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 +375 -143
- package/dist/controller/oidc-discovery.js +2 -1
- package/dist/controller/utils.d.ts +10 -2
- package/dist/controller/utils.js +88 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +23 -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/typings.d.ts +155 -36
- package/package.json +11 -10
- package/dist/read-config.d.ts +0 -3
@@ -31,8 +31,9 @@ class OidcDiscoveryController {
|
|
31
31
|
};
|
32
32
|
}
|
33
33
|
jwks() {
|
34
|
+
var _a;
|
34
35
|
return __awaiter(this, void 0, void 0, function* () {
|
35
|
-
const { jwtSigningKeys, jwsAlg } = this.opts.openid;
|
36
|
+
const { jwtSigningKeys, jwsAlg } = (_a = this.opts.openid) !== null && _a !== void 0 ? _a : {};
|
36
37
|
if (!jwtSigningKeys || !(0, utils_1.isJWSKeyPairLoaded)(jwtSigningKeys)) {
|
37
38
|
throw new error_1.JacksonError('JWT signing keys are not loaded', 501);
|
38
39
|
}
|
@@ -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;
|
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");
|
@@ -48,7 +48,9 @@ const defaultOpts = (opts) => {
|
|
48
48
|
}
|
49
49
|
newOpts.scimPath = newOpts.scimPath || '/api/scim/v2.0';
|
50
50
|
newOpts.samlAudience = newOpts.samlAudience || 'https://saml.boxyhq.com';
|
51
|
-
|
51
|
+
// 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).
|
52
|
+
newOpts.preLoadedConnection = newOpts.preLoadedConnection || '';
|
53
|
+
newOpts.preLoadedConfig = newOpts.preLoadedConfig || ''; // for backwards compatibility
|
52
54
|
newOpts.idpEnabled = newOpts.idpEnabled === true;
|
53
55
|
(0, defaultDb_1.default)(newOpts);
|
54
56
|
newOpts.clientSecretVerifier = newOpts.clientSecretVerifier || 'dummy';
|
@@ -60,43 +62,50 @@ const defaultOpts = (opts) => {
|
|
60
62
|
const controllers = (opts) => __awaiter(void 0, void 0, void 0, function* () {
|
61
63
|
opts = defaultOpts(opts);
|
62
64
|
const db = yield db_1.default.new(opts.db);
|
63
|
-
const
|
65
|
+
const connectionStore = db.store('saml:config');
|
64
66
|
const sessionStore = db.store('oauth:session', opts.db.ttl);
|
65
67
|
const codeStore = db.store('oauth:code', opts.db.ttl);
|
66
68
|
const tokenStore = db.store('oauth:token', opts.db.ttl);
|
67
69
|
const healthCheckStore = db.store('_health:check');
|
68
|
-
const
|
69
|
-
const adminController = new admin_1.AdminController({
|
70
|
+
const connectionAPIController = new api_1.ConnectionAPIController({ connectionStore, opts });
|
71
|
+
const adminController = new admin_1.AdminController({ connectionStore });
|
70
72
|
const healthCheckController = new health_check_1.HealthCheckController({ healthCheckStore });
|
71
73
|
yield healthCheckController.init();
|
72
74
|
const oauthController = new oauth_1.OAuthController({
|
73
|
-
|
75
|
+
connectionStore,
|
74
76
|
sessionStore,
|
75
77
|
codeStore,
|
76
78
|
tokenStore,
|
77
79
|
opts,
|
78
80
|
});
|
79
81
|
const logoutController = new logout_1.LogoutController({
|
80
|
-
|
82
|
+
connectionStore,
|
81
83
|
sessionStore,
|
82
84
|
opts,
|
83
85
|
});
|
84
86
|
const directorySync = yield (0, directory_sync_1.default)({ db, opts });
|
85
87
|
const oidcDiscoveryController = new oidc_discovery_1.OidcDiscoveryController({ opts });
|
86
88
|
const spConfig = new sp_config_1.SPSAMLConfig(opts);
|
87
|
-
// write pre-loaded
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
89
|
+
// write pre-loaded connections if present
|
90
|
+
const preLoadedConnection = opts.preLoadedConnection || opts.preLoadedConfig;
|
91
|
+
if (preLoadedConnection && preLoadedConnection.length > 0) {
|
92
|
+
const connections = yield (0, loadConnection_1.default)(preLoadedConnection);
|
93
|
+
for (const connection of connections) {
|
94
|
+
if ('oidcDiscoveryUrl' in connection) {
|
95
|
+
yield connectionAPIController.createOIDCConnection(connection);
|
96
|
+
}
|
97
|
+
else {
|
98
|
+
yield connectionAPIController.createSAMLConnection(connection);
|
99
|
+
}
|
100
|
+
console.log(`loaded connection for tenant "${connection.tenant}" and product "${connection.product}"`);
|
93
101
|
}
|
94
102
|
}
|
95
103
|
const type = opts.db.engine === 'sql' && opts.db.type ? ' Type: ' + opts.db.type : '';
|
96
104
|
console.log(`Using engine: ${opts.db.engine}.${type}`);
|
97
105
|
return {
|
98
106
|
spConfig,
|
99
|
-
apiController,
|
107
|
+
apiController: connectionAPIController,
|
108
|
+
connectionAPIController,
|
100
109
|
oauthController,
|
101
110
|
adminController,
|
102
111
|
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/typings.d.ts
CHANGED
@@ -1,32 +1,113 @@
|
|
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
|
-
|
11
|
-
forceAuthn
|
12
|
-
}
|
13
|
-
export interface
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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 interface SAMLSSORecord extends SAMLSSOConnection {
|
27
|
+
clientID: string;
|
28
|
+
clientSecret: string;
|
29
|
+
idpMetadata: {
|
30
|
+
entityID: string;
|
31
|
+
loginType?: string;
|
32
|
+
provider: string | 'Unknown';
|
33
|
+
slo: {
|
34
|
+
postUrl?: string;
|
35
|
+
redirectUrl?: string;
|
36
|
+
};
|
37
|
+
sso: {
|
38
|
+
postUrl?: string;
|
39
|
+
redirectUrl?: string;
|
40
|
+
};
|
41
|
+
thumbprint?: string;
|
42
|
+
validTo?: string;
|
43
|
+
};
|
44
|
+
certs: {
|
45
|
+
privateKey: string;
|
46
|
+
publicKey: string;
|
47
|
+
};
|
48
|
+
}
|
49
|
+
export interface OIDCSSORecord extends SSOConnection {
|
50
|
+
clientID: string;
|
51
|
+
clientSecret: string;
|
52
|
+
oidcProvider: {
|
53
|
+
provider?: string;
|
54
|
+
discoveryUrl?: string;
|
55
|
+
clientId?: string;
|
23
56
|
clientSecret?: string;
|
24
|
-
|
25
|
-
|
57
|
+
};
|
58
|
+
}
|
59
|
+
export declare type ConnectionType = 'saml' | 'oidc';
|
60
|
+
declare type ClientIDQuery = {
|
61
|
+
clientID: string;
|
62
|
+
};
|
63
|
+
declare type TenantQuery = {
|
64
|
+
tenant: string;
|
65
|
+
product: string;
|
66
|
+
strategy?: ConnectionType;
|
67
|
+
};
|
68
|
+
export declare type GetConnectionsQuery = ClientIDQuery | TenantQuery;
|
69
|
+
export declare type DelConnectionsQuery = (ClientIDQuery & {
|
70
|
+
clientSecret: string;
|
71
|
+
}) | TenantQuery;
|
72
|
+
export declare type GetConfigQuery = ClientIDQuery | Omit<TenantQuery, 'strategy'>;
|
73
|
+
export declare type DelConfigQuery = (ClientIDQuery & {
|
74
|
+
clientSecret: string;
|
75
|
+
}) | Omit<TenantQuery, 'strategy'>;
|
76
|
+
export interface IConnectionAPIController {
|
77
|
+
/**
|
78
|
+
* @deprecated Use `createSAMLConnection` instead.
|
79
|
+
*/
|
80
|
+
config(body: SAMLSSOConnection): Promise<SAMLSSORecord>;
|
81
|
+
createSAMLConnection(body: SAMLSSOConnectionWithRawMetadata | SAMLSSOConnectionWithEncodedMetadata): Promise<SAMLSSORecord>;
|
82
|
+
createOIDCConnection(body: OIDCSSOConnection): Promise<OIDCSSORecord>;
|
83
|
+
/**
|
84
|
+
* @deprecated Use `updateSAMLConnection` instead.
|
85
|
+
*/
|
86
|
+
updateConfig(body: SAMLSSOConnection & {
|
87
|
+
clientID: string;
|
88
|
+
clientSecret: string;
|
89
|
+
}): Promise<void>;
|
90
|
+
updateSAMLConnection(body: (SAMLSSOConnectionWithRawMetadata | SAMLSSOConnectionWithEncodedMetadata) & {
|
91
|
+
clientID: string;
|
92
|
+
clientSecret: string;
|
93
|
+
}): Promise<void>;
|
94
|
+
updateOIDCConnection(body: OIDCSSOConnection & {
|
95
|
+
clientID: string;
|
96
|
+
clientSecret: string;
|
26
97
|
}): Promise<void>;
|
98
|
+
getConnections(body: GetConnectionsQuery): Promise<Array<SAMLSSORecord | OIDCSSORecord>>;
|
99
|
+
/**
|
100
|
+
* @deprecated Use `getConnections` instead.
|
101
|
+
*/
|
102
|
+
getConfig(body: GetConfigQuery): Promise<SAMLSSORecord | Record<string, never>>;
|
103
|
+
deleteConnections(body: DelConnectionsQuery): Promise<void>;
|
104
|
+
/**
|
105
|
+
* @deprecated Use `deleteConnections` instead.
|
106
|
+
*/
|
107
|
+
deleteConfig(body: DelConfigQuery): Promise<void>;
|
27
108
|
}
|
28
109
|
export interface IOAuthController {
|
29
|
-
authorize(body:
|
110
|
+
authorize(body: OAuthReq): Promise<{
|
30
111
|
redirect_url?: string;
|
31
112
|
authorize_form?: string;
|
32
113
|
}>;
|
@@ -34,11 +115,14 @@ export interface IOAuthController {
|
|
34
115
|
redirect_url?: string;
|
35
116
|
app_select_form?: string;
|
36
117
|
}>;
|
118
|
+
oidcAuthzResponse(body: OIDCAuthzResponsePayload): Promise<{
|
119
|
+
redirect_url?: string;
|
120
|
+
}>;
|
37
121
|
token(body: OAuthTokenReq): Promise<OAuthTokenRes>;
|
38
122
|
userInfo(token: string): Promise<Profile>;
|
39
123
|
}
|
40
124
|
export interface IAdminController {
|
41
|
-
|
125
|
+
getAllConnection(pageOffset?: number, pageLimit?: number): any;
|
42
126
|
}
|
43
127
|
export interface IHealthCheckController {
|
44
128
|
status(): Promise<{
|
@@ -71,35 +155,67 @@ export interface IOidcDiscoveryController {
|
|
71
155
|
}>;
|
72
156
|
}
|
73
157
|
export interface OAuthReqBody {
|
158
|
+
state: string;
|
74
159
|
response_type: 'code';
|
75
|
-
client_id: string;
|
76
160
|
redirect_uri: string;
|
77
|
-
state: string;
|
78
|
-
tenant?: string;
|
79
|
-
product?: string;
|
80
|
-
access_type?: string;
|
81
|
-
resource?: string;
|
82
|
-
scope?: string;
|
83
|
-
nonce?: string;
|
84
161
|
code_challenge: string;
|
85
162
|
code_challenge_method: 'plain' | 'S256' | '';
|
86
|
-
|
163
|
+
scope?: string;
|
164
|
+
nonce?: string;
|
87
165
|
idp_hint?: string;
|
88
166
|
prompt?: string;
|
89
167
|
}
|
168
|
+
export interface OAuthReqBodyWithClientId extends OAuthReqBody {
|
169
|
+
client_id: string;
|
170
|
+
}
|
171
|
+
export interface OAuthReqBodyWithTenantProduct extends OAuthReqBody {
|
172
|
+
client_id: 'dummy';
|
173
|
+
tenant: string;
|
174
|
+
product: string;
|
175
|
+
}
|
176
|
+
export interface OAuthReqBodyWithAccessType extends OAuthReqBody {
|
177
|
+
client_id: 'dummy';
|
178
|
+
access_type: string;
|
179
|
+
}
|
180
|
+
export interface OAuthReqBodyWithResource extends OAuthReqBody {
|
181
|
+
client_id: 'dummy';
|
182
|
+
resource: string;
|
183
|
+
}
|
184
|
+
export declare type OAuthReq = OAuthReqBodyWithClientId | OAuthReqBodyWithTenantProduct | OAuthReqBodyWithAccessType | OAuthReqBodyWithResource;
|
90
185
|
export interface SAMLResponsePayload {
|
91
186
|
SAMLResponse: string;
|
92
187
|
RelayState: string;
|
93
188
|
idp_hint?: string;
|
94
189
|
}
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
190
|
+
interface OIDCAuthzResponseSuccess {
|
191
|
+
code: string;
|
192
|
+
state: string;
|
193
|
+
error?: never;
|
194
|
+
error_description?: never;
|
195
|
+
}
|
196
|
+
interface OIDCAuthzResponseError {
|
197
|
+
code?: never;
|
198
|
+
state: string;
|
199
|
+
error: OAuthErrorHandlerParams['error'] | OIDCErrorCodes;
|
200
|
+
error_description?: string;
|
201
|
+
}
|
202
|
+
export declare type OIDCAuthzResponsePayload = OIDCAuthzResponseSuccess | OIDCAuthzResponseError;
|
203
|
+
interface OAuthTokenReqBody {
|
99
204
|
code: string;
|
100
205
|
grant_type: 'authorization_code';
|
101
|
-
redirect_uri
|
206
|
+
redirect_uri: string;
|
207
|
+
}
|
208
|
+
export interface OAuthTokenReqWithCodeVerifier extends OAuthTokenReqBody {
|
209
|
+
code_verifier: string;
|
210
|
+
client_id?: never;
|
211
|
+
client_secret?: never;
|
212
|
+
}
|
213
|
+
export interface OAuthTokenReqWithCredentials extends OAuthTokenReqBody {
|
214
|
+
code_verifier?: never;
|
215
|
+
client_id: string;
|
216
|
+
client_secret: string;
|
102
217
|
}
|
218
|
+
export declare type OAuthTokenReq = OAuthTokenReqWithCodeVerifier | OAuthTokenReqWithCredentials;
|
103
219
|
export interface OAuthTokenRes {
|
104
220
|
access_token: string;
|
105
221
|
id_token?: string;
|
@@ -156,14 +272,16 @@ export interface DatabaseOption {
|
|
156
272
|
export interface JacksonOption {
|
157
273
|
externalUrl: string;
|
158
274
|
samlPath: string;
|
275
|
+
oidcPath?: string;
|
159
276
|
samlAudience?: string;
|
160
277
|
preLoadedConfig?: string;
|
278
|
+
preLoadedConnection?: string;
|
161
279
|
idpEnabled?: boolean;
|
162
280
|
db: DatabaseOption;
|
163
281
|
clientSecretVerifier?: string;
|
164
282
|
idpDiscoveryPath?: string;
|
165
283
|
scimPath?: string;
|
166
|
-
openid
|
284
|
+
openid?: {
|
167
285
|
jwsAlg?: string;
|
168
286
|
jwtSigningKeys?: {
|
169
287
|
private: string;
|
@@ -191,7 +309,7 @@ interface Metadata {
|
|
191
309
|
loginType: 'idp' | 'sp';
|
192
310
|
provider: string;
|
193
311
|
}
|
194
|
-
export interface
|
312
|
+
export interface SAMLConnection {
|
195
313
|
idpMetadata: Metadata;
|
196
314
|
certs: {
|
197
315
|
privateKey: string;
|
@@ -200,11 +318,12 @@ export interface SAMLConfig {
|
|
200
318
|
defaultRedirectUrl: string;
|
201
319
|
}
|
202
320
|
export interface OAuthErrorHandlerParams {
|
203
|
-
error: 'invalid_request' | 'access_denied' | 'unauthorized_client' | 'unsupported_response_type' | 'invalid_scope' | 'server_error' | 'temporarily_unavailable';
|
321
|
+
error: 'invalid_request' | 'access_denied' | 'unauthorized_client' | 'unsupported_response_type' | 'invalid_scope' | 'server_error' | 'temporarily_unavailable' | OIDCErrorCodes;
|
204
322
|
error_description: string;
|
205
323
|
redirect_uri: string;
|
206
324
|
state?: string;
|
207
325
|
}
|
326
|
+
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';
|
208
327
|
export interface ISPSAMLConfig {
|
209
328
|
get(): {
|
210
329
|
acsUrl: string;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@boxyhq/saml-jackson",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.3.1",
|
4
4
|
"description": "SAML Jackson library",
|
5
5
|
"keywords": [
|
6
6
|
"SAML 2.0"
|
@@ -27,7 +27,7 @@
|
|
27
27
|
"db:migration:run:planetscale": "cross-env DB_SSL=true DB_ENGINE=planetscale DB_URL=${PLANETSCALE_URL} ts-node --transpile-only ./node_modules/typeorm/cli.js migration:run -d typeorm.ts",
|
28
28
|
"db:migration:run:mariadb": "cross-env DB_TYPE=mariadb DB_URL=mariadb://root@localhost:3306/mysql ts-node --transpile-only ./node_modules/typeorm/cli.js migration:run -d typeorm.ts",
|
29
29
|
"prepublishOnly": "npm run build",
|
30
|
-
"test": "tap --ts --timeout=100 --coverage test/**/*.test.ts",
|
30
|
+
"test": "tap --ts --timeout=100 --coverage-map=map.js test/**/*.test.ts",
|
31
31
|
"sort": "npx sort-package-json"
|
32
32
|
},
|
33
33
|
"tap": {
|
@@ -41,11 +41,12 @@
|
|
41
41
|
"@boxyhq/saml20": "1.0.7",
|
42
42
|
"@opentelemetry/api": "1.0.4",
|
43
43
|
"@opentelemetry/api-metrics": "0.27.0",
|
44
|
-
"axios": "
|
45
|
-
"jose": "4.
|
46
|
-
"marked": "4.1.
|
44
|
+
"axios": "1.1.2",
|
45
|
+
"jose": "4.10.0",
|
46
|
+
"marked": "4.1.1",
|
47
47
|
"mongodb": "4.10.0",
|
48
48
|
"mysql2": "2.3.3",
|
49
|
+
"openid-client": "5.1.10",
|
49
50
|
"node-forge": "1.3.1",
|
50
51
|
"pg": "8.8.0",
|
51
52
|
"redis": "4.3.1",
|
@@ -57,20 +58,20 @@
|
|
57
58
|
},
|
58
59
|
"devDependencies": {
|
59
60
|
"@faker-js/faker": "7.5.0",
|
60
|
-
"@types/node": "18.
|
61
|
+
"@types/node": "18.8.3",
|
61
62
|
"@types/sinon": "10.0.13",
|
62
63
|
"@types/tap": "15.0.7",
|
63
|
-
"@typescript-eslint/eslint-plugin": "5.
|
64
|
+
"@typescript-eslint/eslint-plugin": "5.40.0",
|
64
65
|
"@typescript-eslint/parser": "5.38.1",
|
65
66
|
"cross-env": "7.0.3",
|
66
|
-
"eslint": "8.
|
67
|
+
"eslint": "8.25.0",
|
67
68
|
"eslint-config-prettier": "8.5.0",
|
68
69
|
"prettier": "2.7.1",
|
69
|
-
"sinon": "14.0.
|
70
|
+
"sinon": "14.0.1",
|
70
71
|
"tap": "16.3.0",
|
71
72
|
"ts-node": "10.9.1",
|
72
73
|
"tsconfig-paths": "4.1.0",
|
73
|
-
"typescript": "4.8.
|
74
|
+
"typescript": "4.8.4"
|
74
75
|
},
|
75
76
|
"engines": {
|
76
77
|
"node": ">=14.18.1 <=16.x"
|
package/dist/read-config.d.ts
DELETED