Repository navigation
Expand file tree
/
Copy pathexecuteEmbeddedSelfServiceFlow.ts
More file actions
148 lines (135 loc) · 4.78 KB
/
Copy pathexecuteEmbeddedSelfServiceFlow.ts
File metadata and controls
148 lines (135 loc) · 4.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2026 The ThunderID Authors
// SPDX-License-Identifier: Apache-2.0
import ThunderIDAPIError from '../../errors/ThunderIDAPIError';
import {EmbeddedSelfServiceFlowRequest, EmbeddedSelfServiceFlowResponse} from '../../models/embedded-self-service-flow';
/**
* Configuration for the executeEmbeddedSelfServiceFlow request.
*/
export interface ExecuteEmbeddedSelfServiceFlowConfig extends Omit<RequestInit, 'method' | 'body'> {
/**
* The base path of the API endpoint.
*/
baseUrl?: string;
/**
* Optional custom fetcher function. If not provided, native fetch is used, which sends no
* access token; framework SDKs pass a fetcher that attaches the signed-in user's token.
*/
fetcher?: (url: string, config: RequestInit) => Promise<Response>;
/**
* The step to execute: an initiating request or a continuation.
*/
payload: EmbeddedSelfServiceFlowRequest;
/**
* The absolute flow execution endpoint.
*/
url?: string;
}
/**
* Executes one step of a self-service flow, an operation the signed-in user performs on their own
* account, such as verifying their email address. A new execution names the flow by its handle; the
* server takes the user from the access token on that first request and keeps it for the execution.
*
* @param config - Configuration object with URL, payload, fetcher and optional request config.
* @returns A promise that resolves with the flow step.
* @throws ThunderIDAPIError when the input is invalid or the request fails, coded
* `executeEmbeddedSelfServiceFlow-` plus `ValidationError-001` (invalid `url` or `baseUrl`), `ValidationError-002`
* (no payload), `ValidationError-003` (a new flow without a handle), `ResponseError-001` (the server refused, with
* its status) or `NetworkError-001` (no response, or an unreadable one).
*
* @example
* ```typescript
* const step = await executeEmbeddedSelfServiceFlow({
* baseUrl: 'https://localhost:8090',
* payload: {
* flowType: 'SELF_SERVICE',
* handle: 'default-self-attribute-verification-flow',
* inputs: {attribute: 'email'},
* },
* fetcher: authenticatedFetcher,
* });
*
* await executeEmbeddedSelfServiceFlow({
* baseUrl: 'https://localhost:8090',
* payload: {executionId: step.executionId, action: 'verify', inputs: {otp: '123456'}},
* fetcher: authenticatedFetcher,
* });
* ```
*/
const executeEmbeddedSelfServiceFlow = async ({
url,
baseUrl,
payload,
fetcher,
...requestConfig
}: ExecuteEmbeddedSelfServiceFlowConfig): Promise<EmbeddedSelfServiceFlowResponse> => {
try {
// eslint-disable-next-line no-new
new URL((url ?? baseUrl)!);
} catch (error) {
throw new ThunderIDAPIError(
`Invalid URL provided. ${error instanceof Error ? error.message : String(error)}`,
'executeEmbeddedSelfServiceFlow-ValidationError-001',
'javascript',
400,
'The provided `url` or `baseUrl` path does not adhere to the URL schema.',
);
}
if (!payload) {
throw new ThunderIDAPIError(
'Self-service flow payload is required',
'executeEmbeddedSelfServiceFlow-ValidationError-002',
'javascript',
400,
'If a self-service flow payload is not provided, the request cannot be constructed correctly.',
);
}
if (!payload.executionId && !payload.handle) {
throw new ThunderIDAPIError(
'The flow handle is required',
'executeEmbeddedSelfServiceFlow-ValidationError-003',
'javascript',
400,
'A new self-service flow must name the flow to start by its handle.',
);
}
const fetchFn: typeof fetch = fetcher ?? fetch;
const resolvedUrl: string = url ?? `${baseUrl}/flow/execute`;
const requestInit: RequestInit = {
...requestConfig,
// Every step is rendered from `meta`, so always ask for it.
body: JSON.stringify({...payload, verbose: true}),
headers: {
...requestConfig.headers,
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
};
try {
const response: Response = await fetchFn(resolvedUrl, requestInit);
if (!response?.ok) {
const errorText: string = await response.text();
throw new ThunderIDAPIError(
errorText,
'executeEmbeddedSelfServiceFlow-ResponseError-001',
'javascript',
response.status,
response.statusText,
'Self-service flow request failed',
);
}
return (await response.json()) as EmbeddedSelfServiceFlowResponse;
} catch (error) {
if (error instanceof ThunderIDAPIError) {
throw error;
}
throw new ThunderIDAPIError(
`Network or parsing error: ${error instanceof Error ? error.message : 'Unknown error'}`,
'executeEmbeddedSelfServiceFlow-NetworkError-001',
'javascript',
0,
'Network Error',
);
}
};
export default executeEmbeddedSelfServiceFlow;