Skip to content

Commit 6c2acd6

Browse files
committed
onboard recovery flow
1 parent b7e2818 commit 6c2acd6

19 files changed

Lines changed: 1983 additions & 11 deletions

packages/javascript/src/AsgardeoJavaScriptClient.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
2+
* Copyright (c) 2025-2026, WSO2 LLC. (https://www.wso2.com).
33
*
44
* WSO2 LLC. licenses this file to you under the Apache License,
55
* Version 2.0 (the "License"); you may not use this file except
@@ -162,6 +162,10 @@ class AsgardeoJavaScriptClient<T = Config> implements AsgardeoClient<T> {
162162
throw new Error('Method not implemented.');
163163
}
164164

165+
recover(_payload: EmbeddedFlowExecuteRequestPayload): Promise<EmbeddedFlowExecuteResponse> {
166+
throw new Error('Method not implemented.');
167+
}
168+
165169
signUp(options?: SignUpOptions): Promise<void>;
166170

167171
signUp(payload: EmbeddedFlowExecuteRequestPayload): Promise<EmbeddedFlowExecuteResponse>;
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
import AsgardeoAPIError from '../../errors/AsgardeoAPIError';
20+
import {EmbeddedFlowExecuteRequestConfig as EmbeddedFlowExecuteRequestConfigV2} from '../../models/v2/embedded-flow-v2';
21+
import {EmbeddedRecoveryFlowResponse} from '../../models/v2/embedded-recovery-flow-v2';
22+
23+
/**
24+
* Executes an embedded recovery flow by sending a request to the flow execution endpoint.
25+
*
26+
* This function handles password-recovery and account-recovery flows driven by the
27+
* Asgardeo server. The server returns UI components for each step (e.g. username
28+
* collection, OTP verification, password reset) and this function forwards the
29+
* user's responses back to the server.
30+
*
31+
* @param requestConfig - Request configuration containing URL, payload, and optional headers.
32+
* @returns A promise that resolves with the flow execution response.
33+
* @throws AsgardeoAPIError when the request fails or a payload is missing.
34+
*
35+
* @example
36+
* ```typescript
37+
* // Initiate recovery flow
38+
* const response = await executeEmbeddedRecoveryFlowV2({
39+
* baseUrl: 'https://api.asgardeo.io/t/myorg',
40+
* payload: {
41+
* flowType: 'RECOVERY',
42+
* applicationId: 'my-app-id',
43+
* },
44+
* });
45+
*
46+
* // Continue recovery flow with user input
47+
* const nextResponse = await executeEmbeddedRecoveryFlowV2({
48+
* baseUrl: 'https://api.asgardeo.io/t/myorg',
49+
* payload: {
50+
* executionId: response.executionId,
51+
* action: 'submit',
52+
* inputs: { username: 'user@example.com' },
53+
* challengeToken: response.challengeToken,
54+
* },
55+
* });
56+
* ```
57+
*/
58+
const executeEmbeddedRecoveryFlowV2 = async ({
59+
url,
60+
baseUrl,
61+
payload,
62+
...requestConfig
63+
}: EmbeddedFlowExecuteRequestConfigV2): Promise<EmbeddedRecoveryFlowResponse> => {
64+
if (!payload) {
65+
throw new AsgardeoAPIError(
66+
'Recovery payload is required',
67+
'executeEmbeddedRecoveryFlow-ValidationError-002',
68+
'javascript',
69+
400,
70+
'If a recovery payload is not provided, the request cannot be constructed correctly.',
71+
);
72+
}
73+
74+
const endpoint: string = url ?? `${baseUrl}/flow/execute`;
75+
76+
// Strip any user-provided 'verbose' parameter as it should only be used internally
77+
const cleanPayload: typeof payload =
78+
typeof payload === 'object' && payload !== null
79+
? Object.fromEntries(Object.entries(payload).filter(([key]: [string, unknown]) => key !== 'verbose'))
80+
: payload;
81+
82+
// `verbose: true` is required to get the `meta` field in the response that includes component details.
83+
// Add verbose:true if:
84+
// 1. payload contains only applicationId and flowType (initial request)
85+
// 2. payload contains only executionId (flow continuation without inputs)
86+
const hasOnlyAppIdAndFlowType: boolean =
87+
typeof cleanPayload === 'object' &&
88+
cleanPayload !== null &&
89+
'applicationId' in cleanPayload &&
90+
'flowType' in cleanPayload &&
91+
Object.keys(cleanPayload).length === 2;
92+
const hasOnlyFlowId: boolean =
93+
typeof cleanPayload === 'object' &&
94+
cleanPayload !== null &&
95+
'executionId' in cleanPayload &&
96+
Object.keys(cleanPayload).length === 1;
97+
98+
const requestPayload: Record<string, unknown> =
99+
hasOnlyAppIdAndFlowType || hasOnlyFlowId
100+
? {
101+
...cleanPayload,
102+
verbose: true,
103+
}
104+
: cleanPayload;
105+
106+
const response: Response = await fetch(endpoint, {
107+
...requestConfig,
108+
body: JSON.stringify(requestPayload),
109+
headers: {
110+
Accept: 'application/json',
111+
'Content-Type': 'application/json',
112+
...requestConfig.headers,
113+
},
114+
method: requestConfig.method || 'POST',
115+
});
116+
117+
if (!response.ok) {
118+
const errorText: string = await response.text();
119+
120+
throw new AsgardeoAPIError(
121+
`Recovery request failed: ${errorText}`,
122+
'executeEmbeddedRecoveryFlow-ResponseError-001',
123+
'javascript',
124+
response.status,
125+
response.statusText,
126+
);
127+
}
128+
129+
const flowResponse: EmbeddedRecoveryFlowResponse = await response.json();
130+
131+
return flowResponse;
132+
};
133+
134+
export default executeEmbeddedRecoveryFlowV2;

packages/javascript/src/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright (c) 2020, WSO2 LLC. (https://www.wso2.com). All Rights Reserved.
2+
* Copyright (c) 2020-2026, WSO2 LLC. (https://www.wso2.com). All Rights Reserved.
33
*
44
* WSO2 LLC. licenses this file to you under the Apache License,
55
* Version 2.0 (the "License"); you may not use this file except
@@ -47,6 +47,7 @@ export {default as updateMeProfile, UpdateMeProfileConfig} from './api/updateMeP
4747
export {default as getBrandingPreference, GetBrandingPreferenceConfig} from './api/getBrandingPreference';
4848
export {default as executeEmbeddedSignInFlowV2} from './api/v2/executeEmbeddedSignInFlowV2';
4949
export {default as executeEmbeddedSignUpFlowV2} from './api/v2/executeEmbeddedSignUpFlowV2';
50+
export {default as executeEmbeddedRecoveryFlowV2} from './api/v2/executeEmbeddedRecoveryFlowV2';
5051
export {
5152
default as executeEmbeddedUserOnboardingFlowV2,
5253
EmbeddedUserOnboardingFlowResponse,
@@ -112,6 +113,14 @@ export {
112113
EmbeddedSignUpFlowRequest as EmbeddedSignUpFlowRequestV2,
113114
EmbeddedSignUpFlowErrorResponse as EmbeddedSignUpFlowErrorResponseV2,
114115
} from './models/v2/embedded-signup-flow-v2';
116+
export {
117+
EmbeddedRecoveryFlowStatus as EmbeddedRecoveryFlowStatusV2,
118+
EmbeddedRecoveryFlowType as EmbeddedRecoveryFlowTypeV2,
119+
EmbeddedRecoveryFlowResponse as EmbeddedRecoveryFlowResponseV2,
120+
EmbeddedRecoveryFlowInitiateRequest as EmbeddedRecoveryFlowInitiateRequestV2,
121+
EmbeddedRecoveryFlowRequest as EmbeddedRecoveryFlowRequestV2,
122+
EmbeddedRecoveryFlowErrorResponse as EmbeddedRecoveryFlowErrorResponseV2,
123+
} from './models/v2/embedded-recovery-flow-v2';
115124
export {
116125
OrganizationUnit,
117126
OrganizationUnitListResponse,

packages/javascript/src/models/client.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
2+
* Copyright (c) 2025-2026, WSO2 LLC. (https://www.wso2.com).
33
*
44
* WSO2 LLC. licenses this file to you under the Apache License,
55
* Version 2.0 (the "License"); you may not use this file except
@@ -143,6 +143,14 @@ export interface AsgardeoClient<T> {
143143
*/
144144
reInitialize(config: Partial<T>): Promise<boolean>;
145145

146+
/**
147+
* Initiates an embedded recovery flow for the user (e.g. password reset).
148+
*
149+
* @param payload - The payload containing the necessary information to execute the embedded recovery flow.
150+
* @returns A promise that resolves to an EmbeddedFlowExecuteResponse containing the flow execution details.
151+
*/
152+
recover(payload: EmbeddedFlowExecuteRequestPayload): Promise<EmbeddedFlowExecuteResponse>;
153+
146154
/**
147155
* Sets the session data for the specified session ID.
148156
* @param sessionData - The session data to be set.

packages/javascript/src/models/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
*/
1818

1919
import {I18nBundle} from '@asgardeo/i18n';
20-
import {ComponentsExtensions} from './v2/extensions/components';
2120
import {Platform} from './platforms';
2221
import {RecursivePartial} from './utility-types';
22+
import {ComponentsExtensions} from './v2/extensions/components';
2323
import {ThemeConfig, ThemeMode} from '../theme/types';
2424

2525
/**

packages/javascript/src/models/embedded-flow.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com).
2+
* Copyright (c) 2025-2026, WSO2 LLC. (https://www.wso2.com).
33
*
44
* WSO2 LLC. licenses this file to you under the Apache License,
55
* Version 2.0 (the "License"); you may not use this file except
@@ -18,6 +18,7 @@
1818

1919
export enum EmbeddedFlowType {
2020
Authentication = 'AUTHENTICATION',
21+
Recovery = 'RECOVERY',
2122
Registration = 'REGISTRATION',
2223
UserOnboarding = 'USER_ONBOARDING',
2324
}
@@ -166,7 +167,7 @@ export interface EmbeddedFlowExecuteErrorResponse {
166167
* Currently only supports 'REGISTRATION' but may be extended to
167168
* include other flow types (e.g., 'LOGIN', 'PASSWORD_RESET') in the future.
168169
*/
169-
flowType: 'REGISTRATION';
170+
flowType: 'REGISTRATION' | 'RECOVERY';
170171

171172
/**
172173
* Brief error message describing what went wrong.

0 commit comments

Comments
 (0)