@thunderid/react 1.11.2 → 1.12.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.
@@ -3,6 +3,8 @@ const require_useThunderID = require('../../../contexts/ThunderID/useThunderID.c
3
3
  const require_useTranslation = require('../../../hooks/useTranslation.cjs');
4
4
  const require_flowTransformer = require('../../../utils/flowTransformer.cjs');
5
5
  const require_BaseDeviceGrant = require('./BaseDeviceGrant.cjs');
6
+ const require_useOAuthCallback = require('../../../hooks/auth/useOAuthCallback.cjs');
7
+ const require_oauth = require('../../../utils/oauth.cjs');
6
8
  let __thunderid_browser = require("@thunderid/browser");
7
9
  __thunderid_browser = require_rolldown_runtime.__toESM(__thunderid_browser);
8
10
  let react = require("react");
@@ -11,6 +13,10 @@ let react_jsx_runtime = require("react/jsx-runtime");
11
13
  react_jsx_runtime = require_rolldown_runtime.__toESM(react_jsx_runtime);
12
14
 
13
15
  //#region src/components/auth/DeviceGrant/DeviceGrant.tsx
16
+ const toDeviceGrantError = (cause, fallbackMessage) => {
17
+ if (cause instanceof __thunderid_browser.ThunderIDAPIError || cause instanceof __thunderid_browser.ThunderIDRuntimeError) return cause;
18
+ return new __thunderid_browser.ThunderIDRuntimeError(cause instanceof Error ? cause.message : fallbackMessage, "DEVICE_GRANT_ERROR", "react", cause);
19
+ };
14
20
  const clearVerificationParameters = () => {
15
21
  if (!window.location.href) return;
16
22
  const url = new URL(window.location.href);
@@ -19,11 +25,51 @@ const clearVerificationParameters = () => {
19
25
  window.history.replaceState(window.history.state, "", url.toString());
20
26
  };
21
27
  const hasUserCodeInput = (components) => components.some((component) => component.ref === "user_code" || Array.isArray(component.components) && hasUserCodeInput(component.components));
28
+ const findAction = (components, actionId) => {
29
+ for (const component of components) {
30
+ if (component.id === actionId) return component;
31
+ if (component.components) {
32
+ const nestedAction = findAction(component.components, actionId);
33
+ if (nestedAction) return nestedAction;
34
+ }
35
+ }
36
+ };
37
+ const parseConsentPurposes = (value) => {
38
+ let parsed = value;
39
+ if (typeof parsed === "string") try {
40
+ parsed = JSON.parse(parsed);
41
+ } catch {
42
+ return null;
43
+ }
44
+ if (Array.isArray(parsed)) return parsed;
45
+ if (typeof parsed === "object" && parsed !== null && "purposes" in parsed && Array.isArray(parsed.purposes)) return parsed.purposes;
46
+ return null;
47
+ };
48
+ const CONSENT_REASON_KEY = "__consent_reason__";
49
+ const flattenComponents = (items) => items.flatMap((component) => [component, ...flattenComponents(component.components ?? [])]);
50
+ const findConsentSubmitActionId = (items) => {
51
+ const actions = flattenComponents(items).filter((component) => component.id && component.eventType?.toUpperCase() === "SUBMIT");
52
+ return (actions.find((component) => component.variant?.toLowerCase() !== "primary") ?? actions[0])?.id;
53
+ };
54
+ const compileConsentInputs = (inputs, actionId, components, additionalData) => {
55
+ const action = actionId ? findAction(components, actionId) : void 0;
56
+ const purposes = parseConsentPurposes(additionalData["consentPrompt"]);
57
+ const consentReason = inputs[CONSENT_REASON_KEY];
58
+ const cleanInputs = { ...inputs };
59
+ delete cleanInputs[CONSENT_REASON_KEY];
60
+ const isSubmitAction = Boolean(action && action.eventType?.toUpperCase() !== "TRIGGER");
61
+ const isTimeout = consentReason === __thunderid_browser.ConsentConstants.REASON_TIMEOUT;
62
+ const isDeny = isTimeout || Boolean(action && isSubmitAction && action.variant?.toLowerCase() !== "primary");
63
+ return (0, __thunderid_browser.compileConsentDecisions)(cleanInputs, isSubmitAction ? purposes : null, {
64
+ approved: !isDeny,
65
+ ...isTimeout ? { reason: __thunderid_browser.ConsentConstants.REASON_TIMEOUT } : isDeny ? { reason: __thunderid_browser.ConsentConstants.REASON_USER_DENIED } : {}
66
+ });
67
+ };
22
68
  /**
23
69
  * Runs the application's configured Device Grant flow and exposes its state for custom renderers.
24
70
  */
25
71
  const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, onError = void 0, preferences = {}, size = "medium", variant = "default",...baseProps }) => {
26
- const { applicationId, baseUrl, meta } = require_useThunderID.default();
72
+ const { applicationId, baseUrl, getStorageManager, isInitialized, isLoading: sdkLoading, meta, vendor } = require_useThunderID.default();
27
73
  const { t } = require_useTranslation.default(preferences?.i18n);
28
74
  const [components, setComponents] = (0, react.useState)([]);
29
75
  const [additionalData, setAdditionalData] = (0, react.useState)({});
@@ -31,14 +77,56 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
31
77
  const [serverFieldErrors, setServerFieldErrors] = (0, react.useState)(null);
32
78
  const [executionId, setExecutionId] = (0, react.useState)("");
33
79
  const [challengeToken, setChallengeToken] = (0, react.useState)("");
80
+ const [isStorageReady, setIsStorageReady] = (0, react.useState)(false);
81
+ const challengeTokenRef = (0, react.useRef)("");
34
82
  const [error, setError] = (0, react.useState)(null);
35
83
  const [isLoading, setIsLoading] = (0, react.useState)(() => {
84
+ if (typeof window !== "undefined" && [
85
+ "code",
86
+ "state",
87
+ "error"
88
+ ].some((parameter) => new URL(window.location.href).searchParams.has(parameter))) return false;
36
89
  if (applicationId) return true;
37
90
  return typeof window !== "undefined" && Boolean(new URL(window.location.href).searchParams.get("applicationId"));
38
91
  });
39
92
  const [result, setResult] = (0, react.useState)(null);
93
+ const [isTimeoutDisabled, setIsTimeoutDisabled] = (0, react.useState)(false);
40
94
  const flowStartAttempted = (0, react.useRef)(false);
41
95
  const verificationParametersRead = (0, react.useRef)(false);
96
+ const oauthCodeProcessedRef = (0, react.useRef)(false);
97
+ const executionIdStorageKey = `${vendor}_device_grant_execution_id`;
98
+ (0, react.useEffect)(() => {
99
+ if (!isInitialized || sdkLoading) return;
100
+ (async () => {
101
+ try {
102
+ const storedChallengeToken = (await (await getStorageManager?.())?.getTemporaryData())?.["challengeToken"];
103
+ if (typeof storedChallengeToken === "string") {
104
+ challengeTokenRef.current = storedChallengeToken;
105
+ setChallengeToken(storedChallengeToken);
106
+ }
107
+ } catch {} finally {
108
+ setIsStorageReady(true);
109
+ }
110
+ })();
111
+ }, [
112
+ getStorageManager,
113
+ isInitialized,
114
+ sdkLoading
115
+ ]);
116
+ const persistExecutionId = (0, react.useCallback)((id) => {
117
+ setExecutionId(id ?? "");
118
+ if (id) sessionStorage.setItem(executionIdStorageKey, id);
119
+ else sessionStorage.removeItem(executionIdStorageKey);
120
+ }, [executionIdStorageKey]);
121
+ const persistChallengeToken = (0, react.useCallback)(async (token) => {
122
+ challengeTokenRef.current = token ?? "";
123
+ setChallengeToken(token ?? "");
124
+ try {
125
+ const storageManager = await getStorageManager();
126
+ if (storageManager) if (token) await storageManager.setTemporaryDataParameter("challengeToken", token);
127
+ else await storageManager.removeTemporaryDataParameter("challengeToken");
128
+ } catch {}
129
+ }, [getStorageManager]);
42
130
  const handleError = (0, react.useCallback)((flowError) => {
43
131
  setComponents([]);
44
132
  setAdditionalData({});
@@ -47,15 +135,23 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
47
135
  setResult("failure");
48
136
  onError?.(flowError);
49
137
  }, [onError]);
50
- const applyResponse = (0, react.useCallback)((response) => {
138
+ const applyResponse = (0, react.useCallback)(async (response) => {
139
+ const redirectURL = response.data?.["redirectURL"] ?? response.redirectUrl;
140
+ if (response.type === __thunderid_browser.EmbeddedSignInFlowType.Redirection && typeof redirectURL === "string") {
141
+ persistExecutionId(response.executionId ?? null);
142
+ await persistChallengeToken(response.challengeToken ?? null);
143
+ require_oauth.initiateOAuthRedirect(redirectURL, vendor);
144
+ return;
145
+ }
51
146
  const normalized = require_flowTransformer.normalizeFlowResponse(response, t, {
52
147
  throwOnError: false,
53
148
  resolveTranslations: false
54
149
  }, meta);
55
- setExecutionId(response.executionId);
56
- setChallengeToken(response.challengeToken ?? "");
150
+ persistExecutionId(response.flowStatus === __thunderid_browser.EmbeddedSignInFlowStatus.Incomplete ? response.executionId ?? null : null);
151
+ await persistChallengeToken(response.flowStatus === __thunderid_browser.EmbeddedSignInFlowStatus.Incomplete ? response.challengeToken ?? null : null);
57
152
  setComponents(normalized.components);
58
153
  setAdditionalData(normalized.additionalData);
154
+ setIsTimeoutDisabled(false);
59
155
  setServerFieldErrors(response.data?.fieldErrors ?? null);
60
156
  setError(response.error ? new Error(require_flowTransformer.extractErrorMessage(response, t)) : null);
61
157
  if (response.flowStatus === __thunderid_browser.EmbeddedSignInFlowStatus.Complete) {
@@ -64,7 +160,7 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
64
160
  return;
65
161
  }
66
162
  if (response.flowStatus === __thunderid_browser.EmbeddedSignInFlowStatus.Error) {
67
- const flowError = new Error(require_flowTransformer.extractErrorMessage(response, t, "deviceGrant.messages.failed"));
163
+ const flowError = new __thunderid_browser.ThunderIDRuntimeError(require_flowTransformer.extractErrorMessage(response, t, "deviceGrant.messages.failed"), "DEVICE_GRANT_ERROR", "react");
68
164
  setError(flowError);
69
165
  setResult("failure");
70
166
  onError?.(flowError);
@@ -75,24 +171,41 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
75
171
  meta,
76
172
  onComplete,
77
173
  onError,
78
- t
174
+ persistChallengeToken,
175
+ persistExecutionId,
176
+ t,
177
+ vendor
79
178
  ]);
80
- const startFlow = (0, react.useCallback)(async (appId) => {
179
+ const startFlow = (0, react.useCallback)(async (appId, resumeId) => {
81
180
  setIsLoading(true);
82
181
  setError(null);
83
182
  try {
84
- const response = await (0, __thunderid_browser.executeEmbeddedDeviceGrantFlow)({
85
- baseUrl,
86
- payload: {
87
- applicationId: appId,
88
- flowType: __thunderid_browser.EmbeddedFlowType.DeviceGrant,
89
- inputs: {}
183
+ const challengeFromStorage = challengeTokenRef.current;
184
+ let response;
185
+ try {
186
+ response = await (0, __thunderid_browser.executeEmbeddedDeviceGrantFlow)({
187
+ baseUrl,
188
+ payload: resumeId ? {
189
+ executionId: resumeId,
190
+ ...challengeFromStorage ? { challengeToken: challengeFromStorage } : {}
191
+ } : {
192
+ applicationId: appId,
193
+ flowType: __thunderid_browser.EmbeddedFlowType.DeviceGrant,
194
+ inputs: {}
195
+ }
196
+ });
197
+ } catch (resumeError) {
198
+ if (resumeId && resumeError instanceof __thunderid_browser.ThunderIDAPIError && resumeError.statusCode === 400) {
199
+ persistExecutionId(null);
200
+ await persistChallengeToken(null);
201
+ throw new __thunderid_browser.ThunderIDRuntimeError(t("deviceGrant.errors.continue"), "DEVICE_GRANT_ERROR", "react");
90
202
  }
91
- });
92
- clearVerificationParameters();
93
- applyResponse(response);
203
+ throw resumeError;
204
+ }
205
+ if (!resumeId) clearVerificationParameters();
206
+ await applyResponse(response);
94
207
  } catch (flowError) {
95
- handleError(flowError instanceof Error ? flowError : new Error(t("deviceGrant.errors.continue")));
208
+ handleError(toDeviceGrantError(flowError, t("deviceGrant.errors.continue")));
96
209
  } finally {
97
210
  setIsLoading(false);
98
211
  }
@@ -100,6 +213,8 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
100
213
  applyResponse,
101
214
  baseUrl,
102
215
  handleError,
216
+ persistChallengeToken,
217
+ persistExecutionId,
103
218
  t
104
219
  ]);
105
220
  (0, react.useEffect)(() => {
@@ -111,17 +226,24 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
111
226
  }
112
227
  const applicationIdFromUrl = url.searchParams.get("applicationId");
113
228
  const appId = applicationIdFromUrl?.trim() ? applicationIdFromUrl : applicationId;
229
+ const storedExecutionId = sessionStorage.getItem(executionIdStorageKey);
230
+ const hasOAuthCallback = url.searchParams.has("code") || url.searchParams.has("state") || url.searchParams.has("error");
231
+ if (!isInitialized || sdkLoading || !isStorageReady || hasOAuthCallback || oauthCodeProcessedRef.current) return;
114
232
  if (flowStartAttempted.current) return;
115
- if (!appId) {
233
+ if (!appId && !storedExecutionId) {
116
234
  flowStartAttempted.current = true;
117
- handleError(new Error(t("deviceGrant.errors.start")));
235
+ handleError(new __thunderid_browser.ThunderIDRuntimeError(t("deviceGrant.errors.start"), "DEVICE_GRANT_ERROR", "react"));
118
236
  return;
119
237
  }
120
238
  flowStartAttempted.current = true;
121
- startFlow(appId);
239
+ startFlow(appId ?? "", storedExecutionId ?? void 0);
122
240
  }, [
123
241
  applicationId,
242
+ executionIdStorageKey,
124
243
  handleError,
244
+ isInitialized,
245
+ isStorageReady,
246
+ sdkLoading,
125
247
  startFlow,
126
248
  t
127
249
  ]);
@@ -131,24 +253,92 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
131
253
  setResult(null);
132
254
  try {
133
255
  if (!executionId) return;
134
- applyResponse(await (0, __thunderid_browser.executeEmbeddedDeviceGrantFlow)({
256
+ const action = payload.action ?? actionId;
257
+ const inputs = compileConsentInputs(payload.inputs ?? {}, action, components, additionalData);
258
+ if (!hasUserCodeInput(components)) inputs["user_code"] = void 0;
259
+ await applyResponse(await (0, __thunderid_browser.executeEmbeddedDeviceGrantFlow)({
135
260
  baseUrl,
136
261
  payload: {
137
- action: payload.action ?? actionId,
138
- challengeToken,
262
+ action,
263
+ challengeToken: challengeTokenRef.current || challengeToken,
139
264
  executionId,
140
- inputs: {
141
- ...Object.fromEntries(Object.entries(payload.inputs ?? {}).filter(([key]) => !key.startsWith("__consent_opt__"))),
142
- ...!hasUserCodeInput(components) ? { user_code: void 0 } : {}
143
- }
265
+ inputs
144
266
  }
145
267
  }));
146
268
  } catch (flowError) {
147
- handleError(flowError instanceof Error ? flowError : new Error(t("deviceGrant.errors.continue")));
269
+ handleError(toDeviceGrantError(flowError, t("deviceGrant.errors.continue")));
148
270
  } finally {
149
271
  setIsLoading(false);
150
272
  }
151
273
  };
274
+ const timeoutContextRef = (0, react.useRef)({
275
+ components: [],
276
+ hasConsentPrompt: false,
277
+ submit
278
+ });
279
+ timeoutContextRef.current = {
280
+ components,
281
+ hasConsentPrompt: Boolean(additionalData["consentPrompt"]),
282
+ submit
283
+ };
284
+ const timeoutSubmittedForRef = (0, react.useRef)(null);
285
+ (0, react.useEffect)(() => {
286
+ const timeoutMs = Number(additionalData["stepTimeout"]) || 0;
287
+ if (timeoutMs <= 0 || !executionId) {
288
+ setIsTimeoutDisabled(false);
289
+ return;
290
+ }
291
+ const timerId = setTimeout(() => {
292
+ setIsTimeoutDisabled(true);
293
+ const expiredError = new __thunderid_browser.ThunderIDRuntimeError(t("errors.signin.timeout") || "Time allowed to complete the step has expired.", "DEVICE_GRANT_ERROR", "react");
294
+ const context = timeoutContextRef.current;
295
+ const actionId = context.hasConsentPrompt ? findConsentSubmitActionId(context.components) : void 0;
296
+ if (actionId && timeoutSubmittedForRef.current !== timeoutMs) {
297
+ timeoutSubmittedForRef.current = timeoutMs;
298
+ context.submit({
299
+ action: actionId,
300
+ inputs: { [CONSENT_REASON_KEY]: __thunderid_browser.ConsentConstants.REASON_TIMEOUT }
301
+ });
302
+ return;
303
+ }
304
+ handleError(expiredError);
305
+ }, Math.max(0, timeoutMs - Date.now()));
306
+ return () => clearTimeout(timerId);
307
+ }, [
308
+ additionalData,
309
+ executionId,
310
+ handleError,
311
+ t
312
+ ]);
313
+ require_useOAuthCallback.useOAuthCallback({
314
+ currentExecutionId: executionId || null,
315
+ executionIdStorageKey,
316
+ isInitialized: isInitialized && !sdkLoading && isStorageReady,
317
+ isSubmitting: isLoading,
318
+ onError: (callbackError) => {
319
+ persistExecutionId(null);
320
+ persistChallengeToken(null);
321
+ handleError(toDeviceGrantError(callbackError, String(callbackError)));
322
+ },
323
+ onFlowChange: (response) => void applyResponse(response),
324
+ onSubmit: async ({ executionId: callbackExecutionId, inputs }) => {
325
+ setIsLoading(true);
326
+ try {
327
+ return await (0, __thunderid_browser.executeEmbeddedDeviceGrantFlow)({
328
+ baseUrl,
329
+ payload: {
330
+ challengeToken: challengeTokenRef.current || challengeToken,
331
+ executionId: callbackExecutionId,
332
+ inputs
333
+ }
334
+ });
335
+ } finally {
336
+ setIsLoading(false);
337
+ }
338
+ },
339
+ processedRef: oauthCodeProcessedRef,
340
+ setExecutionId: persistExecutionId
341
+ });
152
342
  const handleBaseSubmit = (payload, component) => submit(payload, component.id);
153
343
  const fieldErrors = (0, react.useMemo)(() => {
154
344
  const errors = {};
@@ -164,6 +354,7 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
164
354
  fieldErrors,
165
355
  initialValues,
166
356
  isLoading,
357
+ isTimeoutDisabled,
167
358
  meta,
168
359
  onSubmit: submit,
169
360
  result
@@ -176,6 +367,7 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
176
367
  error,
177
368
  initialValues,
178
369
  isLoading,
370
+ isTimeoutDisabled,
179
371
  onError: handleError,
180
372
  onSubmit: handleBaseSubmit,
181
373
  preferences,
@@ -1 +1 @@
1
- {"version":3,"file":"DeviceGrant.cjs","names":["DeviceGrant: FC<DeviceGrantProps>","useThunderID","useTranslation","normalizeFlowResponse","extractErrorMessage","EmbeddedSignInFlowStatus","EmbeddedFlowType","errors: Record<string, string>","BaseDeviceGrant"],"sources":["../../../../../src/components/auth/DeviceGrant/DeviceGrant.tsx"],"sourcesContent":["// Copyright 2025-2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {EmbeddedFlowType, EmbeddedSignInFlowStatus, executeEmbeddedDeviceGrantFlow} from '@thunderid/browser';\nimport type {EmbeddedFlowComponent, EmbeddedSignInFlowRequest, FlowMetadataResponse} from '@thunderid/browser';\nimport {useCallback, useEffect, useMemo, useRef, useState} from 'react';\nimport type {FC, ReactElement, ReactNode} from 'react';\nimport BaseDeviceGrant, {type BaseDeviceGrantProps, type DeviceGrantResult} from './BaseDeviceGrant';\nimport useThunderID from '../../../contexts/ThunderID/useThunderID';\nimport useTranslation from '../../../hooks/useTranslation';\nimport {extractErrorMessage, normalizeFlowResponse} from '../../../utils/flowTransformer';\n\ntype EmbeddedDeviceGrantFlowResponse = Awaited<ReturnType<typeof executeEmbeddedDeviceGrantFlow>>;\ntype DeviceGrantFieldError = NonNullable<EmbeddedDeviceGrantFlowResponse['data']['fieldErrors']>[number];\n\n/** Values exposed to consumers that provide custom Device Grant UI. */\nexport interface DeviceGrantRenderProps {\n /** Additional values returned for the active flow step. */\n additionalData: Record<string, unknown>;\n /** Components returned by the configured flow. */\n components: EmbeddedFlowComponent[];\n /** Current flow error, when one needs to be shown. */\n error: Error | null;\n /** First server validation message for each field. */\n fieldErrors: Record<string, string>;\n /** Values prefilled from the verification URI. */\n initialValues: Record<string, string>;\n /** Whether a flow request is in progress. */\n isLoading: boolean;\n /** Flow metadata used to resolve application-specific template values. */\n meta: FlowMetadataResponse | null;\n /** Submits user-code input or continues the active flow. */\n onSubmit: (payload: EmbeddedSignInFlowRequest) => Promise<void>;\n /** Terminal flow result when the flow has no configured prompt to render. */\n result: DeviceGrantResult;\n}\n\nexport type DeviceGrantProps = Omit<\n BaseDeviceGrantProps,\n 'children' | 'components' | 'error' | 'initialValues' | 'isLoading' | 'onSubmit' | 'result' | 'serverFieldErrors'\n> & {\n /** Render props for Gate or other consumers that provide their own flow UI. */\n children?: (props: DeviceGrantRenderProps) => ReactNode;\n onComplete?: (response: EmbeddedDeviceGrantFlowResponse) => void;\n};\n\nconst clearVerificationParameters = (): void => {\n if (!window.location.href) return;\n const url = new URL(window.location.href);\n url.searchParams.delete('user_code');\n url.searchParams.delete('applicationId');\n window.history.replaceState(window.history.state, '', url.toString());\n};\n\nconst hasUserCodeInput = (components: EmbeddedFlowComponent[]): boolean =>\n components.some(\n (component: EmbeddedFlowComponent) =>\n component.ref === 'user_code' || (Array.isArray(component.components) && hasUserCodeInput(component.components)),\n );\n\n/**\n * Runs the application's configured Device Grant flow and exposes its state for custom renderers.\n */\nconst DeviceGrant: FC<DeviceGrantProps> = ({\n children = undefined,\n className = '',\n onComplete = undefined,\n onError = undefined,\n preferences = {},\n size = 'medium',\n variant = 'default',\n ...baseProps\n}: DeviceGrantProps): ReactElement => {\n const {applicationId, baseUrl, meta} = useThunderID();\n const {t} = useTranslation(preferences?.i18n);\n const [components, setComponents] = useState<EmbeddedFlowComponent[]>([]);\n const [additionalData, setAdditionalData] = useState<Record<string, unknown>>({});\n const [initialValues, setInitialValues] = useState<Record<string, string>>({});\n const [serverFieldErrors, setServerFieldErrors] = useState<\n EmbeddedDeviceGrantFlowResponse['data']['fieldErrors'] | null\n >(null);\n const [executionId, setExecutionId] = useState('');\n const [challengeToken, setChallengeToken] = useState('');\n const [error, setError] = useState<Error | null>(null);\n const [isLoading, setIsLoading] = useState(() => {\n if (applicationId) return true;\n return typeof window !== 'undefined' && Boolean(new URL(window.location.href).searchParams.get('applicationId'));\n });\n const [result, setResult] = useState<DeviceGrantResult>(null);\n const flowStartAttempted = useRef(false);\n const verificationParametersRead = useRef(false);\n\n const handleError = useCallback(\n (flowError: Error): void => {\n setComponents([]);\n setAdditionalData({});\n setServerFieldErrors(null);\n setError(flowError);\n setResult('failure');\n onError?.(flowError);\n },\n [onError],\n );\n\n const applyResponse = useCallback(\n (response: EmbeddedDeviceGrantFlowResponse): void => {\n const normalized = normalizeFlowResponse(\n response,\n t,\n {\n throwOnError: false,\n resolveTranslations: false,\n },\n meta,\n );\n setExecutionId(response.executionId);\n setChallengeToken(response.challengeToken ?? '');\n setComponents(normalized.components);\n setAdditionalData(normalized.additionalData);\n setServerFieldErrors(response.data?.fieldErrors ?? null);\n setError(response.error ? new Error(extractErrorMessage(response, t)) : null);\n\n if (response.flowStatus === EmbeddedSignInFlowStatus.Complete) {\n setResult('success');\n onComplete?.(response);\n return;\n }\n\n if (response.flowStatus === EmbeddedSignInFlowStatus.Error) {\n const flowError = new Error(extractErrorMessage(response, t, 'deviceGrant.messages.failed'));\n setError(flowError);\n setResult('failure');\n onError?.(flowError);\n return;\n }\n\n setResult(null);\n },\n [meta, onComplete, onError, t],\n );\n\n const startFlow = useCallback(\n async (appId: string): Promise<void> => {\n setIsLoading(true);\n setError(null);\n try {\n const response = await executeEmbeddedDeviceGrantFlow({\n baseUrl,\n payload: {\n applicationId: appId,\n flowType: EmbeddedFlowType.DeviceGrant,\n inputs: {},\n },\n });\n clearVerificationParameters();\n applyResponse(response);\n } catch (flowError) {\n handleError(flowError instanceof Error ? flowError : new Error(t('deviceGrant.errors.continue')));\n } finally {\n setIsLoading(false);\n }\n },\n [applyResponse, baseUrl, handleError, t],\n );\n\n useEffect(() => {\n const url = new URL(window.location.href);\n if (!verificationParametersRead.current) {\n verificationParametersRead.current = true;\n const userCode = url.searchParams.get('user_code') ?? '';\n if (userCode) setInitialValues({user_code: userCode});\n }\n\n const applicationIdFromUrl = url.searchParams.get('applicationId');\n const appId = applicationIdFromUrl?.trim() ? applicationIdFromUrl : applicationId;\n if (flowStartAttempted.current) return;\n if (!appId) {\n flowStartAttempted.current = true;\n handleError(new Error(t('deviceGrant.errors.start')));\n return;\n }\n\n flowStartAttempted.current = true;\n void startFlow(appId);\n }, [applicationId, handleError, startFlow, t]);\n\n const submit = async (payload: EmbeddedSignInFlowRequest, actionId?: string): Promise<void> => {\n setIsLoading(true);\n setError(null);\n setResult(null);\n try {\n if (!executionId) return;\n\n const response = await executeEmbeddedDeviceGrantFlow({\n baseUrl,\n payload: {\n action: payload.action ?? actionId,\n challengeToken,\n executionId,\n inputs: {\n ...Object.fromEntries(\n Object.entries(payload.inputs ?? {}).filter(\n ([key]: [string, unknown]) => !key.startsWith('__consent_opt__'),\n ),\n ),\n ...(!hasUserCodeInput(components) ? {user_code: undefined} : {}),\n },\n },\n });\n applyResponse(response);\n } catch (flowError) {\n handleError(flowError instanceof Error ? flowError : new Error(t('deviceGrant.errors.continue')));\n } finally {\n setIsLoading(false);\n }\n };\n\n const handleBaseSubmit = (payload: EmbeddedSignInFlowRequest, component: EmbeddedFlowComponent): Promise<void> =>\n submit(payload, component.id);\n\n const fieldErrors = useMemo<Record<string, string>>(() => {\n const errors: Record<string, string> = {};\n (serverFieldErrors ?? []).forEach((fieldError: DeviceGrantFieldError) => {\n if (!(fieldError.identifier in errors)) errors[fieldError.identifier] = fieldError.message;\n });\n\n return errors;\n }, [serverFieldErrors]);\n\n if (children) {\n return (\n <>\n {children({\n additionalData,\n components,\n error,\n fieldErrors,\n initialValues,\n isLoading,\n meta,\n onSubmit: submit,\n result,\n })}\n </>\n );\n }\n\n return (\n <BaseDeviceGrant\n {...baseProps}\n className={className}\n components={components}\n additionalData={additionalData}\n error={error}\n initialValues={initialValues}\n isLoading={isLoading}\n onError={handleError}\n onSubmit={handleBaseSubmit}\n preferences={preferences}\n result={result}\n serverFieldErrors={serverFieldErrors}\n size={size}\n variant={variant}\n />\n );\n};\n\nexport default DeviceGrant;\n"],"mappings":"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"}
1
+ {"version":3,"file":"DeviceGrant.cjs","names":["ThunderIDAPIError","ThunderIDRuntimeError","parsed: unknown","actions: EmbeddedFlowComponent[]","consentReason: unknown","cleanInputs: Record<string, unknown>","ConsentConstants","DeviceGrant: FC<DeviceGrantProps>","useThunderID","useTranslation","EmbeddedSignInFlowType","normalizeFlowResponse","EmbeddedSignInFlowStatus","extractErrorMessage","response: EmbeddedDeviceGrantFlowResponse","EmbeddedFlowType","errors: Record<string, string>","BaseDeviceGrant"],"sources":["../../../../../src/components/auth/DeviceGrant/DeviceGrant.tsx"],"sourcesContent":["// Copyright 2025-2026 The ThunderID Authors\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n compileConsentDecisions,\n ConsentConstants,\n EmbeddedFlowType,\n EmbeddedSignInFlowStatus,\n EmbeddedSignInFlowType,\n ThunderIDAPIError,\n ThunderIDRuntimeError,\n executeEmbeddedDeviceGrantFlow,\n} from '@thunderid/browser';\nimport type {\n ConsentPurposeData,\n EmbeddedFlowComponent,\n EmbeddedSignInFlowRequest,\n FlowMetadataResponse,\n} from '@thunderid/browser';\nimport {useCallback, useEffect, useMemo, useRef, useState} from 'react';\nimport type {FC, ReactElement, ReactNode} from 'react';\nimport BaseDeviceGrant, {type BaseDeviceGrantProps, type DeviceGrantResult} from './BaseDeviceGrant';\nimport useThunderID from '../../../contexts/ThunderID/useThunderID';\nimport {useOAuthCallback} from '../../../hooks/auth/useOAuthCallback';\nimport useTranslation from '../../../hooks/useTranslation';\nimport {extractErrorMessage, normalizeFlowResponse} from '../../../utils/flowTransformer';\nimport {initiateOAuthRedirect} from '../../../utils/oauth';\n\ntype EmbeddedDeviceGrantFlowResponse = Awaited<ReturnType<typeof executeEmbeddedDeviceGrantFlow>>;\ntype DeviceGrantFieldError = NonNullable<EmbeddedDeviceGrantFlowResponse['data']['fieldErrors']>[number];\n\nconst toDeviceGrantError = (cause: unknown, fallbackMessage: string): Error => {\n if (cause instanceof ThunderIDAPIError || cause instanceof ThunderIDRuntimeError) return cause;\n return new ThunderIDRuntimeError(\n cause instanceof Error ? cause.message : fallbackMessage,\n 'DEVICE_GRANT_ERROR',\n 'react',\n cause,\n );\n};\n\n/** Values exposed to consumers that provide custom Device Grant UI. */\nexport interface DeviceGrantRenderProps {\n /** Additional values returned for the active flow step. */\n additionalData: Record<string, unknown>;\n /** Components returned by the configured flow. */\n components: EmbeddedFlowComponent[];\n /** Current flow error, when one needs to be shown. */\n error: Error | null;\n /** First server validation message for each field. */\n fieldErrors: Record<string, string>;\n /** Values prefilled from the verification URI. */\n initialValues: Record<string, string>;\n /** Whether a flow request is in progress. */\n isLoading: boolean;\n /** Whether the active flow step has expired. */\n isTimeoutDisabled: boolean;\n /** Flow metadata used to resolve application-specific template values. */\n meta: FlowMetadataResponse | null;\n /** Submits user-code input or continues the active flow. */\n onSubmit: (payload: EmbeddedSignInFlowRequest) => Promise<void>;\n /** Terminal flow result when the flow has no configured prompt to render. */\n result: DeviceGrantResult;\n}\n\nexport type DeviceGrantProps = Omit<\n BaseDeviceGrantProps,\n 'children' | 'components' | 'error' | 'initialValues' | 'isLoading' | 'onSubmit' | 'result' | 'serverFieldErrors'\n> & {\n /** Render props for Gate or other consumers that provide their own flow UI. */\n children?: (props: DeviceGrantRenderProps) => ReactNode;\n onComplete?: (response: EmbeddedDeviceGrantFlowResponse) => void;\n};\n\nconst clearVerificationParameters = (): void => {\n if (!window.location.href) return;\n const url = new URL(window.location.href);\n url.searchParams.delete('user_code');\n url.searchParams.delete('applicationId');\n window.history.replaceState(window.history.state, '', url.toString());\n};\n\nconst hasUserCodeInput = (components: EmbeddedFlowComponent[]): boolean =>\n components.some(\n (component: EmbeddedFlowComponent) =>\n component.ref === 'user_code' || (Array.isArray(component.components) && hasUserCodeInput(component.components)),\n );\n\nconst findAction = (components: EmbeddedFlowComponent[], actionId: string): EmbeddedFlowComponent | undefined => {\n for (const component of components) {\n if (component.id === actionId) return component;\n if (component.components) {\n const nestedAction = findAction(component.components, actionId);\n if (nestedAction) return nestedAction;\n }\n }\n return undefined;\n};\n\nconst parseConsentPurposes = (value: unknown): ConsentPurposeData[] | null => {\n let parsed: unknown = value;\n if (typeof parsed === 'string') {\n try {\n parsed = JSON.parse(parsed) as unknown;\n } catch {\n return null;\n }\n }\n if (Array.isArray(parsed)) return parsed as ConsentPurposeData[];\n if (typeof parsed === 'object' && parsed !== null && 'purposes' in parsed && Array.isArray(parsed.purposes)) {\n return parsed.purposes as ConsentPurposeData[];\n }\n return null;\n};\n\nconst CONSENT_REASON_KEY = '__consent_reason__';\n\nconst flattenComponents = (items: EmbeddedFlowComponent[]): EmbeddedFlowComponent[] =>\n items.flatMap((component: EmbeddedFlowComponent) => [component, ...flattenComponents(component.components ?? [])]);\n\nconst findConsentSubmitActionId = (items: EmbeddedFlowComponent[]): string | undefined => {\n const actions: EmbeddedFlowComponent[] = flattenComponents(items).filter(\n (component: EmbeddedFlowComponent) => component.id && component.eventType?.toUpperCase() === 'SUBMIT',\n );\n return (\n actions.find((component: EmbeddedFlowComponent) => component.variant?.toLowerCase() !== 'primary') ?? actions[0]\n )?.id;\n};\n\nconst compileConsentInputs = (\n inputs: Record<string, unknown>,\n actionId: string | undefined,\n components: EmbeddedFlowComponent[],\n additionalData: Record<string, unknown>,\n): Record<string, unknown> => {\n const action = actionId ? findAction(components, actionId) : undefined;\n const purposes = parseConsentPurposes(additionalData['consentPrompt']);\n const consentReason: unknown = inputs[CONSENT_REASON_KEY];\n const cleanInputs: Record<string, unknown> = {...inputs};\n delete cleanInputs[CONSENT_REASON_KEY];\n const isSubmitAction = Boolean(action && action.eventType?.toUpperCase() !== 'TRIGGER');\n const isTimeout = consentReason === ConsentConstants.REASON_TIMEOUT;\n const isDeny = isTimeout || Boolean(action && isSubmitAction && action.variant?.toLowerCase() !== 'primary');\n\n return compileConsentDecisions(cleanInputs, isSubmitAction ? purposes : null, {\n approved: !isDeny,\n ...(isTimeout\n ? {reason: ConsentConstants.REASON_TIMEOUT}\n : isDeny\n ? {reason: ConsentConstants.REASON_USER_DENIED}\n : {}),\n });\n};\n\n/**\n * Runs the application's configured Device Grant flow and exposes its state for custom renderers.\n */\nconst DeviceGrant: FC<DeviceGrantProps> = ({\n children = undefined,\n className = '',\n onComplete = undefined,\n onError = undefined,\n preferences = {},\n size = 'medium',\n variant = 'default',\n ...baseProps\n}: DeviceGrantProps): ReactElement => {\n const {\n applicationId,\n baseUrl,\n getStorageManager,\n isInitialized,\n isLoading: sdkLoading,\n meta,\n vendor,\n } = useThunderID();\n const {t} = useTranslation(preferences?.i18n);\n const [components, setComponents] = useState<EmbeddedFlowComponent[]>([]);\n const [additionalData, setAdditionalData] = useState<Record<string, unknown>>({});\n const [initialValues, setInitialValues] = useState<Record<string, string>>({});\n const [serverFieldErrors, setServerFieldErrors] = useState<\n EmbeddedDeviceGrantFlowResponse['data']['fieldErrors'] | null\n >(null);\n const [executionId, setExecutionId] = useState('');\n const [challengeToken, setChallengeToken] = useState('');\n const [isStorageReady, setIsStorageReady] = useState(false);\n const challengeTokenRef = useRef('');\n const [error, setError] = useState<Error | null>(null);\n const [isLoading, setIsLoading] = useState(() => {\n if (\n typeof window !== 'undefined' &&\n ['code', 'state', 'error'].some((parameter: string) => new URL(window.location.href).searchParams.has(parameter))\n ) {\n return false;\n }\n if (applicationId) return true;\n return typeof window !== 'undefined' && Boolean(new URL(window.location.href).searchParams.get('applicationId'));\n });\n const [result, setResult] = useState<DeviceGrantResult>(null);\n const [isTimeoutDisabled, setIsTimeoutDisabled] = useState(false);\n const flowStartAttempted = useRef(false);\n const verificationParametersRead = useRef(false);\n const oauthCodeProcessedRef = useRef(false);\n const executionIdStorageKey = `${vendor}_device_grant_execution_id`;\n\n useEffect(() => {\n if (!isInitialized || sdkLoading) return;\n void (async (): Promise<void> => {\n try {\n const storageManager = await getStorageManager?.();\n const temporaryData = await storageManager?.getTemporaryData();\n const storedChallengeToken = temporaryData?.['challengeToken'];\n if (typeof storedChallengeToken === 'string') {\n challengeTokenRef.current = storedChallengeToken;\n setChallengeToken(storedChallengeToken);\n }\n } catch {\n // Flow continuation can still proceed without a cached token if the server permits it.\n } finally {\n setIsStorageReady(true);\n }\n })();\n }, [getStorageManager, isInitialized, sdkLoading]);\n\n const persistExecutionId = useCallback(\n (id: string | null): void => {\n setExecutionId(id ?? '');\n if (id) sessionStorage.setItem(executionIdStorageKey, id);\n else sessionStorage.removeItem(executionIdStorageKey);\n },\n [executionIdStorageKey],\n );\n\n const persistChallengeToken = useCallback(\n async (token: string | null): Promise<void> => {\n challengeTokenRef.current = token ?? '';\n setChallengeToken(token ?? '');\n try {\n const storageManager = await getStorageManager();\n if (storageManager) {\n if (token) await storageManager.setTemporaryDataParameter('challengeToken', token);\n else await storageManager.removeTemporaryDataParameter('challengeToken');\n }\n } catch {\n // The in-memory value is still available for same-page flow continuation.\n }\n },\n [getStorageManager],\n );\n\n const handleError = useCallback(\n (flowError: Error): void => {\n setComponents([]);\n setAdditionalData({});\n setServerFieldErrors(null);\n setError(flowError);\n setResult('failure');\n onError?.(flowError);\n },\n [onError],\n );\n\n const applyResponse = useCallback(\n async (response: EmbeddedDeviceGrantFlowResponse): Promise<void> => {\n const redirectURL =\n (response.data as Record<string, unknown> | undefined)?.['redirectURL'] ?? response.redirectUrl;\n if (response.type === EmbeddedSignInFlowType.Redirection && typeof redirectURL === 'string') {\n persistExecutionId(response.executionId ?? null);\n await persistChallengeToken(response.challengeToken ?? null);\n initiateOAuthRedirect(redirectURL, vendor);\n return;\n }\n\n const normalized = normalizeFlowResponse(\n response,\n t,\n {\n throwOnError: false,\n resolveTranslations: false,\n },\n meta,\n );\n persistExecutionId(\n response.flowStatus === EmbeddedSignInFlowStatus.Incomplete ? (response.executionId ?? null) : null,\n );\n await persistChallengeToken(\n response.flowStatus === EmbeddedSignInFlowStatus.Incomplete ? (response.challengeToken ?? null) : null,\n );\n setComponents(normalized.components);\n setAdditionalData(normalized.additionalData);\n setIsTimeoutDisabled(false);\n setServerFieldErrors(response.data?.fieldErrors ?? null);\n setError(response.error ? new Error(extractErrorMessage(response, t)) : null);\n\n if (response.flowStatus === EmbeddedSignInFlowStatus.Complete) {\n setResult('success');\n onComplete?.(response);\n return;\n }\n\n if (response.flowStatus === EmbeddedSignInFlowStatus.Error) {\n const flowError = new ThunderIDRuntimeError(\n extractErrorMessage(response, t, 'deviceGrant.messages.failed'),\n 'DEVICE_GRANT_ERROR',\n 'react',\n );\n setError(flowError);\n setResult('failure');\n onError?.(flowError);\n return;\n }\n\n setResult(null);\n },\n [meta, onComplete, onError, persistChallengeToken, persistExecutionId, t, vendor],\n );\n\n const startFlow = useCallback(\n async (appId: string, resumeId?: string): Promise<void> => {\n setIsLoading(true);\n setError(null);\n try {\n const challengeFromStorage = challengeTokenRef.current;\n let response: EmbeddedDeviceGrantFlowResponse;\n try {\n response = await executeEmbeddedDeviceGrantFlow({\n baseUrl,\n payload: resumeId\n ? {executionId: resumeId, ...(challengeFromStorage ? {challengeToken: challengeFromStorage} : {})}\n : {applicationId: appId, flowType: EmbeddedFlowType.DeviceGrant, inputs: {}},\n });\n } catch (resumeError) {\n if (resumeId && resumeError instanceof ThunderIDAPIError && resumeError.statusCode === 400) {\n persistExecutionId(null);\n await persistChallengeToken(null);\n throw new ThunderIDRuntimeError(t('deviceGrant.errors.continue'), 'DEVICE_GRANT_ERROR', 'react');\n }\n throw resumeError;\n }\n if (!resumeId) clearVerificationParameters();\n await applyResponse(response);\n } catch (flowError) {\n handleError(toDeviceGrantError(flowError, t('deviceGrant.errors.continue')));\n } finally {\n setIsLoading(false);\n }\n },\n [applyResponse, baseUrl, handleError, persistChallengeToken, persistExecutionId, t],\n );\n\n useEffect(() => {\n const url = new URL(window.location.href);\n if (!verificationParametersRead.current) {\n verificationParametersRead.current = true;\n const userCode = url.searchParams.get('user_code') ?? '';\n if (userCode) setInitialValues({user_code: userCode});\n }\n\n const applicationIdFromUrl = url.searchParams.get('applicationId');\n const appId = applicationIdFromUrl?.trim() ? applicationIdFromUrl : applicationId;\n const storedExecutionId = sessionStorage.getItem(executionIdStorageKey);\n const hasOAuthCallback =\n url.searchParams.has('code') || url.searchParams.has('state') || url.searchParams.has('error');\n if (!isInitialized || sdkLoading || !isStorageReady || hasOAuthCallback || oauthCodeProcessedRef.current) return;\n if (flowStartAttempted.current) return;\n if (!appId && !storedExecutionId) {\n flowStartAttempted.current = true;\n handleError(new ThunderIDRuntimeError(t('deviceGrant.errors.start'), 'DEVICE_GRANT_ERROR', 'react'));\n return;\n }\n\n flowStartAttempted.current = true;\n void startFlow(appId ?? '', storedExecutionId ?? undefined);\n }, [applicationId, executionIdStorageKey, handleError, isInitialized, isStorageReady, sdkLoading, startFlow, t]);\n\n const submit = async (payload: EmbeddedSignInFlowRequest, actionId?: string): Promise<void> => {\n setIsLoading(true);\n setError(null);\n setResult(null);\n try {\n if (!executionId) return;\n\n const action = payload.action ?? actionId;\n const inputs = compileConsentInputs(payload.inputs ?? {}, action, components, additionalData);\n if (!hasUserCodeInput(components)) inputs['user_code'] = undefined;\n\n const response = await executeEmbeddedDeviceGrantFlow({\n baseUrl,\n payload: {\n action,\n challengeToken: challengeTokenRef.current || challengeToken,\n executionId,\n inputs,\n },\n });\n await applyResponse(response);\n } catch (flowError) {\n handleError(toDeviceGrantError(flowError, t('deviceGrant.errors.continue')));\n } finally {\n setIsLoading(false);\n }\n };\n\n const timeoutContextRef = useRef<{\n components: EmbeddedFlowComponent[];\n hasConsentPrompt: boolean;\n submit: typeof submit;\n }>({components: [], hasConsentPrompt: false, submit});\n timeoutContextRef.current = {components, hasConsentPrompt: Boolean(additionalData['consentPrompt']), submit};\n const timeoutSubmittedForRef = useRef<number | null>(null);\n\n useEffect(() => {\n const timeoutMs = Number(additionalData['stepTimeout']) || 0;\n if (timeoutMs <= 0 || !executionId) {\n setIsTimeoutDisabled(false);\n return undefined;\n }\n\n const timerId = setTimeout(\n () => {\n setIsTimeoutDisabled(true);\n const expiredError = new ThunderIDRuntimeError(\n t('errors.signin.timeout') || 'Time allowed to complete the step has expired.',\n 'DEVICE_GRANT_ERROR',\n 'react',\n );\n const context = timeoutContextRef.current;\n const actionId = context.hasConsentPrompt ? findConsentSubmitActionId(context.components) : undefined;\n if (actionId && timeoutSubmittedForRef.current !== timeoutMs) {\n timeoutSubmittedForRef.current = timeoutMs;\n void context.submit({action: actionId, inputs: {[CONSENT_REASON_KEY]: ConsentConstants.REASON_TIMEOUT}});\n return;\n }\n handleError(expiredError);\n },\n Math.max(0, timeoutMs - Date.now()),\n );\n\n return () => clearTimeout(timerId);\n }, [additionalData, executionId, handleError, t]);\n\n useOAuthCallback({\n currentExecutionId: executionId || null,\n executionIdStorageKey,\n isInitialized: isInitialized && !sdkLoading && isStorageReady,\n isSubmitting: isLoading,\n onError: (callbackError: unknown) => {\n persistExecutionId(null);\n void persistChallengeToken(null);\n handleError(toDeviceGrantError(callbackError, String(callbackError)));\n },\n onFlowChange: (response: EmbeddedDeviceGrantFlowResponse) => void applyResponse(response),\n onSubmit: async ({executionId: callbackExecutionId, inputs}) => {\n setIsLoading(true);\n try {\n return await executeEmbeddedDeviceGrantFlow({\n baseUrl,\n payload: {\n challengeToken: challengeTokenRef.current || challengeToken,\n executionId: callbackExecutionId,\n inputs,\n },\n });\n } finally {\n setIsLoading(false);\n }\n },\n processedRef: oauthCodeProcessedRef,\n setExecutionId: persistExecutionId,\n });\n\n const handleBaseSubmit = (payload: EmbeddedSignInFlowRequest, component: EmbeddedFlowComponent): Promise<void> =>\n submit(payload, component.id);\n\n const fieldErrors = useMemo<Record<string, string>>(() => {\n const errors: Record<string, string> = {};\n (serverFieldErrors ?? []).forEach((fieldError: DeviceGrantFieldError) => {\n if (!(fieldError.identifier in errors)) errors[fieldError.identifier] = fieldError.message;\n });\n\n return errors;\n }, [serverFieldErrors]);\n\n if (children) {\n return (\n <>\n {children({\n additionalData,\n components,\n error,\n fieldErrors,\n initialValues,\n isLoading,\n isTimeoutDisabled,\n meta,\n onSubmit: submit,\n result,\n })}\n </>\n );\n }\n\n return (\n <BaseDeviceGrant\n {...baseProps}\n className={className}\n components={components}\n additionalData={additionalData}\n error={error}\n initialValues={initialValues}\n isLoading={isLoading}\n isTimeoutDisabled={isTimeoutDisabled}\n onError={handleError}\n onSubmit={handleBaseSubmit}\n preferences={preferences}\n result={result}\n serverFieldErrors={serverFieldErrors}\n size={size}\n variant={variant}\n />\n );\n};\n\nexport default DeviceGrant;\n"],"mappings":"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"}
@@ -409,7 +409,7 @@ const SignIn = ({ className, preferences, size = "medium", onSuccess, onError, v
409
409
  const handleSubmit = async (payload) => {
410
410
  const effectiveExecutionId = payload.executionId || currentExecutionId;
411
411
  if (!effectiveExecutionId) throw new Error("No active flow ID");
412
- const processedInputs = { ...payload.inputs };
412
+ let processedInputs = { ...payload.inputs };
413
413
  const consentReason = processedInputs[CONSENT_REASON_KEY];
414
414
  delete processedInputs[CONSENT_REASON_KEY];
415
415
  if (additionalData?.["consentPrompt"]) try {
@@ -433,25 +433,9 @@ const SignIn = ({ className, preferences, size = "medium", onSuccess, onError, v
433
433
  if (consentReason === __thunderid_browser.ConsentConstants.REASON_TIMEOUT) isDeny = true;
434
434
  if (!isDeny && require_isConsentApprovalBlocked.default(purposes, processedInputs)) return;
435
435
  const reason = consentReason ?? (isDeny ? __thunderid_browser.ConsentConstants.REASON_USER_DENIED : void 0);
436
- const decisions = {
436
+ processedInputs = (0, __thunderid_browser.compileConsentDecisions)(processedInputs, purposes, {
437
437
  approved: !isDeny,
438
- ...reason ? { reason } : {},
439
- purposes: purposes.map((p) => ({
440
- approved: !isDeny,
441
- elements: [...(p.essential || []).map((e) => ({
442
- approved: !isDeny,
443
- name: e.name
444
- })), ...(p.optional || []).map((e) => {
445
- const key = `__consent_opt__${p.purposeId}__${e.name}`;
446
- const approved = !isDeny && processedInputs[key] === "true";
447
- return (0, __thunderid_browser.getConsentElementDecision)(p.purposeId, e, approved, processedInputs);
448
- })],
449
- purposeName: p.purposeName
450
- }))
451
- };
452
- processedInputs["consent_decisions"] = JSON.stringify(decisions);
453
- Object.keys(processedInputs).forEach((key) => {
454
- if (key.startsWith("__consent_opt__") || key.startsWith(__thunderid_browser.CONSENT_MODIFIED_KEY_PREFIX) || key.startsWith(__thunderid_browser.CONSENT_INVALID_KEY_PREFIX)) delete processedInputs[key];
438
+ ...reason ? { reason } : {}
455
439
  });
456
440
  } catch (e) {}
457
441
  try {