@boxyhq/saml-jackson 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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");
@@ -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) => {
package/dist/typings.d.ts CHANGED
@@ -1,32 +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;
11
- forceAuthn: boolean | 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;
12
29
  };
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;
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;
20
58
  }): Promise<any>;
21
- deleteConfig(body: {
22
- clientID?: string;
23
- clientSecret?: string;
24
- tenant?: string;
25
- product?: string;
26
- }): 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>;
27
63
  }
28
64
  export interface IOAuthController {
29
- authorize(body: OAuthReqBody): Promise<{
65
+ authorize(body: OAuthReq): Promise<{
30
66
  redirect_url?: string;
31
67
  authorize_form?: string;
32
68
  }>;
@@ -34,11 +70,14 @@ export interface IOAuthController {
34
70
  redirect_url?: string;
35
71
  app_select_form?: string;
36
72
  }>;
73
+ oidcAuthzResponse(body: OIDCAuthzResponsePayload): Promise<{
74
+ redirect_url?: string;
75
+ }>;
37
76
  token(body: OAuthTokenReq): Promise<OAuthTokenRes>;
38
77
  userInfo(token: string): Promise<Profile>;
39
78
  }
40
79
  export interface IAdminController {
41
- getAllConfig(pageOffset?: number, pageLimit?: number): any;
80
+ getAllConnection(pageOffset?: number, pageLimit?: number): any;
42
81
  }
43
82
  export interface IHealthCheckController {
44
83
  status(): Promise<{
@@ -71,35 +110,67 @@ export interface IOidcDiscoveryController {
71
110
  }>;
72
111
  }
73
112
  export interface OAuthReqBody {
113
+ state: string;
74
114
  response_type: 'code';
75
- client_id: string;
76
115
  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
116
  code_challenge: string;
85
117
  code_challenge_method: 'plain' | 'S256' | '';
86
- provider: 'saml';
118
+ scope?: string;
119
+ nonce?: string;
87
120
  idp_hint?: string;
88
121
  prompt?: string;
89
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;
134
+ }
135
+ export interface OAuthReqBodyWithResource extends OAuthReqBody {
136
+ client_id: 'dummy';
137
+ resource: string;
138
+ }
139
+ export declare type OAuthReq = OAuthReqBodyWithClientId | OAuthReqBodyWithTenantProduct | OAuthReqBodyWithAccessType | OAuthReqBodyWithResource;
90
140
  export interface SAMLResponsePayload {
91
141
  SAMLResponse: string;
92
142
  RelayState: string;
93
143
  idp_hint?: string;
94
144
  }
95
- export interface OAuthTokenReq {
96
- client_id: string;
97
- client_secret: string;
98
- 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 {
99
159
  code: string;
100
160
  grant_type: 'authorization_code';
101
- 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;
102
172
  }
173
+ export declare type OAuthTokenReq = OAuthTokenReqWithCodeVerifier | OAuthTokenReqWithCredentials;
103
174
  export interface OAuthTokenRes {
104
175
  access_token: string;
105
176
  id_token?: string;
@@ -156,8 +227,10 @@ export interface DatabaseOption {
156
227
  export interface JacksonOption {
157
228
  externalUrl: string;
158
229
  samlPath: string;
230
+ oidcPath: string;
159
231
  samlAudience?: string;
160
232
  preLoadedConfig?: string;
233
+ preLoadedConnection?: string;
161
234
  idpEnabled?: boolean;
162
235
  db: DatabaseOption;
163
236
  clientSecretVerifier?: string;
@@ -191,7 +264,7 @@ interface Metadata {
191
264
  loginType: 'idp' | 'sp';
192
265
  provider: string;
193
266
  }
194
- export interface SAMLConfig {
267
+ export interface SAMLConnection {
195
268
  idpMetadata: Metadata;
196
269
  certs: {
197
270
  privateKey: string;
@@ -200,11 +273,12 @@ export interface SAMLConfig {
200
273
  defaultRedirectUrl: string;
201
274
  }
202
275
  export interface OAuthErrorHandlerParams {
203
- 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;
204
277
  error_description: string;
205
278
  redirect_uri: string;
206
279
  state?: string;
207
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';
208
282
  export interface ISPSAMLConfig {
209
283
  get(): {
210
284
  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.0",
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": {
@@ -46,6 +46,7 @@
46
46
  "marked": "4.1.0",
47
47
  "mongodb": "4.10.0",
48
48
  "mysql2": "2.3.3",
49
+ "openid-client": "5.1.9",
49
50
  "node-forge": "1.3.1",
50
51
  "pg": "8.8.0",
51
52
  "redis": "4.3.1",
@@ -1,3 +0,0 @@
1
- import { IdPConfig } from './typings';
2
- declare const readConfig: (preLoadedConfig: string) => Promise<IdPConfig[]>;
3
- export default readConfig;