@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.
@@ -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;
@@ -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 { APIController } from './controller/api';
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: 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 read_config_1 = __importDefault(require("./read-config"));
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
- newOpts.preLoadedConfig = newOpts.preLoadedConfig || ''; // path to folder containing static SAML config 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).
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 configStore = db.store('saml:config');
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 apiController = new api_1.APIController({ configStore });
69
- const adminController = new admin_1.AdminController({ configStore });
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
- configStore,
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
- configStore,
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 config if present
88
- if (opts.preLoadedConfig && opts.preLoadedConfig.length > 0) {
89
- const configs = yield (0, read_config_1.default)(opts.preLoadedConfig);
90
- for (const config of configs) {
91
- yield apiController.config(config);
92
- console.log(`loaded config for tenant "${config.tenant}" and product "${config.product}"`);
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 readConfig = (preLoadedConfig) => __awaiter(void 0, void 0, void 0, function* () {
38
- if (preLoadedConfig.startsWith('./')) {
39
- preLoadedConfig = path.resolve(process.cwd(), preLoadedConfig);
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(preLoadedConfig);
42
- const configs = [];
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: config } = yield Promise.resolve().then(() => __importStar(require(
47
- /* webpackIgnore: true */ path.join(preLoadedConfig, file))));
48
- const rawMetadata = yield fs.promises.readFile(path.join(preLoadedConfig, path.parse(file).name + '.xml'), 'utf8');
49
- config.encodedRawMetadata = Buffer.from(rawMetadata, 'utf8').toString('base64');
50
- configs.push(config);
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 configs;
54
+ return connections;
54
55
  });
55
- exports.default = readConfig;
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
- createConfig: {
7
- name: 'jackson.config.create',
8
- description: 'Number of SAML config create requests',
6
+ createConnection: {
7
+ name: 'jackson.connection.create',
8
+ description: 'Number of IdP connection create requests',
9
9
  },
10
- getConfig: {
11
- name: 'jackson.config.get',
12
- description: 'Number of SAML config get requests',
10
+ getConnections: {
11
+ name: 'jackson.connections.get',
12
+ description: 'Number of IdP connections get requests',
13
13
  },
14
- deleteConfig: {
15
- name: 'jackson.config.delete',
16
- description: 'Number of SAML config delete requests',
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 SAML oauth authorize requests',
20
+ description: 'Number of oauth authorize requests',
21
21
  },
22
22
  oauthToken: {
23
23
  name: 'jackson.oauth.token',
24
- description: 'Number of SAML oauth token requests',
24
+ description: 'Number of oauth token requests',
25
25
  },
26
26
  oauthUserInfo: {
27
27
  name: 'jackson.oauth.userinfo',
28
- description: 'Number of SAML oauth user info requests',
28
+ description: 'Number of oauth user info requests',
29
29
  },
30
30
  };
31
31
  const createCounter = (action) => {
@@ -1,7 +1,7 @@
1
1
  declare const _default: {
2
- generate: () => Promise<{
3
- publicKey: string;
4
- privateKey: string;
5
- } | undefined>;
2
+ generate: () => {
3
+ publicKey: any;
4
+ privateKey: any;
5
+ };
6
6
  };
7
7
  export default _default;
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 x509 = __importStar(require("@peculiar/x509"));
36
- const webcrypto_1 = require("@peculiar/webcrypto");
37
- const crypto = new webcrypto_1.Crypto();
38
- x509.cryptoProvider.set(crypto);
39
- const alg = {
40
- name: 'RSASSA-PKCS1-v1_5',
41
- hash: 'SHA-256',
42
- publicExponent: new Uint8Array([1, 0, 1]),
43
- modulusLength: 2048,
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
- export declare type IdPConfig = {
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
- rawMetadata?: string;
10
- encodedRawMetadata?: string;
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
- export interface IAPIController {
13
- config(body: IdPConfig): Promise<any>;
14
- updateConfig(body: any): Promise<any>;
15
- getConfig(body: {
16
- clientID?: string;
17
- tenant?: string;
18
- product?: string;
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
- deleteConfig(body: {
21
- clientID?: string;
22
- clientSecret?: string;
23
- tenant?: string;
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: OAuthReqBody): Promise<{
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
- getAllConfig(pageOffset?: number, pageLimit?: number): any;
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
- provider: 'saml';
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
- export interface OAuthTokenReq {
94
- client_id: string;
95
- client_secret: string;
96
- code_verifier: string;
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?: string;
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 SAMLConfig {
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;