@omni-co/embed 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -134
- package/lib/cjs/embed.d.ts +1 -1
- package/lib/cjs/embed.js +61 -63
- package/lib/cjs/index.d.ts +4 -4
- package/lib/cjs/omni_internal_browser.d.ts +2 -2
- package/lib/cjs/random-str.server.js +2 -2
- package/lib/cjs/signature.d.ts +8 -2
- package/lib/cjs/signature.js +9 -8
- package/lib/cjs/types.d.ts +4 -15
- package/lib/cjs/types.js +0 -1
- package/lib/esm/embed.d.ts +1 -1
- package/lib/esm/embed.js +64 -66
- package/lib/esm/index.d.ts +4 -4
- package/lib/esm/index.js +4 -4
- package/lib/esm/omni_internal_browser.d.ts +2 -2
- package/lib/esm/omni_internal_browser.js +2 -2
- package/lib/esm/random-str.server.js +3 -3
- package/lib/esm/signature.d.ts +8 -2
- package/lib/esm/signature.js +10 -9
- package/lib/esm/types.d.ts +4 -15
- package/lib/esm/types.js +1 -1
- package/lib/omni_internal_browser/omni_internal_browser.d.ts +2 -2
- package/lib/omni_internal_browser/omni_internal_browser.js +2 -2
- package/lib/omni_internal_browser/types.d.ts +4 -15
- package/lib/omni_internal_browser/types.js +1 -1
- package/package.json +15 -3
package/lib/cjs/embed.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EmbedSsoContentDiscoveryProps, EmbedSsoDashboardProps, EmbedSsoWorkbookProps, CreateSessionTokenProps, RedeemSessionTokenProps } from
|
|
1
|
+
import { EmbedSsoContentDiscoveryProps, EmbedSsoDashboardProps, EmbedSsoWorkbookProps, CreateSessionTokenProps, RedeemSessionTokenProps } from './types.js';
|
|
2
2
|
export declare const embedSsoDashboard: (props: EmbedSsoDashboardProps) => Promise<string>;
|
|
3
3
|
export declare const embedSsoWorkbook: (props: EmbedSsoWorkbookProps) => Promise<string>;
|
|
4
4
|
/**
|
package/lib/cjs/embed.js
CHANGED
|
@@ -4,59 +4,59 @@ exports.redeemSessionToken = exports.createSessionToken = exports.embedSsoConten
|
|
|
4
4
|
const random_str_server_js_1 = require("./random-str.server.js");
|
|
5
5
|
const signature_js_1 = require("./signature.js");
|
|
6
6
|
const constants_js_1 = require("./constants.js");
|
|
7
|
-
const defaultEmbedDomain =
|
|
8
|
-
const embedLoginPath =
|
|
9
|
-
const generateSsoEmbedSessionPath =
|
|
10
|
-
const redeemSsoEmbedSessionPath =
|
|
7
|
+
const defaultEmbedDomain = 'embed-omniapp.co';
|
|
8
|
+
const embedLoginPath = '/embed/login';
|
|
9
|
+
const generateSsoEmbedSessionPath = '/api/unstable/embed/sso/generate-session';
|
|
10
|
+
const redeemSsoEmbedSessionPath = '/embed/sso/redeem-session';
|
|
11
11
|
const createEmbedRequestUrl = (props) => {
|
|
12
12
|
let host, domain, organizationName;
|
|
13
|
-
if (
|
|
13
|
+
if ('host' in props)
|
|
14
14
|
host = props.host;
|
|
15
|
-
if (
|
|
15
|
+
if ('organizationName' in props) {
|
|
16
16
|
domain = props.domain;
|
|
17
17
|
organizationName = props.organizationName;
|
|
18
18
|
}
|
|
19
19
|
// Generate the url for the embed login route
|
|
20
20
|
if (!host)
|
|
21
21
|
host = `${organizationName}.${domain ?? defaultEmbedDomain}`;
|
|
22
|
-
const url = new URL(props.requestPath, `https://${host}${props.port ? `:${props.port}` :
|
|
22
|
+
const url = new URL(props.requestPath, `https://${host}${props.port ? `:${props.port}` : ''}`);
|
|
23
23
|
return url;
|
|
24
24
|
};
|
|
25
25
|
const validateProps = (props) => {
|
|
26
26
|
const { externalId, name, nonce, secret, uiSettings, userAttributes, customTheme, customThemeId, } = props;
|
|
27
27
|
let host, domain, organizationName;
|
|
28
|
-
if (
|
|
28
|
+
if ('host' in props)
|
|
29
29
|
host = props.host;
|
|
30
|
-
if (
|
|
30
|
+
if ('organizationName' in props)
|
|
31
31
|
organizationName = props.organizationName;
|
|
32
|
-
if (
|
|
32
|
+
if ('domain' in props)
|
|
33
33
|
domain = props.domain;
|
|
34
34
|
if (!externalId)
|
|
35
|
-
throw new Error(
|
|
35
|
+
throw new Error('externalId is required');
|
|
36
36
|
if (!name)
|
|
37
|
-
throw new Error(
|
|
37
|
+
throw new Error('name is required');
|
|
38
38
|
if (!secret)
|
|
39
|
-
throw new Error(
|
|
39
|
+
throw new Error('secret is required');
|
|
40
40
|
if (!host && !organizationName)
|
|
41
|
-
throw new Error(
|
|
41
|
+
throw new Error('Must specify either `host` or `organizationName`');
|
|
42
42
|
if (host && (organizationName || domain))
|
|
43
|
-
throw new Error(
|
|
43
|
+
throw new Error('`host` must be used exclusively and cannot be combined with `organizationName` or `domain` (domain is deprecated)');
|
|
44
44
|
if (customThemeId && customTheme)
|
|
45
|
-
console.warn(
|
|
45
|
+
console.warn('If you use a `customThemeId` any theme styles passed in via `customTheme` will be ignored.');
|
|
46
46
|
if (domain)
|
|
47
47
|
console.warn("`domain` is deprecated and will be removed in a future v1.0.0 release. Use `host` to specify your vanity domain instead (eg `host: 'omni.my-company.com'`).");
|
|
48
48
|
if (nonce && nonce.length !== 32)
|
|
49
|
-
throw new Error(
|
|
50
|
-
if (typeof uiSettings !==
|
|
51
|
-
throw new Error(
|
|
52
|
-
if (typeof userAttributes !==
|
|
53
|
-
throw new Error(
|
|
49
|
+
throw new Error('nonce must be 32 characters');
|
|
50
|
+
if (typeof uiSettings !== 'object' && uiSettings)
|
|
51
|
+
throw new Error('uiSettings must be an object');
|
|
52
|
+
if (typeof userAttributes !== 'object' && userAttributes)
|
|
53
|
+
throw new Error('userAttributes must be an object');
|
|
54
54
|
};
|
|
55
55
|
const embedSsoContent = async (props) => {
|
|
56
56
|
validateProps(props);
|
|
57
|
-
|
|
57
|
+
const { accessBoost, branch, connectionRoles: rawConnectionRoles, contentPath, customTheme: rawCustomTheme, customThemeId, domain, email, entity, entityFolderContentRole, entityFolderGroupContentRole, entityFolderLabel, entityGroupLabel, externalId, filterSearchParam, groups: rawGroups, host, linkAccess, mode, modelRoles: rawModelRoles, name, organizationName, preserveEntityFolderContentRole, prefersDark, port, secret, theme, timezone, uiSettings: rawUiSettings, userAttributes: rawUserAttributes, } = props;
|
|
58
58
|
// Handle defaults
|
|
59
|
-
nonce = nonce ?? (await (0, random_str_server_js_1.random32ByteString)());
|
|
59
|
+
const nonce = props.nonce ?? (await (0, random_str_server_js_1.random32ByteString)());
|
|
60
60
|
const uiSettings = rawUiSettings && JSON.stringify(rawUiSettings);
|
|
61
61
|
const userAttributes = rawUserAttributes && JSON.stringify(rawUserAttributes);
|
|
62
62
|
const customTheme = rawCustomTheme && JSON.stringify(rawCustomTheme);
|
|
@@ -99,44 +99,45 @@ const embedSsoContent = async (props) => {
|
|
|
99
99
|
prefersDark,
|
|
100
100
|
preserveEntityFolderContentRole,
|
|
101
101
|
theme,
|
|
102
|
+
timezone,
|
|
102
103
|
uiSettings,
|
|
103
104
|
userAttributes,
|
|
104
105
|
});
|
|
105
106
|
// Build and return the signed url
|
|
106
|
-
url.searchParams.append(
|
|
107
|
-
url.searchParams.append(
|
|
108
|
-
url.searchParams.append(
|
|
109
|
-
url.searchParams.append(
|
|
110
|
-
url.searchParams.append(
|
|
111
|
-
userAttributes && url.searchParams.append(
|
|
107
|
+
url.searchParams.append('contentPath', contentPath);
|
|
108
|
+
url.searchParams.append('externalId', externalId);
|
|
109
|
+
url.searchParams.append('name', name);
|
|
110
|
+
url.searchParams.append('nonce', nonce);
|
|
111
|
+
url.searchParams.append('signature', signature);
|
|
112
|
+
userAttributes && url.searchParams.append('userAttributes', userAttributes);
|
|
112
113
|
accessBoost !== undefined &&
|
|
113
|
-
url.searchParams.append(
|
|
114
|
-
entity && url.searchParams.append(
|
|
115
|
-
prefersDark && url.searchParams.append(
|
|
116
|
-
theme && url.searchParams.append(
|
|
114
|
+
url.searchParams.append('accessBoost', accessBoost.toString());
|
|
115
|
+
entity && url.searchParams.append('entity', entity);
|
|
116
|
+
prefersDark && url.searchParams.append('prefersDark', prefersDark);
|
|
117
|
+
theme && url.searchParams.append('theme', theme);
|
|
118
|
+
timezone && url.searchParams.append('timezone', timezone);
|
|
117
119
|
filterSearchParam &&
|
|
118
|
-
url.searchParams.append(
|
|
119
|
-
linkAccess && url.searchParams.append(
|
|
120
|
-
customTheme && url.searchParams.append(
|
|
121
|
-
customThemeId && url.searchParams.append(
|
|
122
|
-
connectionRoles &&
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
modelRoles && url.searchParams.append("modelRoles", modelRoles);
|
|
120
|
+
url.searchParams.append('filterSearchParam', filterSearchParam);
|
|
121
|
+
linkAccess && url.searchParams.append('linkAccess', linkAccess);
|
|
122
|
+
customTheme && url.searchParams.append('customTheme', customTheme);
|
|
123
|
+
customThemeId && url.searchParams.append('customThemeId', customThemeId);
|
|
124
|
+
connectionRoles && url.searchParams.append('connectionRoles', connectionRoles);
|
|
125
|
+
mode && url.searchParams.append('mode', mode);
|
|
126
|
+
modelRoles && url.searchParams.append('modelRoles', modelRoles);
|
|
126
127
|
entityFolderContentRole &&
|
|
127
|
-
url.searchParams.append(
|
|
128
|
+
url.searchParams.append('entityFolderContentRole', entityFolderContentRole);
|
|
128
129
|
entityFolderGroupContentRole &&
|
|
129
|
-
url.searchParams.append(
|
|
130
|
-
groups && url.searchParams.append(
|
|
131
|
-
email && url.searchParams.append(
|
|
132
|
-
uiSettings && url.searchParams.append(
|
|
130
|
+
url.searchParams.append('entityFolderGroupContentRole', entityFolderGroupContentRole);
|
|
131
|
+
groups && url.searchParams.append('groups', groups);
|
|
132
|
+
email && url.searchParams.append('email', email);
|
|
133
|
+
uiSettings && url.searchParams.append('uiSettings', uiSettings);
|
|
133
134
|
entityGroupLabel &&
|
|
134
|
-
url.searchParams.append(
|
|
135
|
+
url.searchParams.append('entityGroupLabel', entityGroupLabel);
|
|
135
136
|
entityFolderLabel &&
|
|
136
|
-
url.searchParams.append(
|
|
137
|
+
url.searchParams.append('entityFolderLabel', entityFolderLabel);
|
|
137
138
|
preserveEntityFolderContentRole !== undefined &&
|
|
138
|
-
url.searchParams.append(
|
|
139
|
-
branch && url.searchParams.append(
|
|
139
|
+
url.searchParams.append('preserveEntityFolderContentRole', preserveEntityFolderContentRole.toString());
|
|
140
|
+
branch && url.searchParams.append('branch', branch);
|
|
140
141
|
return url.toString();
|
|
141
142
|
};
|
|
142
143
|
const buildContentPath = (contentType, contentId) => `/${contentType}/${contentId}`;
|
|
@@ -158,9 +159,7 @@ exports.embedSsoWorkbook = embedSsoWorkbook;
|
|
|
158
159
|
const embedSsoContentDiscovery = async (props) => {
|
|
159
160
|
// Entity paths for the Content Discovery embed no longer require stripping the leading `/`.
|
|
160
161
|
// This check maintains backwards compatibility with the old behavior.
|
|
161
|
-
const contentPath = props.path.startsWith(
|
|
162
|
-
? props.path
|
|
163
|
-
: `/${props.path}`;
|
|
162
|
+
const contentPath = props.path.startsWith('/') ? props.path : `/${props.path}`;
|
|
164
163
|
return embedSsoContent({
|
|
165
164
|
...props,
|
|
166
165
|
contentPath,
|
|
@@ -185,13 +184,13 @@ const createSessionToken = async ({ apiKey, domain, host, organizationName, port
|
|
|
185
184
|
});
|
|
186
185
|
// Hit the endpoint with the endpoint params + signature
|
|
187
186
|
const fetchResponse = await fetch(generateRequestUrl, {
|
|
188
|
-
method:
|
|
187
|
+
method: 'POST',
|
|
189
188
|
body: JSON.stringify({
|
|
190
189
|
...restProps,
|
|
191
190
|
}),
|
|
192
191
|
headers: {
|
|
193
192
|
Authorization: `Bearer ${apiKey}`,
|
|
194
|
-
|
|
193
|
+
'Content-type': 'application/json',
|
|
195
194
|
},
|
|
196
195
|
});
|
|
197
196
|
if (!fetchResponse.ok)
|
|
@@ -201,7 +200,7 @@ const createSessionToken = async ({ apiKey, domain, host, organizationName, port
|
|
|
201
200
|
};
|
|
202
201
|
const fetchData = (await fetchResponse.json());
|
|
203
202
|
if (!fetchData.sessionId)
|
|
204
|
-
return { error:
|
|
203
|
+
return { error: 'No session ID returned from endpoint.', success: false };
|
|
205
204
|
return { sessionToken: fetchData.sessionId, success: true };
|
|
206
205
|
};
|
|
207
206
|
exports.createSessionToken = createSessionToken;
|
|
@@ -229,14 +228,13 @@ theme, }) => {
|
|
|
229
228
|
theme,
|
|
230
229
|
branch,
|
|
231
230
|
});
|
|
232
|
-
branch !== undefined &&
|
|
233
|
-
redeemRequestUrl.searchParams.append("branch", branch);
|
|
231
|
+
branch !== undefined && redeemRequestUrl.searchParams.append('branch', branch);
|
|
234
232
|
prefersDark !== undefined &&
|
|
235
|
-
redeemRequestUrl.searchParams.append(
|
|
236
|
-
theme !== undefined && redeemRequestUrl.searchParams.append(
|
|
237
|
-
redeemRequestUrl.searchParams.append(
|
|
238
|
-
redeemRequestUrl.searchParams.append(
|
|
239
|
-
redeemRequestUrl.searchParams.append(
|
|
233
|
+
redeemRequestUrl.searchParams.append('prefersDark', prefersDark);
|
|
234
|
+
theme !== undefined && redeemRequestUrl.searchParams.append('theme', theme);
|
|
235
|
+
redeemRequestUrl.searchParams.append('nonce', nonce);
|
|
236
|
+
redeemRequestUrl.searchParams.append('sessionId', sessionId);
|
|
237
|
+
redeemRequestUrl.searchParams.append('signature', signature);
|
|
240
238
|
return redeemRequestUrl.toString();
|
|
241
239
|
};
|
|
242
240
|
exports.redeemSessionToken = redeemSessionToken;
|
package/lib/cjs/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
1
|
+
export * from './embed.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export * from './signature.js';
|
|
4
|
+
export * from './constants.js';
|
|
@@ -13,8 +13,8 @@ const crypto_1 = __importDefault(require("crypto"));
|
|
|
13
13
|
* of at least one collision.
|
|
14
14
|
*/
|
|
15
15
|
const random32ByteString = async () => {
|
|
16
|
-
const humanReadableByteSpace =
|
|
17
|
-
let randomString =
|
|
16
|
+
const humanReadableByteSpace = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
17
|
+
let randomString = '';
|
|
18
18
|
for (let i = 0; i < 32; i++) {
|
|
19
19
|
// Generate a random number between 0 and humanReadableByteSpace.length - 1
|
|
20
20
|
// and return the corresponding character from humanReadableByteSpace.
|
package/lib/cjs/signature.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RedeemSessionExclusiveProps } from
|
|
1
|
+
import { RedeemSessionExclusiveProps } from './types.js';
|
|
2
2
|
export declare const hmacSign: (data: string, secret: string) => string;
|
|
3
3
|
type SignatureProps = {
|
|
4
4
|
/**
|
|
@@ -109,6 +109,12 @@ type SignatureProps = {
|
|
|
109
109
|
* The theme of the embed.
|
|
110
110
|
*/
|
|
111
111
|
theme?: string;
|
|
112
|
+
/**
|
|
113
|
+
* IANA timezone override applied to the embed session's query execution.
|
|
114
|
+
*
|
|
115
|
+
* Example: "America/New_York"
|
|
116
|
+
*/
|
|
117
|
+
timezone?: string;
|
|
112
118
|
/**
|
|
113
119
|
* The UI settings for the embedded content.
|
|
114
120
|
*/
|
|
@@ -140,6 +146,6 @@ type SignSessionRedemptionProps = Pick<SignatureProps, RedeemSessionExclusivePro
|
|
|
140
146
|
*/
|
|
141
147
|
export declare const signSessionRedemption: ({ requestUrl, nonce, sessionId, secret, branch, prefersDark, theme, }: SignSessionRedemptionProps) => string;
|
|
142
148
|
export declare const TEST_ONLY: {
|
|
143
|
-
generateStringForSignature: ({ loginUrl, contentPath, externalId, name, nonce, accessBoost, branch, connectionRoles, customTheme, customThemeId, email, entity, entityFolderContentRole, entityFolderGroupContentRole, entityFolderLabel, entityGroupLabel, filterSearchParam, groups, linkAccess, mode, modelRoles, prefersDark, preserveEntityFolderContentRole, theme, uiSettings, userAttributes, }: Omit<SignatureProps, "secret">) => string;
|
|
149
|
+
generateStringForSignature: ({ loginUrl, contentPath, externalId, name, nonce, accessBoost, branch, connectionRoles, customTheme, customThemeId, email, entity, entityFolderContentRole, entityFolderGroupContentRole, entityFolderLabel, entityGroupLabel, filterSearchParam, groups, linkAccess, mode, modelRoles, prefersDark, preserveEntityFolderContentRole, theme, timezone, uiSettings, userAttributes, }: Omit<SignatureProps, "secret">) => string;
|
|
144
150
|
};
|
|
145
151
|
export {};
|
package/lib/cjs/signature.js
CHANGED
|
@@ -6,20 +6,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.TEST_ONLY = exports.signSessionRedemption = exports.getSignature = exports.hmacSign = void 0;
|
|
7
7
|
const crypto_1 = __importDefault(require("crypto"));
|
|
8
8
|
const hmacSign = (data, secret) => {
|
|
9
|
-
const hmac = crypto_1.default.createHmac(
|
|
9
|
+
const hmac = crypto_1.default.createHmac('sha256', secret);
|
|
10
10
|
hmac.update(data);
|
|
11
|
-
return hmac.digest(
|
|
11
|
+
return hmac.digest('base64url');
|
|
12
12
|
};
|
|
13
13
|
exports.hmacSign = hmacSign;
|
|
14
14
|
const generateStringForSignature = ({
|
|
15
15
|
// Required parameters
|
|
16
16
|
loginUrl, contentPath, externalId, name, nonce,
|
|
17
17
|
// Optional parameters
|
|
18
|
-
accessBoost, branch, connectionRoles, customTheme, customThemeId, email, entity, entityFolderContentRole, entityFolderGroupContentRole, entityFolderLabel, entityGroupLabel, filterSearchParam, groups, linkAccess, mode, modelRoles, prefersDark, preserveEntityFolderContentRole, theme, uiSettings, userAttributes, }) => {
|
|
18
|
+
accessBoost, branch, connectionRoles, customTheme, customThemeId, email, entity, entityFolderContentRole, entityFolderGroupContentRole, entityFolderLabel, entityGroupLabel, filterSearchParam, groups, linkAccess, mode, modelRoles, prefersDark, preserveEntityFolderContentRole, theme, timezone, uiSettings, userAttributes, }) => {
|
|
19
19
|
// We create a map of optional params and convert it to an array
|
|
20
20
|
// to ensure that the order of the optional params is alphabetical and consistent.
|
|
21
21
|
const optionalParamsMap = {
|
|
22
|
-
...(accessBoost !== undefined && { accessBoost: accessBoost.toString() }),
|
|
22
|
+
...(accessBoost !== undefined && { accessBoost: accessBoost.toString() }), // need to support accessBoost=false
|
|
23
23
|
...(branch && { branch }),
|
|
24
24
|
...(connectionRoles && { connectionRoles }),
|
|
25
25
|
...(customTheme && { customTheme }),
|
|
@@ -40,6 +40,7 @@ accessBoost, branch, connectionRoles, customTheme, customThemeId, email, entity,
|
|
|
40
40
|
preserveEntityFolderContentRole: preserveEntityFolderContentRole.toString(),
|
|
41
41
|
}),
|
|
42
42
|
...(theme && { theme }),
|
|
43
|
+
...(timezone && { timezone }),
|
|
43
44
|
...(uiSettings && { uiSettings }),
|
|
44
45
|
...(userAttributes && { userAttributes }),
|
|
45
46
|
};
|
|
@@ -54,10 +55,10 @@ accessBoost, branch, connectionRoles, customTheme, customThemeId, email, entity,
|
|
|
54
55
|
${externalId}
|
|
55
56
|
${name}
|
|
56
57
|
${nonce}
|
|
57
|
-
${optionalParams.join(
|
|
58
|
+
${optionalParams.join('\n')}
|
|
58
59
|
`
|
|
59
60
|
.trim()
|
|
60
|
-
.replace(/\n\s+/g,
|
|
61
|
+
.replace(/\n\s+/g, '\n');
|
|
61
62
|
};
|
|
62
63
|
/**
|
|
63
64
|
* Creates a signature value for the /embed/login endpoint.
|
|
@@ -89,10 +90,10 @@ const signSessionRedemption = ({ requestUrl, nonce, sessionId, secret, branch, p
|
|
|
89
90
|
${requestUrl}
|
|
90
91
|
${nonce}
|
|
91
92
|
${sessionId}
|
|
92
|
-
${optionalParams.join(
|
|
93
|
+
${optionalParams.join('\n')}
|
|
93
94
|
`
|
|
94
95
|
.trim()
|
|
95
|
-
.replace(/\n\s+/g,
|
|
96
|
+
.replace(/\n\s+/g, '\n');
|
|
96
97
|
return (0, exports.hmacSign)(signatureString, secret);
|
|
97
98
|
};
|
|
98
99
|
exports.signSessionRedemption = signSessionRedemption;
|
package/lib/cjs/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CustomThemeProperty, EmbedEntityFolderContentRoles, EmbedSessionMode, EmbedUiSettings } from
|
|
1
|
+
import type { CustomThemeProperty, EmbedEntityFolderContentRoles, EmbedSessionMode, EmbedUiSettings } from './constants.js';
|
|
2
2
|
type UserAttributeValue = string | string[] | number | number[];
|
|
3
3
|
type HostProps = ({
|
|
4
4
|
host?: never;
|
|
@@ -39,6 +39,7 @@ type EmbedSsoBaseProps = {
|
|
|
39
39
|
userAttributes?: Record<string, UserAttributeValue>;
|
|
40
40
|
prefersDark?: string;
|
|
41
41
|
theme?: string;
|
|
42
|
+
timezone?: string;
|
|
42
43
|
customTheme?: CustomThemeProperties;
|
|
43
44
|
customThemeId?: string;
|
|
44
45
|
/**
|
|
@@ -187,20 +188,8 @@ export type EmbedSsoContentDiscoveryProps = EmbedSsoBaseProps & {
|
|
|
187
188
|
* Path name of the content discovery page to embed.
|
|
188
189
|
*/
|
|
189
190
|
path: string;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
* Required connection roles object. Object keys should be connection IDs from the embedded Omni instance.
|
|
193
|
-
* Object values should be connection roles (base roles or custom role names).
|
|
194
|
-
*/
|
|
195
|
-
connectionRoles: Record<string, string>;
|
|
196
|
-
} | {
|
|
197
|
-
/**
|
|
198
|
-
* Required model roles object. Object keys should be model IDs from the embedded Omni instance.
|
|
199
|
-
* Object values should be model roles (base roles or custom role names).
|
|
200
|
-
*/
|
|
201
|
-
modelRoles: Record<string, string>;
|
|
202
|
-
});
|
|
203
|
-
export type RedeemSessionExclusiveProps = "branch" | "nonce" | "prefersDark" | "secret" | "theme";
|
|
191
|
+
};
|
|
192
|
+
export type RedeemSessionExclusiveProps = 'branch' | 'nonce' | 'prefersDark' | 'secret' | 'theme';
|
|
204
193
|
export type CreateSessionTokenProps = Omit<EmbedSsoContentProps, RedeemSessionExclusiveProps> & {
|
|
205
194
|
apiKey: string;
|
|
206
195
|
} & HostProps;
|
package/lib/cjs/types.js
CHANGED
package/lib/esm/embed.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EmbedSsoContentDiscoveryProps, EmbedSsoDashboardProps, EmbedSsoWorkbookProps, CreateSessionTokenProps, RedeemSessionTokenProps } from
|
|
1
|
+
import { EmbedSsoContentDiscoveryProps, EmbedSsoDashboardProps, EmbedSsoWorkbookProps, CreateSessionTokenProps, RedeemSessionTokenProps } from './types.js';
|
|
2
2
|
export declare const embedSsoDashboard: (props: EmbedSsoDashboardProps) => Promise<string>;
|
|
3
3
|
export declare const embedSsoWorkbook: (props: EmbedSsoWorkbookProps) => Promise<string>;
|
|
4
4
|
/**
|
package/lib/esm/embed.js
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
import { random32ByteString } from
|
|
2
|
-
import { getSignature, signSessionRedemption } from
|
|
3
|
-
import { EmbeddedContent } from
|
|
4
|
-
const defaultEmbedDomain =
|
|
5
|
-
const embedLoginPath =
|
|
6
|
-
const generateSsoEmbedSessionPath =
|
|
7
|
-
const redeemSsoEmbedSessionPath =
|
|
1
|
+
import { random32ByteString } from './random-str.server.js';
|
|
2
|
+
import { getSignature, signSessionRedemption } from './signature.js';
|
|
3
|
+
import { EmbeddedContent } from './constants.js';
|
|
4
|
+
const defaultEmbedDomain = 'embed-omniapp.co';
|
|
5
|
+
const embedLoginPath = '/embed/login';
|
|
6
|
+
const generateSsoEmbedSessionPath = '/api/unstable/embed/sso/generate-session';
|
|
7
|
+
const redeemSsoEmbedSessionPath = '/embed/sso/redeem-session';
|
|
8
8
|
const createEmbedRequestUrl = (props) => {
|
|
9
9
|
let host, domain, organizationName;
|
|
10
|
-
if (
|
|
10
|
+
if ('host' in props)
|
|
11
11
|
host = props.host;
|
|
12
|
-
if (
|
|
12
|
+
if ('organizationName' in props) {
|
|
13
13
|
domain = props.domain;
|
|
14
14
|
organizationName = props.organizationName;
|
|
15
15
|
}
|
|
16
16
|
// Generate the url for the embed login route
|
|
17
17
|
if (!host)
|
|
18
18
|
host = `${organizationName}.${domain ?? defaultEmbedDomain}`;
|
|
19
|
-
const url = new URL(props.requestPath, `https://${host}${props.port ? `:${props.port}` :
|
|
19
|
+
const url = new URL(props.requestPath, `https://${host}${props.port ? `:${props.port}` : ''}`);
|
|
20
20
|
return url;
|
|
21
21
|
};
|
|
22
22
|
const validateProps = (props) => {
|
|
23
23
|
const { externalId, name, nonce, secret, uiSettings, userAttributes, customTheme, customThemeId, } = props;
|
|
24
24
|
let host, domain, organizationName;
|
|
25
|
-
if (
|
|
25
|
+
if ('host' in props)
|
|
26
26
|
host = props.host;
|
|
27
|
-
if (
|
|
27
|
+
if ('organizationName' in props)
|
|
28
28
|
organizationName = props.organizationName;
|
|
29
|
-
if (
|
|
29
|
+
if ('domain' in props)
|
|
30
30
|
domain = props.domain;
|
|
31
31
|
if (!externalId)
|
|
32
|
-
throw new Error(
|
|
32
|
+
throw new Error('externalId is required');
|
|
33
33
|
if (!name)
|
|
34
|
-
throw new Error(
|
|
34
|
+
throw new Error('name is required');
|
|
35
35
|
if (!secret)
|
|
36
|
-
throw new Error(
|
|
36
|
+
throw new Error('secret is required');
|
|
37
37
|
if (!host && !organizationName)
|
|
38
|
-
throw new Error(
|
|
38
|
+
throw new Error('Must specify either `host` or `organizationName`');
|
|
39
39
|
if (host && (organizationName || domain))
|
|
40
|
-
throw new Error(
|
|
40
|
+
throw new Error('`host` must be used exclusively and cannot be combined with `organizationName` or `domain` (domain is deprecated)');
|
|
41
41
|
if (customThemeId && customTheme)
|
|
42
|
-
console.warn(
|
|
42
|
+
console.warn('If you use a `customThemeId` any theme styles passed in via `customTheme` will be ignored.');
|
|
43
43
|
if (domain)
|
|
44
44
|
console.warn("`domain` is deprecated and will be removed in a future v1.0.0 release. Use `host` to specify your vanity domain instead (eg `host: 'omni.my-company.com'`).");
|
|
45
45
|
if (nonce && nonce.length !== 32)
|
|
46
|
-
throw new Error(
|
|
47
|
-
if (typeof uiSettings !==
|
|
48
|
-
throw new Error(
|
|
49
|
-
if (typeof userAttributes !==
|
|
50
|
-
throw new Error(
|
|
46
|
+
throw new Error('nonce must be 32 characters');
|
|
47
|
+
if (typeof uiSettings !== 'object' && uiSettings)
|
|
48
|
+
throw new Error('uiSettings must be an object');
|
|
49
|
+
if (typeof userAttributes !== 'object' && userAttributes)
|
|
50
|
+
throw new Error('userAttributes must be an object');
|
|
51
51
|
};
|
|
52
52
|
const embedSsoContent = async (props) => {
|
|
53
53
|
validateProps(props);
|
|
54
|
-
|
|
54
|
+
const { accessBoost, branch, connectionRoles: rawConnectionRoles, contentPath, customTheme: rawCustomTheme, customThemeId, domain, email, entity, entityFolderContentRole, entityFolderGroupContentRole, entityFolderLabel, entityGroupLabel, externalId, filterSearchParam, groups: rawGroups, host, linkAccess, mode, modelRoles: rawModelRoles, name, organizationName, preserveEntityFolderContentRole, prefersDark, port, secret, theme, timezone, uiSettings: rawUiSettings, userAttributes: rawUserAttributes, } = props;
|
|
55
55
|
// Handle defaults
|
|
56
|
-
nonce = nonce ?? (await random32ByteString());
|
|
56
|
+
const nonce = props.nonce ?? (await random32ByteString());
|
|
57
57
|
const uiSettings = rawUiSettings && JSON.stringify(rawUiSettings);
|
|
58
58
|
const userAttributes = rawUserAttributes && JSON.stringify(rawUserAttributes);
|
|
59
59
|
const customTheme = rawCustomTheme && JSON.stringify(rawCustomTheme);
|
|
@@ -96,44 +96,45 @@ const embedSsoContent = async (props) => {
|
|
|
96
96
|
prefersDark,
|
|
97
97
|
preserveEntityFolderContentRole,
|
|
98
98
|
theme,
|
|
99
|
+
timezone,
|
|
99
100
|
uiSettings,
|
|
100
101
|
userAttributes,
|
|
101
102
|
});
|
|
102
103
|
// Build and return the signed url
|
|
103
|
-
url.searchParams.append(
|
|
104
|
-
url.searchParams.append(
|
|
105
|
-
url.searchParams.append(
|
|
106
|
-
url.searchParams.append(
|
|
107
|
-
url.searchParams.append(
|
|
108
|
-
userAttributes && url.searchParams.append(
|
|
104
|
+
url.searchParams.append('contentPath', contentPath);
|
|
105
|
+
url.searchParams.append('externalId', externalId);
|
|
106
|
+
url.searchParams.append('name', name);
|
|
107
|
+
url.searchParams.append('nonce', nonce);
|
|
108
|
+
url.searchParams.append('signature', signature);
|
|
109
|
+
userAttributes && url.searchParams.append('userAttributes', userAttributes);
|
|
109
110
|
accessBoost !== undefined &&
|
|
110
|
-
url.searchParams.append(
|
|
111
|
-
entity && url.searchParams.append(
|
|
112
|
-
prefersDark && url.searchParams.append(
|
|
113
|
-
theme && url.searchParams.append(
|
|
111
|
+
url.searchParams.append('accessBoost', accessBoost.toString());
|
|
112
|
+
entity && url.searchParams.append('entity', entity);
|
|
113
|
+
prefersDark && url.searchParams.append('prefersDark', prefersDark);
|
|
114
|
+
theme && url.searchParams.append('theme', theme);
|
|
115
|
+
timezone && url.searchParams.append('timezone', timezone);
|
|
114
116
|
filterSearchParam &&
|
|
115
|
-
url.searchParams.append(
|
|
116
|
-
linkAccess && url.searchParams.append(
|
|
117
|
-
customTheme && url.searchParams.append(
|
|
118
|
-
customThemeId && url.searchParams.append(
|
|
119
|
-
connectionRoles &&
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
modelRoles && url.searchParams.append("modelRoles", modelRoles);
|
|
117
|
+
url.searchParams.append('filterSearchParam', filterSearchParam);
|
|
118
|
+
linkAccess && url.searchParams.append('linkAccess', linkAccess);
|
|
119
|
+
customTheme && url.searchParams.append('customTheme', customTheme);
|
|
120
|
+
customThemeId && url.searchParams.append('customThemeId', customThemeId);
|
|
121
|
+
connectionRoles && url.searchParams.append('connectionRoles', connectionRoles);
|
|
122
|
+
mode && url.searchParams.append('mode', mode);
|
|
123
|
+
modelRoles && url.searchParams.append('modelRoles', modelRoles);
|
|
123
124
|
entityFolderContentRole &&
|
|
124
|
-
url.searchParams.append(
|
|
125
|
+
url.searchParams.append('entityFolderContentRole', entityFolderContentRole);
|
|
125
126
|
entityFolderGroupContentRole &&
|
|
126
|
-
url.searchParams.append(
|
|
127
|
-
groups && url.searchParams.append(
|
|
128
|
-
email && url.searchParams.append(
|
|
129
|
-
uiSettings && url.searchParams.append(
|
|
127
|
+
url.searchParams.append('entityFolderGroupContentRole', entityFolderGroupContentRole);
|
|
128
|
+
groups && url.searchParams.append('groups', groups);
|
|
129
|
+
email && url.searchParams.append('email', email);
|
|
130
|
+
uiSettings && url.searchParams.append('uiSettings', uiSettings);
|
|
130
131
|
entityGroupLabel &&
|
|
131
|
-
url.searchParams.append(
|
|
132
|
+
url.searchParams.append('entityGroupLabel', entityGroupLabel);
|
|
132
133
|
entityFolderLabel &&
|
|
133
|
-
url.searchParams.append(
|
|
134
|
+
url.searchParams.append('entityFolderLabel', entityFolderLabel);
|
|
134
135
|
preserveEntityFolderContentRole !== undefined &&
|
|
135
|
-
url.searchParams.append(
|
|
136
|
-
branch && url.searchParams.append(
|
|
136
|
+
url.searchParams.append('preserveEntityFolderContentRole', preserveEntityFolderContentRole.toString());
|
|
137
|
+
branch && url.searchParams.append('branch', branch);
|
|
137
138
|
return url.toString();
|
|
138
139
|
};
|
|
139
140
|
const buildContentPath = (contentType, contentId) => `/${contentType}/${contentId}`;
|
|
@@ -153,9 +154,7 @@ export const embedSsoWorkbook = async (props) => {
|
|
|
153
154
|
export const embedSsoContentDiscovery = async (props) => {
|
|
154
155
|
// Entity paths for the Content Discovery embed no longer require stripping the leading `/`.
|
|
155
156
|
// This check maintains backwards compatibility with the old behavior.
|
|
156
|
-
const contentPath = props.path.startsWith(
|
|
157
|
-
? props.path
|
|
158
|
-
: `/${props.path}`;
|
|
157
|
+
const contentPath = props.path.startsWith('/') ? props.path : `/${props.path}`;
|
|
159
158
|
return embedSsoContent({
|
|
160
159
|
...props,
|
|
161
160
|
contentPath,
|
|
@@ -179,13 +178,13 @@ export const createSessionToken = async ({ apiKey, domain, host, organizationNam
|
|
|
179
178
|
});
|
|
180
179
|
// Hit the endpoint with the endpoint params + signature
|
|
181
180
|
const fetchResponse = await fetch(generateRequestUrl, {
|
|
182
|
-
method:
|
|
181
|
+
method: 'POST',
|
|
183
182
|
body: JSON.stringify({
|
|
184
183
|
...restProps,
|
|
185
184
|
}),
|
|
186
185
|
headers: {
|
|
187
186
|
Authorization: `Bearer ${apiKey}`,
|
|
188
|
-
|
|
187
|
+
'Content-type': 'application/json',
|
|
189
188
|
},
|
|
190
189
|
});
|
|
191
190
|
if (!fetchResponse.ok)
|
|
@@ -195,7 +194,7 @@ export const createSessionToken = async ({ apiKey, domain, host, organizationNam
|
|
|
195
194
|
};
|
|
196
195
|
const fetchData = (await fetchResponse.json());
|
|
197
196
|
if (!fetchData.sessionId)
|
|
198
|
-
return { error:
|
|
197
|
+
return { error: 'No session ID returned from endpoint.', success: false };
|
|
199
198
|
return { sessionToken: fetchData.sessionId, success: true };
|
|
200
199
|
};
|
|
201
200
|
/**
|
|
@@ -222,13 +221,12 @@ theme, }) => {
|
|
|
222
221
|
theme,
|
|
223
222
|
branch,
|
|
224
223
|
});
|
|
225
|
-
branch !== undefined &&
|
|
226
|
-
redeemRequestUrl.searchParams.append("branch", branch);
|
|
224
|
+
branch !== undefined && redeemRequestUrl.searchParams.append('branch', branch);
|
|
227
225
|
prefersDark !== undefined &&
|
|
228
|
-
redeemRequestUrl.searchParams.append(
|
|
229
|
-
theme !== undefined && redeemRequestUrl.searchParams.append(
|
|
230
|
-
redeemRequestUrl.searchParams.append(
|
|
231
|
-
redeemRequestUrl.searchParams.append(
|
|
232
|
-
redeemRequestUrl.searchParams.append(
|
|
226
|
+
redeemRequestUrl.searchParams.append('prefersDark', prefersDark);
|
|
227
|
+
theme !== undefined && redeemRequestUrl.searchParams.append('theme', theme);
|
|
228
|
+
redeemRequestUrl.searchParams.append('nonce', nonce);
|
|
229
|
+
redeemRequestUrl.searchParams.append('sessionId', sessionId);
|
|
230
|
+
redeemRequestUrl.searchParams.append('signature', signature);
|
|
233
231
|
return redeemRequestUrl.toString();
|
|
234
232
|
};
|
package/lib/esm/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
1
|
+
export * from './embed.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export * from './signature.js';
|
|
4
|
+
export * from './constants.js';
|
package/lib/esm/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
1
|
+
export * from './embed.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export * from './signature.js';
|
|
4
|
+
export * from './constants.js';
|