Skip to content

Commit

Permalink
fix: multiple flows on the same page (#868)
Browse files Browse the repository at this point in the history
  • Loading branch information
nirgur authored Dec 22, 2024
1 parent 334053c commit c4182b3
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ export class DescopeComponent implements OnInit, OnChanges {

ngOnInit() {
const sdk = this.authService.descopeSdk; // Capture the class context in a variable
DescopeWc.sdkConfigOverrides = {
const WebComponent: any = customElements?.get('descope-wc') || DescopeWc;

WebComponent.sdkConfigOverrides = {
// Overrides the web-component's base headers to indicate usage via the React SDK
baseHeaders,
// Disables token persistence within the web-component to delegate token management
Expand Down Expand Up @@ -126,10 +128,16 @@ export class DescopeComponent implements OnInit, OnChanges {
this.webComponent.setAttribute('auto-focus', this.autoFocus.toString());
}
if (this.validateOnBlur) {
this.webComponent.setAttribute('validate-on-blur', this.autoFocus.toString());
this.webComponent.setAttribute(
'validate-on-blur',
this.autoFocus.toString()
);
}
if (this.restartOnError) {
this.webComponent.setAttribute('restart-on-error', this.autoFocus.toString());
this.webComponent.setAttribute(
'restart-on-error',
this.autoFocus.toString()
);
}
if (this.debug) {
this.webComponent.setAttribute('debug', this.debug.toString());
Expand Down
7 changes: 5 additions & 2 deletions packages/sdks/react-sdk/src/components/Descope.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ import { getGlobalSdk } from '../sdk';

// web-component code uses browser API, but can be used in SSR apps, hence the lazy loading
const DescopeWC = lazy(async () => {
const module = await import('@descope/web-component');
module.default.sdkConfigOverrides = {
const WebComponent: any =
customElements?.get('descope-wc') ||
(await import('@descope/web-component').then((module) => module.default));

WebComponent.sdkConfigOverrides = {
// Overrides the web-component's base headers to indicate usage via the React SDK
baseHeaders,
// Disables token persistence within the web-component to delegate token management
Expand Down
4 changes: 3 additions & 1 deletion packages/sdks/vue-sdk/src/Descope.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ import { computed } from 'vue';
import { getGlobalSdk } from './sdk';
import type { JWTResponse, ErrorResponse } from './types';

DescopeWcClass.sdkConfigOverrides = {
const WebComponent: any = customElements?.get('descope-wc') || DescopeWcClass;

WebComponent.sdkConfigOverrides = {
// Overrides the web-component's base headers to indicate usage via the React SDK
baseHeaders,
// Disables token persistence within the web-component to delegate token management
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ class BaseDescopeWc extends BaseClass {
}

#syncStateIdFromUrl() {
const { stepId, executionId } = getRunIdsFromUrl();
const { stepId, executionId } = getRunIdsFromUrl(this.flowId);
this.#flowState.update({ stepId, executionId });
}

Expand Down Expand Up @@ -517,7 +517,7 @@ class BaseDescopeWc extends BaseClass {
redirectAuthCodeChallenge,
redirectAuthInitiator,
ssoQueryParams,
} = handleUrlParams();
} = handleUrlParams(this.flowId, this.loggerWrapper);

// we want to update the state when user clicks on back in the browser
window.addEventListener('popstate', this.#eventsCbRefs.popstate);
Expand Down
35 changes: 30 additions & 5 deletions packages/sdks/web-component/src/lib/helpers/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ function resetUrlParam(paramName: string) {
}
}

const getFlowIdFromExecId = (executionId: string) => {
const regex = /(.*)\|#\|.*/;
return regex.exec(executionId)?.[1] || '';
};

export async function fetchContent<T extends 'text' | 'json'>(
url: string,
returnType: T,
Expand Down Expand Up @@ -109,10 +114,17 @@ export function getAnimationDirection(
return undefined;
}

export const getRunIdsFromUrl = () => {
const [executionId = '', stepId = ''] = (getFlowUrlParam() || '').split('_');
export const getRunIdsFromUrl = (flowId: string) => {
let [executionId = '', stepId = ''] = (getFlowUrlParam() || '').split('_');
const executionFlowId = getFlowIdFromExecId(executionId);

// if the flow id does not match, this execution id is not for this flow
if (!flowId || (executionFlowId && executionFlowId !== flowId)) {
executionId = '';
stepId = '';
}

return { executionId, stepId };
return { executionId, stepId, executionFlowId };
};

export const setRunIdsOnUrl = (executionId: string, stepId: string) => {
Expand Down Expand Up @@ -284,8 +296,21 @@ export const getElementDescopeAttributes = (ele: HTMLElement) =>
export const getFlowConfig = (config: Record<string, any>, flowId: string) =>
config?.flows?.[flowId] || {};

export const handleUrlParams = () => {
const { executionId, stepId } = getRunIdsFromUrl();
export const handleUrlParams = (
flowId: string,
logger: { debug: (...data: any[]) => void },
) => {
const { executionId, stepId, executionFlowId } = getRunIdsFromUrl(flowId);

// if the flow id does not match, we do not want to read & remove any query params
// because it's probably belongs to another flow
if (executionFlowId && flowId !== executionFlowId) {
logger.debug(
'Flow id does not match the execution flow id, skipping url params handling',
);
return { ssoQueryParams: {} };
}

if (executionId || stepId) {
clearRunIdsFromUrl();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const formMountMixin = createSingletonMixin(
if (this.#shouldMountInFormEle()) {
this.#handleOuterForm();
}
super['connectedCallback']?.();
super.connectedCallback?.();
}
},
);
35 changes: 33 additions & 2 deletions packages/sdks/web-component/test/helpers/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,40 @@ describe('helpers', () => {
writable: true,
value: new URL('http://localhost'),
});
window.location.search = `?${URL_RUN_IDS_PARAM_NAME}=8_9`;
expect(getRunIdsFromUrl()).toEqual({ executionId: '8', stepId: '9' });
window.location.search = `?${URL_RUN_IDS_PARAM_NAME}=8|#|a_9`;
expect(getRunIdsFromUrl('8')).toEqual({
executionFlowId: '8',
executionId: '8|#|a',
stepId: '9',
});
});

it('getRunIds should return the correct query param when there is no flow Id', () => {
Object.defineProperty(window, 'location', {
writable: true,
value: new URL('http://localhost'),
});
window.location.search = `?${URL_RUN_IDS_PARAM_NAME}=8|#|a_9`;
expect(getRunIdsFromUrl('')).toEqual({
executionFlowId: '8',
executionId: '',
stepId: '',
});
});

it('getRunIds should return the correct query param when exec id is wrong', () => {
Object.defineProperty(window, 'location', {
writable: true,
value: new URL('http://localhost'),
});
window.location.search = `?${URL_RUN_IDS_PARAM_NAME}=8`;
expect(getRunIdsFromUrl('8')).toEqual({
executionFlowId: '',
executionId: '8',
stepId: '',
});
});

it('setRunIds should pushstate new URL with query param', () => {
Object.defineProperty(window, 'location', {
writable: true,
Expand Down
2 changes: 1 addition & 1 deletion packages/widgets/user-profile-widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/descope/descope-js/issues",
"email": "[email protected]"
},
"main": "dist/index.js",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/index.d.ts",
"description": "Descope user profile widget",
Expand Down
24 changes: 15 additions & 9 deletions packages/widgets/user-profile-widget/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export default [
}),
typescript({
rootDir: './src/lib',
declarationDir: 'dist/dts',
declaration: true,
}),
commonjs(),
nodeResolve(),
Expand All @@ -55,16 +57,20 @@ export default [
},
{
input,
output: {
dir: 'dist/esm',
format: 'esm',
sourcemap: true,
},
output: [
{
dir: 'dist/esm',
format: 'esm',
sourcemap: true,
},
{
dir: 'dist/cjs',
format: 'cjs',
sourcemap: true,
},
],
plugins: [
typescript({
rootDir: './src/lib',
declarationDir: 'dist/esm/dts',
}),
typescript(),
define({
replacements: {
BUILD_VERSION: JSON.stringify(packageJson.version),
Expand Down
8 changes: 7 additions & 1 deletion packages/widgets/user-profile-widget/src/lib/widget/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { compose } from '@descope/sdk-helpers';
import WebComponent from '@descope/web-component';
import { initMixin } from './mixins/initMixin/initMixin';

declare const BUILD_VERSION: string;
Expand All @@ -8,6 +7,13 @@ const rootMixin = (superclass: CustomElementConstructor) =>
class RootMixinClass extends initMixin(superclass) {
async init() {
await super.init?.();

const WebComponent: any =
customElements.get('descope-wc') ||
(await import('@descope/web-component').then(
(module) => module.default,
));

WebComponent.sdkConfigOverrides = {
baseHeaders: {
'x-descope-widget-type': 'user-profile-widget',
Expand Down
3 changes: 1 addition & 2 deletions packages/widgets/user-profile-widget/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"declaration": true,
"declarationDir": "dist/dts",
"declaration": false,
"noErrorTruncation": true,
"typeRoots": ["./node_modules/@types"]
},
Expand Down
Loading

0 comments on commit c4182b3

Please sign in to comment.