@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.
@@ -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;
@@ -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 { 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");
@@ -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
- 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).
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 configStore = db.store('saml:config');
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 apiController = new api_1.APIController({ configStore });
69
- const adminController = new admin_1.AdminController({ configStore });
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
- configStore,
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
- configStore,
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 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}"`);
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 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) => {
package/dist/typings.d.ts CHANGED
@@ -1,32 +1,113 @@
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;
11
- forceAuthn: boolean | string;
12
- };
13
- export interface IAPIController {
14
- config(body: IdPConfig): Promise<any>;
15
- updateConfig(body: any): Promise<any>;
16
- getConfig(body: {
17
- clientID?: string;
18
- tenant?: string;
19
- product?: string;
20
- }): Promise<any>;
21
- deleteConfig(body: {
22
- clientID?: 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 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
- tenant?: string;
25
- product?: string;
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: OAuthReqBody): Promise<{
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
- getAllConfig(pageOffset?: number, pageLimit?: number): any;
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
- provider: 'saml';
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
- export interface OAuthTokenReq {
96
- client_id: string;
97
- client_secret: string;
98
- code_verifier: string;
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?: string;
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 SAMLConfig {
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.2.2",
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": "^0.27.2",
45
- "jose": "4.9.3",
46
- "marked": "4.1.0",
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.7.23",
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.38.1",
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.24.0",
67
+ "eslint": "8.25.0",
67
68
  "eslint-config-prettier": "8.5.0",
68
69
  "prettier": "2.7.1",
69
- "sinon": "14.0.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.3"
74
+ "typescript": "4.8.4"
74
75
  },
75
76
  "engines": {
76
77
  "node": ">=14.18.1 <=16.x"
@@ -1,3 +0,0 @@
1
- import { IdPConfig } from './typings';
2
- declare const readConfig: (preLoadedConfig: string) => Promise<IdPConfig[]>;
3
- export default readConfig;