Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/lovely-regions-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@asgardeo/javascript': minor
'@asgardeo/react': minor
---

Add user onboarding components for v2
176 changes: 176 additions & 0 deletions packages/javascript/src/api/v2/executeEmbeddedUserOnboardingFlowV2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/**
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { EmbeddedFlowExecuteRequestConfig as EmbeddedFlowExecuteRequestConfigV2 } from '../../models/v2/embedded-flow-v2';
import { EmbeddedFlowType } from '../../models/embedded-flow';
import AsgardeoAPIError from '../../errors/AsgardeoAPIError';

/**
* Response from the user onboarding flow execution.
*/
export interface EmbeddedUserOnboardingFlowResponse {
/**
* Unique identifier for the flow execution.
*/
flowId: string;

/**
* Current status of the flow.
*/
flowStatus: 'INCOMPLETE' | 'COMPLETE' | 'ERROR';

/**
* Type of the current step in the flow.
*/
type?: 'VIEW' | 'REDIRECTION';

/**
* Data for the current step including components and additional data.
*/
data?: {
/**
* UI components to render for the current step.
*/
components?: any[];

/**
* Additional data from the flow step (e.g., inviteLink).
*/
additionalData?: Record<string, string>;
};

/**
* Reason for failure if flowStatus is ERROR.
*/
failureReason?: string;
}

/**
* Executes an embedded user onboarding flow by sending a request to the flow execution endpoint.
*
* This function handles both:
* - Admin flow: Initiates onboarding, collects user details, generates invite link
* - End-user flow: Validates invite token and allows password setting
*
* @param requestConfig - Request configuration object containing URL, payload, and optional auth token.
* @returns A promise that resolves with the flow execution response.
* @throws AsgardeoAPIError when the request fails or URL is invalid.
*
* @example
* ```typescript
* // Admin initiating user onboarding (requires auth token)
* const response = await executeEmbeddedUserOnboardingFlowV2({
* baseUrl: "https://api.thunder.io",
* payload: {
* flowType: "USER_ONBOARDING"
* },
* headers: {
* Authorization: `Bearer ${accessToken}`
* }
* });
*
* // End-user accepting invite (no auth required)
* const response = await executeEmbeddedUserOnboardingFlowV2({
* baseUrl: "https://api.thunder.io",
* payload: {
* flowId: "flow-id-from-url",
* inputs: { inviteToken: "token-from-url" }
* }
* });
* ```
*/
const executeEmbeddedUserOnboardingFlowV2 = async ({
url,
baseUrl,
payload,
...requestConfig
}: EmbeddedFlowExecuteRequestConfigV2): Promise<EmbeddedUserOnboardingFlowResponse> => {
if (!payload) {
throw new AsgardeoAPIError(
'User onboarding payload is required',
'executeEmbeddedUserOnboardingFlow-ValidationError-002',
'javascript',
400,
'If a user onboarding payload is not provided, the request cannot be constructed correctly.',
);
}

const endpoint: string = url ?? `${baseUrl}/flow/execute`;

// Strip any user-provided 'verbose' parameter as it should only be used internally
const cleanPayload: typeof payload =
typeof payload === 'object' && payload !== null
? Object.fromEntries(Object.entries(payload).filter(([key]) => key !== 'verbose'))
: payload;

// `verbose: true` is required to get the `meta` field in the response that includes component details.
// Add verbose:true for initial requests or flow continuation without inputs
const hasOnlyFlowType: boolean =
typeof cleanPayload === 'object' &&
cleanPayload !== null &&
'flowType' in cleanPayload &&
Object.keys(cleanPayload).length === 1;
const hasOnlyFlowId: boolean =
typeof cleanPayload === 'object' &&
cleanPayload !== null &&
'flowId' in cleanPayload &&
Object.keys(cleanPayload).length === 1;
const hasFlowIdWithInputs: boolean =
typeof cleanPayload === 'object' &&
cleanPayload !== null &&
'flowId' in cleanPayload &&
'inputs' in cleanPayload;

// Add verbose for initial requests and when continuing with inputs
const requestPayload: Record<string, unknown> =
hasOnlyFlowType || hasOnlyFlowId || hasFlowIdWithInputs ? { ...cleanPayload, verbose: true } : cleanPayload;

// Ensure flowType is USER_ONBOARDING for initial requests
if ('flowType' in requestPayload && requestPayload['flowType'] !== EmbeddedFlowType.UserOnboarding) {
requestPayload['flowType'] = EmbeddedFlowType.UserOnboarding;
}

const response: Response = await fetch(endpoint, {
...requestConfig,
method: requestConfig.method || 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
...requestConfig.headers,
},
body: JSON.stringify(requestPayload),
});

if (!response.ok) {
const errorText = await response.text();

throw new AsgardeoAPIError(
`User onboarding request failed: ${errorText}`,
'executeEmbeddedUserOnboardingFlow-ResponseError-001',
'javascript',
response.status,
response.statusText,
);
}

const flowResponse: EmbeddedUserOnboardingFlowResponse = await response.json();

return flowResponse;
};

export default executeEmbeddedUserOnboardingFlowV2;
4 changes: 4 additions & 0 deletions packages/javascript/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export {default as updateMeProfile, UpdateMeProfileConfig} from './api/updateMeP
export {default as getBrandingPreference, GetBrandingPreferenceConfig} from './api/getBrandingPreference';
export {default as executeEmbeddedSignInFlowV2} from './api/v2/executeEmbeddedSignInFlowV2';
export {default as executeEmbeddedSignUpFlowV2} from './api/v2/executeEmbeddedSignUpFlowV2';
export {
default as executeEmbeddedUserOnboardingFlowV2,
EmbeddedUserOnboardingFlowResponse,
} from './api/v2/executeEmbeddedUserOnboardingFlowV2';

export {default as ApplicationNativeAuthenticationConstants} from './constants/ApplicationNativeAuthenticationConstants';
export {default as TokenConstants} from './constants/TokenConstants';
Expand Down
1 change: 1 addition & 0 deletions packages/javascript/src/models/embedded-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
export enum EmbeddedFlowType {
Authentication = 'AUTHENTICATION',
Registration = 'REGISTRATION',
UserOnboarding = 'USER_ONBOARDING',
}

export interface EmbeddedFlowExecuteRequestPayload {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

// v2 exports (current)
export { default as AcceptInvite } from './v2/AcceptInvite';
export type { AcceptInviteProps, AcceptInviteRenderProps } from './v2/AcceptInvite';
export { default as BaseAcceptInvite } from './v2/BaseAcceptInvite';
export type {
BaseAcceptInviteProps,
BaseAcceptInviteRenderProps,
AcceptInviteFlowResponse,
} from './v2/BaseAcceptInvite';
Loading
Loading