@omni-co/embed 0.6.0 → 0.7.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/lib/cjs/embed.d.ts +24 -1
- package/lib/cjs/embed.js +98 -14
- package/lib/cjs/types.d.ts +36 -29
- package/lib/esm/embed.d.ts +24 -1
- package/lib/esm/embed.js +96 -14
- package/lib/esm/types.d.ts +36 -29
- package/package.json +1 -1
package/lib/cjs/embed.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EmbedSsoContentDiscoveryProps, EmbedSsoDashboardProps, EmbedSsoWorkbookProps } from "./types";
|
|
1
|
+
import { EmbedSsoContentDiscoveryProps, EmbedSsoDashboardProps, EmbedSsoWorkbookProps, GenerateEmbedSsoSessionProps, RedeemEmbedSsoSessionProps } from "./types";
|
|
2
2
|
export declare const embedSsoDashboard: (props: EmbedSsoDashboardProps) => Promise<string>;
|
|
3
3
|
export declare const embedSsoWorkbook: (props: EmbedSsoWorkbookProps) => Promise<string>;
|
|
4
4
|
/**
|
|
@@ -7,3 +7,26 @@ export declare const embedSsoWorkbook: (props: EmbedSsoWorkbookProps) => Promise
|
|
|
7
7
|
* @returns Promise wrapped around a generated embed login url.
|
|
8
8
|
*/
|
|
9
9
|
export declare const embedSsoContentDiscovery: (props: EmbedSsoContentDiscoveryProps) => Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* First step of the 2-step SSO embed login flow. If successful, outputs an object that
|
|
12
|
+
* includes an SSO embed session token. If unsuccessful, outputs an object that includes
|
|
13
|
+
* an error message.
|
|
14
|
+
*
|
|
15
|
+
* To elaborate, this function upserts an SSO embed user and session to the specified Omni
|
|
16
|
+
* organization / host. The returned session token corresponds to the generated session.
|
|
17
|
+
*/
|
|
18
|
+
export declare const createSessionToken: ({ apiKey, domain, host, organizationName, port, ...restProps }: GenerateEmbedSsoSessionProps) => Promise<{
|
|
19
|
+
success: true;
|
|
20
|
+
sessionToken: string;
|
|
21
|
+
error?: never;
|
|
22
|
+
} | {
|
|
23
|
+
success: false;
|
|
24
|
+
sessionToken?: never;
|
|
25
|
+
error: string;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Second step of the 2-step SSO embed login flow. Uses a generated session token
|
|
29
|
+
* to output an session redemption URL that can be used to display embedded content.
|
|
30
|
+
* The returned URL should be passed into an `iframe`'s src attribute.
|
|
31
|
+
*/
|
|
32
|
+
export declare const redeemSessionToken: ({ domain, host, organizationName, port, nonce: propsNonce, secret, prefersDark, sessionToken: sessionId, theme, }: RedeemEmbedSsoSessionProps) => Promise<string>;
|
package/lib/cjs/embed.js
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.embedSsoContentDiscovery = exports.embedSsoWorkbook = exports.embedSsoDashboard = void 0;
|
|
3
|
+
exports.redeemSessionToken = exports.createSessionToken = exports.embedSsoContentDiscovery = exports.embedSsoWorkbook = exports.embedSsoDashboard = void 0;
|
|
4
4
|
const random_str_1 = require("./random-str");
|
|
5
5
|
const signature_1 = require("./signature");
|
|
6
6
|
const types_1 = require("./types");
|
|
7
7
|
const defaultEmbedDomain = "embed-omniapp.co";
|
|
8
8
|
const embedLoginPath = "/embed/login";
|
|
9
|
+
const generateSsoEmbedSessionPath = "/api/unstable/embed/sso/generate-session";
|
|
10
|
+
const redeemSsoEmbedSessionPath = "/embed/sso/redeem-session";
|
|
11
|
+
const createEmbedRequestUrl = (props) => {
|
|
12
|
+
let host, domain, organizationName;
|
|
13
|
+
if ("host" in props)
|
|
14
|
+
host = props.host;
|
|
15
|
+
if ("organizationName" in props) {
|
|
16
|
+
domain = props.domain;
|
|
17
|
+
organizationName = props.organizationName;
|
|
18
|
+
}
|
|
19
|
+
// Generate the url for the embed login route
|
|
20
|
+
if (!host)
|
|
21
|
+
host = `${organizationName}.${domain !== null && domain !== void 0 ? domain : defaultEmbedDomain}`;
|
|
22
|
+
const url = new URL(props.requestPath, `https://${host}${props.port ? `:${props.port}` : ""}`);
|
|
23
|
+
return url;
|
|
24
|
+
};
|
|
9
25
|
const validateProps = (props) => {
|
|
10
26
|
const { externalId, name, nonce, secret, userAttributes, customTheme, customThemeId, } = props;
|
|
11
27
|
let host, domain, organizationName;
|
|
@@ -36,24 +52,20 @@ const validateProps = (props) => {
|
|
|
36
52
|
};
|
|
37
53
|
const embedSsoContent = async (props) => {
|
|
38
54
|
validateProps(props);
|
|
39
|
-
let { accessBoost, connectionRoles: rawConnectionRoles, contentPath, customTheme: rawCustomTheme, customThemeId, email, externalId, filterSearchParam, linkAccess, mode, name, nonce, port, secret, userAttributes: rawUserAttributes, entity, prefersDark, theme, entityFolderContentRole, groups: rawGroups, } = props;
|
|
40
|
-
let host, domain, organizationName;
|
|
41
|
-
if ("host" in props)
|
|
42
|
-
host = props.host;
|
|
43
|
-
if ("organizationName" in props) {
|
|
44
|
-
domain = props.domain;
|
|
45
|
-
organizationName = props.organizationName;
|
|
46
|
-
}
|
|
55
|
+
let { accessBoost, connectionRoles: rawConnectionRoles, contentPath, customTheme: rawCustomTheme, customThemeId, domain, email, externalId, filterSearchParam, host, linkAccess, mode, name, nonce, organizationName, port, secret, userAttributes: rawUserAttributes, entity, prefersDark, theme, entityFolderContentRole, groups: rawGroups, } = props;
|
|
47
56
|
// Handle defaults
|
|
48
57
|
nonce = nonce !== null && nonce !== void 0 ? nonce : (await (0, random_str_1.random32ByteString)());
|
|
49
58
|
const userAttributes = rawUserAttributes && JSON.stringify(rawUserAttributes);
|
|
50
59
|
const customTheme = rawCustomTheme && JSON.stringify(rawCustomTheme);
|
|
51
60
|
const connectionRoles = rawConnectionRoles && JSON.stringify(rawConnectionRoles);
|
|
52
61
|
const groups = rawGroups && JSON.stringify(rawGroups);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
host
|
|
56
|
-
|
|
62
|
+
const url = createEmbedRequestUrl({
|
|
63
|
+
domain,
|
|
64
|
+
host,
|
|
65
|
+
organizationName,
|
|
66
|
+
port,
|
|
67
|
+
requestPath: embedLoginPath,
|
|
68
|
+
});
|
|
57
69
|
// Get a signature for the request
|
|
58
70
|
const signature = (0, signature_1.getSignature)({
|
|
59
71
|
// Required parameters
|
|
@@ -124,10 +136,82 @@ exports.embedSsoWorkbook = embedSsoWorkbook;
|
|
|
124
136
|
const embedSsoContentDiscovery = async (props) => {
|
|
125
137
|
// Entity paths for the Content Discovery embed no longer require stripping the leading `/`.
|
|
126
138
|
// This check maintains backwards compatibility with the old behavior.
|
|
127
|
-
const contentPath = props.path.startsWith(
|
|
139
|
+
const contentPath = props.path.startsWith("/")
|
|
140
|
+
? props.path
|
|
141
|
+
: `/${props.path}`;
|
|
128
142
|
return embedSsoContent({
|
|
129
143
|
...props,
|
|
130
144
|
contentPath,
|
|
131
145
|
});
|
|
132
146
|
};
|
|
133
147
|
exports.embedSsoContentDiscovery = embedSsoContentDiscovery;
|
|
148
|
+
/**
|
|
149
|
+
* First step of the 2-step SSO embed login flow. If successful, outputs an object that
|
|
150
|
+
* includes an SSO embed session token. If unsuccessful, outputs an object that includes
|
|
151
|
+
* an error message.
|
|
152
|
+
*
|
|
153
|
+
* To elaborate, this function upserts an SSO embed user and session to the specified Omni
|
|
154
|
+
* organization / host. The returned session token corresponds to the generated session.
|
|
155
|
+
*/
|
|
156
|
+
const createSessionToken = async ({ apiKey, domain, host, organizationName, port, ...restProps }) => {
|
|
157
|
+
const generateRequestUrl = createEmbedRequestUrl({
|
|
158
|
+
domain,
|
|
159
|
+
host,
|
|
160
|
+
organizationName,
|
|
161
|
+
port,
|
|
162
|
+
requestPath: generateSsoEmbedSessionPath,
|
|
163
|
+
});
|
|
164
|
+
// Hit the endpoint with the endpoint params + signature
|
|
165
|
+
const fetchResponse = await fetch(generateRequestUrl, {
|
|
166
|
+
method: "POST",
|
|
167
|
+
body: JSON.stringify({
|
|
168
|
+
...restProps,
|
|
169
|
+
}),
|
|
170
|
+
headers: {
|
|
171
|
+
Authorization: `Bearer ${apiKey}`,
|
|
172
|
+
"Content-type": "application/json",
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
if (!fetchResponse.ok)
|
|
176
|
+
return {
|
|
177
|
+
error: `Failed to generate embed session: ${fetchResponse.statusText}`,
|
|
178
|
+
success: false,
|
|
179
|
+
};
|
|
180
|
+
const fetchData = await fetchResponse.json();
|
|
181
|
+
if (!fetchData.sessionId)
|
|
182
|
+
return { error: "No session ID returned from endpoint.", success: false };
|
|
183
|
+
return { sessionToken: fetchData.sessionId, success: true };
|
|
184
|
+
};
|
|
185
|
+
exports.createSessionToken = createSessionToken;
|
|
186
|
+
/**
|
|
187
|
+
* Second step of the 2-step SSO embed login flow. Uses a generated session token
|
|
188
|
+
* to output an session redemption URL that can be used to display embedded content.
|
|
189
|
+
* The returned URL should be passed into an `iframe`'s src attribute.
|
|
190
|
+
*/
|
|
191
|
+
const redeemSessionToken = async ({ domain, host, organizationName, port, nonce: propsNonce, secret, prefersDark, sessionToken: sessionId, // sessionToken is just a facade for the sessionId
|
|
192
|
+
theme, }) => {
|
|
193
|
+
const redeemRequestUrl = createEmbedRequestUrl({
|
|
194
|
+
domain,
|
|
195
|
+
host,
|
|
196
|
+
organizationName,
|
|
197
|
+
port,
|
|
198
|
+
requestPath: redeemSsoEmbedSessionPath,
|
|
199
|
+
});
|
|
200
|
+
const nonce = propsNonce !== null && propsNonce !== void 0 ? propsNonce : (await (0, random_str_1.random32ByteString)());
|
|
201
|
+
const signature = (0, signature_1.signSessionRedemption)({
|
|
202
|
+
requestUrl: redeemRequestUrl.toString(),
|
|
203
|
+
nonce,
|
|
204
|
+
prefersDark,
|
|
205
|
+
sessionId,
|
|
206
|
+
secret,
|
|
207
|
+
theme,
|
|
208
|
+
});
|
|
209
|
+
prefersDark !== undefined &&
|
|
210
|
+
redeemRequestUrl.searchParams.append("prefersDark", prefersDark);
|
|
211
|
+
theme !== undefined && redeemRequestUrl.searchParams.append("theme", theme);
|
|
212
|
+
redeemRequestUrl.searchParams.append("nonce", nonce);
|
|
213
|
+
redeemRequestUrl.searchParams.append("sessionId", sessionId);
|
|
214
|
+
redeemRequestUrl.searchParams.append("signature", signature);
|
|
215
|
+
return redeemRequestUrl.toString();
|
|
216
|
+
};
|
|
217
|
+
exports.redeemSessionToken = redeemSessionToken;
|
package/lib/cjs/types.d.ts
CHANGED
|
@@ -3,6 +3,35 @@ export declare enum EmbedSessionMode {
|
|
|
3
3
|
Application = "APPLICATION",
|
|
4
4
|
SingleContent = "SINGLE_CONTENT"
|
|
5
5
|
}
|
|
6
|
+
type HostProps = {
|
|
7
|
+
host?: never;
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated Introduced for internal testing only. For vanity domains, use the `host` prop instead.
|
|
10
|
+
*
|
|
11
|
+
* @throws Error if `host` is provided.
|
|
12
|
+
*/
|
|
13
|
+
domain?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Name of the organization the content belongs to. If provided, generates a default embed host
|
|
16
|
+
* URL in the form of `https://<organizationName>.embed-omniapp.co`.
|
|
17
|
+
*
|
|
18
|
+
* @throws Error if `host` is provided.
|
|
19
|
+
*/
|
|
20
|
+
organizationName: string;
|
|
21
|
+
} | {
|
|
22
|
+
domain?: never;
|
|
23
|
+
organizationName?: never;
|
|
24
|
+
/**
|
|
25
|
+
* Used to set the host of signed embed URL, required when using vanity domains.
|
|
26
|
+
* Protocol is not required, as https is assumed.
|
|
27
|
+
* Port is not accepted. If required, use the `port` prop.
|
|
28
|
+
*
|
|
29
|
+
* @throws Error if `domain` or `organizationName` are provided.
|
|
30
|
+
* @example "omni.example.com"
|
|
31
|
+
* @example "omni.another-example.app"
|
|
32
|
+
*/
|
|
33
|
+
host: string;
|
|
34
|
+
};
|
|
6
35
|
type EmbedSsoBaseProps = {
|
|
7
36
|
externalId: string;
|
|
8
37
|
name: string;
|
|
@@ -45,35 +74,7 @@ type EmbedSsoBaseProps = {
|
|
|
45
74
|
linkAccess?: string;
|
|
46
75
|
nonce?: string;
|
|
47
76
|
port?: number;
|
|
48
|
-
} &
|
|
49
|
-
host?: never;
|
|
50
|
-
/**
|
|
51
|
-
* @deprecated Introduced for internal testing only. For vanity domains, use the `host` prop instead.
|
|
52
|
-
*
|
|
53
|
-
* @throws Error if `host` is provided.
|
|
54
|
-
*/
|
|
55
|
-
domain?: string;
|
|
56
|
-
/**
|
|
57
|
-
* Name of the organization the content belongs to. If provided, generates a default embed host
|
|
58
|
-
* URL in the form of `https://<organizationName>.embed-omniapp.co`.
|
|
59
|
-
*
|
|
60
|
-
* @throws Error if `host` is provided.
|
|
61
|
-
*/
|
|
62
|
-
organizationName: string;
|
|
63
|
-
} | {
|
|
64
|
-
domain?: never;
|
|
65
|
-
organizationName?: never;
|
|
66
|
-
/**
|
|
67
|
-
* Used to set the host of signed embed URL, required when using vanity domains.
|
|
68
|
-
* Protocol is not required, as https is assumed.
|
|
69
|
-
* Port is not accepted. If required, use the `port` prop.
|
|
70
|
-
*
|
|
71
|
-
* @throws Error if `domain` or `organizationName` are provided.
|
|
72
|
-
* @example "omni.example.com"
|
|
73
|
-
* @example "omni.another-example.app"
|
|
74
|
-
*/
|
|
75
|
-
host: string;
|
|
76
|
-
});
|
|
77
|
+
} & HostProps;
|
|
77
78
|
type EmbedSsoDashboardOnlyProps = {
|
|
78
79
|
accessBoost?: boolean;
|
|
79
80
|
};
|
|
@@ -218,4 +219,10 @@ export type EmbedSsoContentDiscoveryProps = EmbedSsoBaseProps & {
|
|
|
218
219
|
*/
|
|
219
220
|
path: string;
|
|
220
221
|
};
|
|
222
|
+
export type GenerateEmbedSsoSessionProps = Omit<EmbedSsoContentProps, "prefersDark" | "theme" | "secret" | "nonce"> & {
|
|
223
|
+
apiKey: string;
|
|
224
|
+
} & HostProps;
|
|
225
|
+
export type RedeemEmbedSsoSessionProps = Pick<EmbedSsoContentProps, "nonce" | "prefersDark" | "secret" | "theme" | "port"> & {
|
|
226
|
+
sessionToken: string;
|
|
227
|
+
} & HostProps;
|
|
221
228
|
export {};
|
package/lib/esm/embed.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EmbedSsoContentDiscoveryProps, EmbedSsoDashboardProps, EmbedSsoWorkbookProps } from "./types";
|
|
1
|
+
import { EmbedSsoContentDiscoveryProps, EmbedSsoDashboardProps, EmbedSsoWorkbookProps, GenerateEmbedSsoSessionProps, RedeemEmbedSsoSessionProps } from "./types";
|
|
2
2
|
export declare const embedSsoDashboard: (props: EmbedSsoDashboardProps) => Promise<string>;
|
|
3
3
|
export declare const embedSsoWorkbook: (props: EmbedSsoWorkbookProps) => Promise<string>;
|
|
4
4
|
/**
|
|
@@ -7,3 +7,26 @@ export declare const embedSsoWorkbook: (props: EmbedSsoWorkbookProps) => Promise
|
|
|
7
7
|
* @returns Promise wrapped around a generated embed login url.
|
|
8
8
|
*/
|
|
9
9
|
export declare const embedSsoContentDiscovery: (props: EmbedSsoContentDiscoveryProps) => Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* First step of the 2-step SSO embed login flow. If successful, outputs an object that
|
|
12
|
+
* includes an SSO embed session token. If unsuccessful, outputs an object that includes
|
|
13
|
+
* an error message.
|
|
14
|
+
*
|
|
15
|
+
* To elaborate, this function upserts an SSO embed user and session to the specified Omni
|
|
16
|
+
* organization / host. The returned session token corresponds to the generated session.
|
|
17
|
+
*/
|
|
18
|
+
export declare const createSessionToken: ({ apiKey, domain, host, organizationName, port, ...restProps }: GenerateEmbedSsoSessionProps) => Promise<{
|
|
19
|
+
success: true;
|
|
20
|
+
sessionToken: string;
|
|
21
|
+
error?: never;
|
|
22
|
+
} | {
|
|
23
|
+
success: false;
|
|
24
|
+
sessionToken?: never;
|
|
25
|
+
error: string;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Second step of the 2-step SSO embed login flow. Uses a generated session token
|
|
29
|
+
* to output an session redemption URL that can be used to display embedded content.
|
|
30
|
+
* The returned URL should be passed into an `iframe`'s src attribute.
|
|
31
|
+
*/
|
|
32
|
+
export declare const redeemSessionToken: ({ domain, host, organizationName, port, nonce: propsNonce, secret, prefersDark, sessionToken: sessionId, theme, }: RedeemEmbedSsoSessionProps) => Promise<string>;
|
package/lib/esm/embed.js
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import { random32ByteString } from "./random-str";
|
|
2
|
-
import { getSignature } from "./signature";
|
|
2
|
+
import { getSignature, signSessionRedemption } from "./signature";
|
|
3
3
|
import { EmbeddedContent, } from "./types";
|
|
4
4
|
const defaultEmbedDomain = "embed-omniapp.co";
|
|
5
5
|
const embedLoginPath = "/embed/login";
|
|
6
|
+
const generateSsoEmbedSessionPath = "/api/unstable/embed/sso/generate-session";
|
|
7
|
+
const redeemSsoEmbedSessionPath = "/embed/sso/redeem-session";
|
|
8
|
+
const createEmbedRequestUrl = (props) => {
|
|
9
|
+
let host, domain, organizationName;
|
|
10
|
+
if ("host" in props)
|
|
11
|
+
host = props.host;
|
|
12
|
+
if ("organizationName" in props) {
|
|
13
|
+
domain = props.domain;
|
|
14
|
+
organizationName = props.organizationName;
|
|
15
|
+
}
|
|
16
|
+
// Generate the url for the embed login route
|
|
17
|
+
if (!host)
|
|
18
|
+
host = `${organizationName}.${domain !== null && domain !== void 0 ? domain : defaultEmbedDomain}`;
|
|
19
|
+
const url = new URL(props.requestPath, `https://${host}${props.port ? `:${props.port}` : ""}`);
|
|
20
|
+
return url;
|
|
21
|
+
};
|
|
6
22
|
const validateProps = (props) => {
|
|
7
23
|
const { externalId, name, nonce, secret, userAttributes, customTheme, customThemeId, } = props;
|
|
8
24
|
let host, domain, organizationName;
|
|
@@ -33,24 +49,20 @@ const validateProps = (props) => {
|
|
|
33
49
|
};
|
|
34
50
|
const embedSsoContent = async (props) => {
|
|
35
51
|
validateProps(props);
|
|
36
|
-
let { accessBoost, connectionRoles: rawConnectionRoles, contentPath, customTheme: rawCustomTheme, customThemeId, email, externalId, filterSearchParam, linkAccess, mode, name, nonce, port, secret, userAttributes: rawUserAttributes, entity, prefersDark, theme, entityFolderContentRole, groups: rawGroups, } = props;
|
|
37
|
-
let host, domain, organizationName;
|
|
38
|
-
if ("host" in props)
|
|
39
|
-
host = props.host;
|
|
40
|
-
if ("organizationName" in props) {
|
|
41
|
-
domain = props.domain;
|
|
42
|
-
organizationName = props.organizationName;
|
|
43
|
-
}
|
|
52
|
+
let { accessBoost, connectionRoles: rawConnectionRoles, contentPath, customTheme: rawCustomTheme, customThemeId, domain, email, externalId, filterSearchParam, host, linkAccess, mode, name, nonce, organizationName, port, secret, userAttributes: rawUserAttributes, entity, prefersDark, theme, entityFolderContentRole, groups: rawGroups, } = props;
|
|
44
53
|
// Handle defaults
|
|
45
54
|
nonce = nonce !== null && nonce !== void 0 ? nonce : (await random32ByteString());
|
|
46
55
|
const userAttributes = rawUserAttributes && JSON.stringify(rawUserAttributes);
|
|
47
56
|
const customTheme = rawCustomTheme && JSON.stringify(rawCustomTheme);
|
|
48
57
|
const connectionRoles = rawConnectionRoles && JSON.stringify(rawConnectionRoles);
|
|
49
58
|
const groups = rawGroups && JSON.stringify(rawGroups);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
host
|
|
53
|
-
|
|
59
|
+
const url = createEmbedRequestUrl({
|
|
60
|
+
domain,
|
|
61
|
+
host,
|
|
62
|
+
organizationName,
|
|
63
|
+
port,
|
|
64
|
+
requestPath: embedLoginPath,
|
|
65
|
+
});
|
|
54
66
|
// Get a signature for the request
|
|
55
67
|
const signature = getSignature({
|
|
56
68
|
// Required parameters
|
|
@@ -119,9 +131,79 @@ export const embedSsoWorkbook = async (props) => {
|
|
|
119
131
|
export const embedSsoContentDiscovery = async (props) => {
|
|
120
132
|
// Entity paths for the Content Discovery embed no longer require stripping the leading `/`.
|
|
121
133
|
// This check maintains backwards compatibility with the old behavior.
|
|
122
|
-
const contentPath = props.path.startsWith(
|
|
134
|
+
const contentPath = props.path.startsWith("/")
|
|
135
|
+
? props.path
|
|
136
|
+
: `/${props.path}`;
|
|
123
137
|
return embedSsoContent({
|
|
124
138
|
...props,
|
|
125
139
|
contentPath,
|
|
126
140
|
});
|
|
127
141
|
};
|
|
142
|
+
/**
|
|
143
|
+
* First step of the 2-step SSO embed login flow. If successful, outputs an object that
|
|
144
|
+
* includes an SSO embed session token. If unsuccessful, outputs an object that includes
|
|
145
|
+
* an error message.
|
|
146
|
+
*
|
|
147
|
+
* To elaborate, this function upserts an SSO embed user and session to the specified Omni
|
|
148
|
+
* organization / host. The returned session token corresponds to the generated session.
|
|
149
|
+
*/
|
|
150
|
+
export const createSessionToken = async ({ apiKey, domain, host, organizationName, port, ...restProps }) => {
|
|
151
|
+
const generateRequestUrl = createEmbedRequestUrl({
|
|
152
|
+
domain,
|
|
153
|
+
host,
|
|
154
|
+
organizationName,
|
|
155
|
+
port,
|
|
156
|
+
requestPath: generateSsoEmbedSessionPath,
|
|
157
|
+
});
|
|
158
|
+
// Hit the endpoint with the endpoint params + signature
|
|
159
|
+
const fetchResponse = await fetch(generateRequestUrl, {
|
|
160
|
+
method: "POST",
|
|
161
|
+
body: JSON.stringify({
|
|
162
|
+
...restProps,
|
|
163
|
+
}),
|
|
164
|
+
headers: {
|
|
165
|
+
Authorization: `Bearer ${apiKey}`,
|
|
166
|
+
"Content-type": "application/json",
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
if (!fetchResponse.ok)
|
|
170
|
+
return {
|
|
171
|
+
error: `Failed to generate embed session: ${fetchResponse.statusText}`,
|
|
172
|
+
success: false,
|
|
173
|
+
};
|
|
174
|
+
const fetchData = await fetchResponse.json();
|
|
175
|
+
if (!fetchData.sessionId)
|
|
176
|
+
return { error: "No session ID returned from endpoint.", success: false };
|
|
177
|
+
return { sessionToken: fetchData.sessionId, success: true };
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Second step of the 2-step SSO embed login flow. Uses a generated session token
|
|
181
|
+
* to output an session redemption URL that can be used to display embedded content.
|
|
182
|
+
* The returned URL should be passed into an `iframe`'s src attribute.
|
|
183
|
+
*/
|
|
184
|
+
export const redeemSessionToken = async ({ domain, host, organizationName, port, nonce: propsNonce, secret, prefersDark, sessionToken: sessionId, // sessionToken is just a facade for the sessionId
|
|
185
|
+
theme, }) => {
|
|
186
|
+
const redeemRequestUrl = createEmbedRequestUrl({
|
|
187
|
+
domain,
|
|
188
|
+
host,
|
|
189
|
+
organizationName,
|
|
190
|
+
port,
|
|
191
|
+
requestPath: redeemSsoEmbedSessionPath,
|
|
192
|
+
});
|
|
193
|
+
const nonce = propsNonce !== null && propsNonce !== void 0 ? propsNonce : (await random32ByteString());
|
|
194
|
+
const signature = signSessionRedemption({
|
|
195
|
+
requestUrl: redeemRequestUrl.toString(),
|
|
196
|
+
nonce,
|
|
197
|
+
prefersDark,
|
|
198
|
+
sessionId,
|
|
199
|
+
secret,
|
|
200
|
+
theme,
|
|
201
|
+
});
|
|
202
|
+
prefersDark !== undefined &&
|
|
203
|
+
redeemRequestUrl.searchParams.append("prefersDark", prefersDark);
|
|
204
|
+
theme !== undefined && redeemRequestUrl.searchParams.append("theme", theme);
|
|
205
|
+
redeemRequestUrl.searchParams.append("nonce", nonce);
|
|
206
|
+
redeemRequestUrl.searchParams.append("sessionId", sessionId);
|
|
207
|
+
redeemRequestUrl.searchParams.append("signature", signature);
|
|
208
|
+
return redeemRequestUrl.toString();
|
|
209
|
+
};
|
package/lib/esm/types.d.ts
CHANGED
|
@@ -3,6 +3,35 @@ export declare enum EmbedSessionMode {
|
|
|
3
3
|
Application = "APPLICATION",
|
|
4
4
|
SingleContent = "SINGLE_CONTENT"
|
|
5
5
|
}
|
|
6
|
+
type HostProps = {
|
|
7
|
+
host?: never;
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated Introduced for internal testing only. For vanity domains, use the `host` prop instead.
|
|
10
|
+
*
|
|
11
|
+
* @throws Error if `host` is provided.
|
|
12
|
+
*/
|
|
13
|
+
domain?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Name of the organization the content belongs to. If provided, generates a default embed host
|
|
16
|
+
* URL in the form of `https://<organizationName>.embed-omniapp.co`.
|
|
17
|
+
*
|
|
18
|
+
* @throws Error if `host` is provided.
|
|
19
|
+
*/
|
|
20
|
+
organizationName: string;
|
|
21
|
+
} | {
|
|
22
|
+
domain?: never;
|
|
23
|
+
organizationName?: never;
|
|
24
|
+
/**
|
|
25
|
+
* Used to set the host of signed embed URL, required when using vanity domains.
|
|
26
|
+
* Protocol is not required, as https is assumed.
|
|
27
|
+
* Port is not accepted. If required, use the `port` prop.
|
|
28
|
+
*
|
|
29
|
+
* @throws Error if `domain` or `organizationName` are provided.
|
|
30
|
+
* @example "omni.example.com"
|
|
31
|
+
* @example "omni.another-example.app"
|
|
32
|
+
*/
|
|
33
|
+
host: string;
|
|
34
|
+
};
|
|
6
35
|
type EmbedSsoBaseProps = {
|
|
7
36
|
externalId: string;
|
|
8
37
|
name: string;
|
|
@@ -45,35 +74,7 @@ type EmbedSsoBaseProps = {
|
|
|
45
74
|
linkAccess?: string;
|
|
46
75
|
nonce?: string;
|
|
47
76
|
port?: number;
|
|
48
|
-
} &
|
|
49
|
-
host?: never;
|
|
50
|
-
/**
|
|
51
|
-
* @deprecated Introduced for internal testing only. For vanity domains, use the `host` prop instead.
|
|
52
|
-
*
|
|
53
|
-
* @throws Error if `host` is provided.
|
|
54
|
-
*/
|
|
55
|
-
domain?: string;
|
|
56
|
-
/**
|
|
57
|
-
* Name of the organization the content belongs to. If provided, generates a default embed host
|
|
58
|
-
* URL in the form of `https://<organizationName>.embed-omniapp.co`.
|
|
59
|
-
*
|
|
60
|
-
* @throws Error if `host` is provided.
|
|
61
|
-
*/
|
|
62
|
-
organizationName: string;
|
|
63
|
-
} | {
|
|
64
|
-
domain?: never;
|
|
65
|
-
organizationName?: never;
|
|
66
|
-
/**
|
|
67
|
-
* Used to set the host of signed embed URL, required when using vanity domains.
|
|
68
|
-
* Protocol is not required, as https is assumed.
|
|
69
|
-
* Port is not accepted. If required, use the `port` prop.
|
|
70
|
-
*
|
|
71
|
-
* @throws Error if `domain` or `organizationName` are provided.
|
|
72
|
-
* @example "omni.example.com"
|
|
73
|
-
* @example "omni.another-example.app"
|
|
74
|
-
*/
|
|
75
|
-
host: string;
|
|
76
|
-
});
|
|
77
|
+
} & HostProps;
|
|
77
78
|
type EmbedSsoDashboardOnlyProps = {
|
|
78
79
|
accessBoost?: boolean;
|
|
79
80
|
};
|
|
@@ -218,4 +219,10 @@ export type EmbedSsoContentDiscoveryProps = EmbedSsoBaseProps & {
|
|
|
218
219
|
*/
|
|
219
220
|
path: string;
|
|
220
221
|
};
|
|
222
|
+
export type GenerateEmbedSsoSessionProps = Omit<EmbedSsoContentProps, "prefersDark" | "theme" | "secret" | "nonce"> & {
|
|
223
|
+
apiKey: string;
|
|
224
|
+
} & HostProps;
|
|
225
|
+
export type RedeemEmbedSsoSessionProps = Pick<EmbedSsoContentProps, "nonce" | "prefersDark" | "secret" | "theme" | "port"> & {
|
|
226
|
+
sessionToken: string;
|
|
227
|
+
} & HostProps;
|
|
221
228
|
export {};
|
package/package.json
CHANGED