@thunderid/react 1.11.3 → 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.
@@ -2,11 +2,17 @@ import useThunderID_default from "../../../contexts/ThunderID/useThunderID.js";
2
2
  import useTranslation_default from "../../../hooks/useTranslation.js";
3
3
  import { extractErrorMessage, normalizeFlowResponse } from "../../../utils/flowTransformer.js";
4
4
  import BaseDeviceGrant_default from "./BaseDeviceGrant.js";
5
- import { EmbeddedFlowType, EmbeddedSignInFlowStatus, executeEmbeddedDeviceGrantFlow } from "@thunderid/browser";
5
+ import { useOAuthCallback } from "../../../hooks/auth/useOAuthCallback.js";
6
+ import { initiateOAuthRedirect } from "../../../utils/oauth.js";
7
+ import { ConsentConstants, EmbeddedFlowType, EmbeddedSignInFlowStatus, EmbeddedSignInFlowType, ThunderIDAPIError, ThunderIDRuntimeError, compileConsentDecisions, executeEmbeddedDeviceGrantFlow } from "@thunderid/browser";
6
8
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
9
  import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
8
10
 
9
11
  //#region src/components/auth/DeviceGrant/DeviceGrant.tsx
12
+ const toDeviceGrantError = (cause, fallbackMessage) => {
13
+ if (cause instanceof ThunderIDAPIError || cause instanceof ThunderIDRuntimeError) return cause;
14
+ return new ThunderIDRuntimeError(cause instanceof Error ? cause.message : fallbackMessage, "DEVICE_GRANT_ERROR", "react", cause);
15
+ };
10
16
  const clearVerificationParameters = () => {
11
17
  if (!window.location.href) return;
12
18
  const url = new URL(window.location.href);
@@ -15,11 +21,51 @@ const clearVerificationParameters = () => {
15
21
  window.history.replaceState(window.history.state, "", url.toString());
16
22
  };
17
23
  const hasUserCodeInput = (components) => components.some((component) => component.ref === "user_code" || Array.isArray(component.components) && hasUserCodeInput(component.components));
24
+ const findAction = (components, actionId) => {
25
+ for (const component of components) {
26
+ if (component.id === actionId) return component;
27
+ if (component.components) {
28
+ const nestedAction = findAction(component.components, actionId);
29
+ if (nestedAction) return nestedAction;
30
+ }
31
+ }
32
+ };
33
+ const parseConsentPurposes = (value) => {
34
+ let parsed = value;
35
+ if (typeof parsed === "string") try {
36
+ parsed = JSON.parse(parsed);
37
+ } catch {
38
+ return null;
39
+ }
40
+ if (Array.isArray(parsed)) return parsed;
41
+ if (typeof parsed === "object" && parsed !== null && "purposes" in parsed && Array.isArray(parsed.purposes)) return parsed.purposes;
42
+ return null;
43
+ };
44
+ const CONSENT_REASON_KEY = "__consent_reason__";
45
+ const flattenComponents = (items) => items.flatMap((component) => [component, ...flattenComponents(component.components ?? [])]);
46
+ const findConsentSubmitActionId = (items) => {
47
+ const actions = flattenComponents(items).filter((component) => component.id && component.eventType?.toUpperCase() === "SUBMIT");
48
+ return (actions.find((component) => component.variant?.toLowerCase() !== "primary") ?? actions[0])?.id;
49
+ };
50
+ const compileConsentInputs = (inputs, actionId, components, additionalData) => {
51
+ const action = actionId ? findAction(components, actionId) : void 0;
52
+ const purposes = parseConsentPurposes(additionalData["consentPrompt"]);
53
+ const consentReason = inputs[CONSENT_REASON_KEY];
54
+ const cleanInputs = { ...inputs };
55
+ delete cleanInputs[CONSENT_REASON_KEY];
56
+ const isSubmitAction = Boolean(action && action.eventType?.toUpperCase() !== "TRIGGER");
57
+ const isTimeout = consentReason === ConsentConstants.REASON_TIMEOUT;
58
+ const isDeny = isTimeout || Boolean(action && isSubmitAction && action.variant?.toLowerCase() !== "primary");
59
+ return compileConsentDecisions(cleanInputs, isSubmitAction ? purposes : null, {
60
+ approved: !isDeny,
61
+ ...isTimeout ? { reason: ConsentConstants.REASON_TIMEOUT } : isDeny ? { reason: ConsentConstants.REASON_USER_DENIED } : {}
62
+ });
63
+ };
18
64
  /**
19
65
  * Runs the application's configured Device Grant flow and exposes its state for custom renderers.
20
66
  */
21
67
  const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, onError = void 0, preferences = {}, size = "medium", variant = "default",...baseProps }) => {
22
- const { applicationId, baseUrl, meta } = useThunderID_default();
68
+ const { applicationId, baseUrl, getStorageManager, isInitialized, isLoading: sdkLoading, meta, vendor } = useThunderID_default();
23
69
  const { t } = useTranslation_default(preferences?.i18n);
24
70
  const [components, setComponents] = useState([]);
25
71
  const [additionalData, setAdditionalData] = useState({});
@@ -27,14 +73,56 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
27
73
  const [serverFieldErrors, setServerFieldErrors] = useState(null);
28
74
  const [executionId, setExecutionId] = useState("");
29
75
  const [challengeToken, setChallengeToken] = useState("");
76
+ const [isStorageReady, setIsStorageReady] = useState(false);
77
+ const challengeTokenRef = useRef("");
30
78
  const [error, setError] = useState(null);
31
79
  const [isLoading, setIsLoading] = useState(() => {
80
+ if (typeof window !== "undefined" && [
81
+ "code",
82
+ "state",
83
+ "error"
84
+ ].some((parameter) => new URL(window.location.href).searchParams.has(parameter))) return false;
32
85
  if (applicationId) return true;
33
86
  return typeof window !== "undefined" && Boolean(new URL(window.location.href).searchParams.get("applicationId"));
34
87
  });
35
88
  const [result, setResult] = useState(null);
89
+ const [isTimeoutDisabled, setIsTimeoutDisabled] = useState(false);
36
90
  const flowStartAttempted = useRef(false);
37
91
  const verificationParametersRead = useRef(false);
92
+ const oauthCodeProcessedRef = useRef(false);
93
+ const executionIdStorageKey = `${vendor}_device_grant_execution_id`;
94
+ useEffect(() => {
95
+ if (!isInitialized || sdkLoading) return;
96
+ (async () => {
97
+ try {
98
+ const storedChallengeToken = (await (await getStorageManager?.())?.getTemporaryData())?.["challengeToken"];
99
+ if (typeof storedChallengeToken === "string") {
100
+ challengeTokenRef.current = storedChallengeToken;
101
+ setChallengeToken(storedChallengeToken);
102
+ }
103
+ } catch {} finally {
104
+ setIsStorageReady(true);
105
+ }
106
+ })();
107
+ }, [
108
+ getStorageManager,
109
+ isInitialized,
110
+ sdkLoading
111
+ ]);
112
+ const persistExecutionId = useCallback((id) => {
113
+ setExecutionId(id ?? "");
114
+ if (id) sessionStorage.setItem(executionIdStorageKey, id);
115
+ else sessionStorage.removeItem(executionIdStorageKey);
116
+ }, [executionIdStorageKey]);
117
+ const persistChallengeToken = useCallback(async (token) => {
118
+ challengeTokenRef.current = token ?? "";
119
+ setChallengeToken(token ?? "");
120
+ try {
121
+ const storageManager = await getStorageManager();
122
+ if (storageManager) if (token) await storageManager.setTemporaryDataParameter("challengeToken", token);
123
+ else await storageManager.removeTemporaryDataParameter("challengeToken");
124
+ } catch {}
125
+ }, [getStorageManager]);
38
126
  const handleError = useCallback((flowError) => {
39
127
  setComponents([]);
40
128
  setAdditionalData({});
@@ -43,15 +131,23 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
43
131
  setResult("failure");
44
132
  onError?.(flowError);
45
133
  }, [onError]);
46
- const applyResponse = useCallback((response) => {
134
+ const applyResponse = useCallback(async (response) => {
135
+ const redirectURL = response.data?.["redirectURL"] ?? response.redirectUrl;
136
+ if (response.type === EmbeddedSignInFlowType.Redirection && typeof redirectURL === "string") {
137
+ persistExecutionId(response.executionId ?? null);
138
+ await persistChallengeToken(response.challengeToken ?? null);
139
+ initiateOAuthRedirect(redirectURL, vendor);
140
+ return;
141
+ }
47
142
  const normalized = normalizeFlowResponse(response, t, {
48
143
  throwOnError: false,
49
144
  resolveTranslations: false
50
145
  }, meta);
51
- setExecutionId(response.executionId);
52
- setChallengeToken(response.challengeToken ?? "");
146
+ persistExecutionId(response.flowStatus === EmbeddedSignInFlowStatus.Incomplete ? response.executionId ?? null : null);
147
+ await persistChallengeToken(response.flowStatus === EmbeddedSignInFlowStatus.Incomplete ? response.challengeToken ?? null : null);
53
148
  setComponents(normalized.components);
54
149
  setAdditionalData(normalized.additionalData);
150
+ setIsTimeoutDisabled(false);
55
151
  setServerFieldErrors(response.data?.fieldErrors ?? null);
56
152
  setError(response.error ? new Error(extractErrorMessage(response, t)) : null);
57
153
  if (response.flowStatus === EmbeddedSignInFlowStatus.Complete) {
@@ -60,7 +156,7 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
60
156
  return;
61
157
  }
62
158
  if (response.flowStatus === EmbeddedSignInFlowStatus.Error) {
63
- const flowError = new Error(extractErrorMessage(response, t, "deviceGrant.messages.failed"));
159
+ const flowError = new ThunderIDRuntimeError(extractErrorMessage(response, t, "deviceGrant.messages.failed"), "DEVICE_GRANT_ERROR", "react");
64
160
  setError(flowError);
65
161
  setResult("failure");
66
162
  onError?.(flowError);
@@ -71,24 +167,41 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
71
167
  meta,
72
168
  onComplete,
73
169
  onError,
74
- t
170
+ persistChallengeToken,
171
+ persistExecutionId,
172
+ t,
173
+ vendor
75
174
  ]);
76
- const startFlow = useCallback(async (appId) => {
175
+ const startFlow = useCallback(async (appId, resumeId) => {
77
176
  setIsLoading(true);
78
177
  setError(null);
79
178
  try {
80
- const response = await executeEmbeddedDeviceGrantFlow({
81
- baseUrl,
82
- payload: {
83
- applicationId: appId,
84
- flowType: EmbeddedFlowType.DeviceGrant,
85
- inputs: {}
179
+ const challengeFromStorage = challengeTokenRef.current;
180
+ let response;
181
+ try {
182
+ response = await executeEmbeddedDeviceGrantFlow({
183
+ baseUrl,
184
+ payload: resumeId ? {
185
+ executionId: resumeId,
186
+ ...challengeFromStorage ? { challengeToken: challengeFromStorage } : {}
187
+ } : {
188
+ applicationId: appId,
189
+ flowType: EmbeddedFlowType.DeviceGrant,
190
+ inputs: {}
191
+ }
192
+ });
193
+ } catch (resumeError) {
194
+ if (resumeId && resumeError instanceof ThunderIDAPIError && resumeError.statusCode === 400) {
195
+ persistExecutionId(null);
196
+ await persistChallengeToken(null);
197
+ throw new ThunderIDRuntimeError(t("deviceGrant.errors.continue"), "DEVICE_GRANT_ERROR", "react");
86
198
  }
87
- });
88
- clearVerificationParameters();
89
- applyResponse(response);
199
+ throw resumeError;
200
+ }
201
+ if (!resumeId) clearVerificationParameters();
202
+ await applyResponse(response);
90
203
  } catch (flowError) {
91
- handleError(flowError instanceof Error ? flowError : new Error(t("deviceGrant.errors.continue")));
204
+ handleError(toDeviceGrantError(flowError, t("deviceGrant.errors.continue")));
92
205
  } finally {
93
206
  setIsLoading(false);
94
207
  }
@@ -96,6 +209,8 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
96
209
  applyResponse,
97
210
  baseUrl,
98
211
  handleError,
212
+ persistChallengeToken,
213
+ persistExecutionId,
99
214
  t
100
215
  ]);
101
216
  useEffect(() => {
@@ -107,17 +222,24 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
107
222
  }
108
223
  const applicationIdFromUrl = url.searchParams.get("applicationId");
109
224
  const appId = applicationIdFromUrl?.trim() ? applicationIdFromUrl : applicationId;
225
+ const storedExecutionId = sessionStorage.getItem(executionIdStorageKey);
226
+ const hasOAuthCallback = url.searchParams.has("code") || url.searchParams.has("state") || url.searchParams.has("error");
227
+ if (!isInitialized || sdkLoading || !isStorageReady || hasOAuthCallback || oauthCodeProcessedRef.current) return;
110
228
  if (flowStartAttempted.current) return;
111
- if (!appId) {
229
+ if (!appId && !storedExecutionId) {
112
230
  flowStartAttempted.current = true;
113
- handleError(new Error(t("deviceGrant.errors.start")));
231
+ handleError(new ThunderIDRuntimeError(t("deviceGrant.errors.start"), "DEVICE_GRANT_ERROR", "react"));
114
232
  return;
115
233
  }
116
234
  flowStartAttempted.current = true;
117
- startFlow(appId);
235
+ startFlow(appId ?? "", storedExecutionId ?? void 0);
118
236
  }, [
119
237
  applicationId,
238
+ executionIdStorageKey,
120
239
  handleError,
240
+ isInitialized,
241
+ isStorageReady,
242
+ sdkLoading,
121
243
  startFlow,
122
244
  t
123
245
  ]);
@@ -127,24 +249,92 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
127
249
  setResult(null);
128
250
  try {
129
251
  if (!executionId) return;
130
- applyResponse(await executeEmbeddedDeviceGrantFlow({
252
+ const action = payload.action ?? actionId;
253
+ const inputs = compileConsentInputs(payload.inputs ?? {}, action, components, additionalData);
254
+ if (!hasUserCodeInput(components)) inputs["user_code"] = void 0;
255
+ await applyResponse(await executeEmbeddedDeviceGrantFlow({
131
256
  baseUrl,
132
257
  payload: {
133
- action: payload.action ?? actionId,
134
- challengeToken,
258
+ action,
259
+ challengeToken: challengeTokenRef.current || challengeToken,
135
260
  executionId,
136
- inputs: {
137
- ...Object.fromEntries(Object.entries(payload.inputs ?? {}).filter(([key]) => !key.startsWith("__consent_opt__"))),
138
- ...!hasUserCodeInput(components) ? { user_code: void 0 } : {}
139
- }
261
+ inputs
140
262
  }
141
263
  }));
142
264
  } catch (flowError) {
143
- handleError(flowError instanceof Error ? flowError : new Error(t("deviceGrant.errors.continue")));
265
+ handleError(toDeviceGrantError(flowError, t("deviceGrant.errors.continue")));
144
266
  } finally {
145
267
  setIsLoading(false);
146
268
  }
147
269
  };
270
+ const timeoutContextRef = useRef({
271
+ components: [],
272
+ hasConsentPrompt: false,
273
+ submit
274
+ });
275
+ timeoutContextRef.current = {
276
+ components,
277
+ hasConsentPrompt: Boolean(additionalData["consentPrompt"]),
278
+ submit
279
+ };
280
+ const timeoutSubmittedForRef = useRef(null);
281
+ useEffect(() => {
282
+ const timeoutMs = Number(additionalData["stepTimeout"]) || 0;
283
+ if (timeoutMs <= 0 || !executionId) {
284
+ setIsTimeoutDisabled(false);
285
+ return;
286
+ }
287
+ const timerId = setTimeout(() => {
288
+ setIsTimeoutDisabled(true);
289
+ const expiredError = new ThunderIDRuntimeError(t("errors.signin.timeout") || "Time allowed to complete the step has expired.", "DEVICE_GRANT_ERROR", "react");
290
+ const context = timeoutContextRef.current;
291
+ const actionId = context.hasConsentPrompt ? findConsentSubmitActionId(context.components) : void 0;
292
+ if (actionId && timeoutSubmittedForRef.current !== timeoutMs) {
293
+ timeoutSubmittedForRef.current = timeoutMs;
294
+ context.submit({
295
+ action: actionId,
296
+ inputs: { [CONSENT_REASON_KEY]: ConsentConstants.REASON_TIMEOUT }
297
+ });
298
+ return;
299
+ }
300
+ handleError(expiredError);
301
+ }, Math.max(0, timeoutMs - Date.now()));
302
+ return () => clearTimeout(timerId);
303
+ }, [
304
+ additionalData,
305
+ executionId,
306
+ handleError,
307
+ t
308
+ ]);
309
+ useOAuthCallback({
310
+ currentExecutionId: executionId || null,
311
+ executionIdStorageKey,
312
+ isInitialized: isInitialized && !sdkLoading && isStorageReady,
313
+ isSubmitting: isLoading,
314
+ onError: (callbackError) => {
315
+ persistExecutionId(null);
316
+ persistChallengeToken(null);
317
+ handleError(toDeviceGrantError(callbackError, String(callbackError)));
318
+ },
319
+ onFlowChange: (response) => void applyResponse(response),
320
+ onSubmit: async ({ executionId: callbackExecutionId, inputs }) => {
321
+ setIsLoading(true);
322
+ try {
323
+ return await executeEmbeddedDeviceGrantFlow({
324
+ baseUrl,
325
+ payload: {
326
+ challengeToken: challengeTokenRef.current || challengeToken,
327
+ executionId: callbackExecutionId,
328
+ inputs
329
+ }
330
+ });
331
+ } finally {
332
+ setIsLoading(false);
333
+ }
334
+ },
335
+ processedRef: oauthCodeProcessedRef,
336
+ setExecutionId: persistExecutionId
337
+ });
148
338
  const handleBaseSubmit = (payload, component) => submit(payload, component.id);
149
339
  const fieldErrors = useMemo(() => {
150
340
  const errors = {};
@@ -160,6 +350,7 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
160
350
  fieldErrors,
161
351
  initialValues,
162
352
  isLoading,
353
+ isTimeoutDisabled,
163
354
  meta,
164
355
  onSubmit: submit,
165
356
  result
@@ -172,6 +363,7 @@ const DeviceGrant = ({ children = void 0, className = "", onComplete = void 0, o
172
363
  error,
173
364
  initialValues,
174
365
  isLoading,
366
+ isTimeoutDisabled,
175
367
  onError: handleError,
176
368
  onSubmit: handleBaseSubmit,
177
369
  preferences,
@@ -1 +1 @@
1
- {"version":3,"file":"DeviceGrant.js","names":["DeviceGrant: FC<DeviceGrantProps>","useThunderID","useTranslation","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.js","names":["parsed: unknown","actions: EmbeddedFlowComponent[]","consentReason: unknown","cleanInputs: Record<string, unknown>","DeviceGrant: FC<DeviceGrantProps>","useThunderID","useTranslation","response: EmbeddedDeviceGrantFlowResponse","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":";;;;;;;;;;;AA+BA,MAAM,sBAAsB,OAAgB,oBAAmC;AAC7E,KAAI,iBAAiB,qBAAqB,iBAAiB,sBAAuB,QAAO;AACzF,QAAO,IAAI,sBACT,iBAAiB,QAAQ,MAAM,UAAU,iBACzC,sBACA,SACA,MACD;;AAoCH,MAAM,oCAA0C;AAC9C,KAAI,CAAC,OAAO,SAAS,KAAM;CAC3B,MAAM,MAAM,IAAI,IAAI,OAAO,SAAS,KAAK;AACzC,KAAI,aAAa,OAAO,YAAY;AACpC,KAAI,aAAa,OAAO,gBAAgB;AACxC,QAAO,QAAQ,aAAa,OAAO,QAAQ,OAAO,IAAI,IAAI,UAAU,CAAC;;AAGvE,MAAM,oBAAoB,eACxB,WAAW,MACR,cACC,UAAU,QAAQ,eAAgB,MAAM,QAAQ,UAAU,WAAW,IAAI,iBAAiB,UAAU,WAAW,CAClH;AAEH,MAAM,cAAc,YAAqC,aAAwD;AAC/G,MAAK,MAAM,aAAa,YAAY;AAClC,MAAI,UAAU,OAAO,SAAU,QAAO;AACtC,MAAI,UAAU,YAAY;GACxB,MAAM,eAAe,WAAW,UAAU,YAAY,SAAS;AAC/D,OAAI,aAAc,QAAO;;;;AAM/B,MAAM,wBAAwB,UAAgD;CAC5E,IAAIA,SAAkB;AACtB,KAAI,OAAO,WAAW,SACpB,KAAI;AACF,WAAS,KAAK,MAAM,OAAO;SACrB;AACN,SAAO;;AAGX,KAAI,MAAM,QAAQ,OAAO,CAAE,QAAO;AAClC,KAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,cAAc,UAAU,MAAM,QAAQ,OAAO,SAAS,CACzG,QAAO,OAAO;AAEhB,QAAO;;AAGT,MAAM,qBAAqB;AAE3B,MAAM,qBAAqB,UACzB,MAAM,SAAS,cAAqC,CAAC,WAAW,GAAG,kBAAkB,UAAU,cAAc,EAAE,CAAC,CAAC,CAAC;AAEpH,MAAM,6BAA6B,UAAuD;CACxF,MAAMC,UAAmC,kBAAkB,MAAM,CAAC,QAC/D,cAAqC,UAAU,MAAM,UAAU,WAAW,aAAa,KAAK,SAC9F;AACD,SACE,QAAQ,MAAM,cAAqC,UAAU,SAAS,aAAa,KAAK,UAAU,IAAI,QAAQ,KAC7G;;AAGL,MAAM,wBACJ,QACA,UACA,YACA,mBAC4B;CAC5B,MAAM,SAAS,WAAW,WAAW,YAAY,SAAS,GAAG;CAC7D,MAAM,WAAW,qBAAqB,eAAe,iBAAiB;CACtE,MAAMC,gBAAyB,OAAO;CACtC,MAAMC,cAAuC,EAAC,GAAG,QAAO;AACxD,QAAO,YAAY;CACnB,MAAM,iBAAiB,QAAQ,UAAU,OAAO,WAAW,aAAa,KAAK,UAAU;CACvF,MAAM,YAAY,kBAAkB,iBAAiB;CACrD,MAAM,SAAS,aAAa,QAAQ,UAAU,kBAAkB,OAAO,SAAS,aAAa,KAAK,UAAU;AAE5G,QAAO,wBAAwB,aAAa,iBAAiB,WAAW,MAAM;EAC5E,UAAU,CAAC;EACX,GAAI,YACA,EAAC,QAAQ,iBAAiB,gBAAe,GACzC,SACE,EAAC,QAAQ,iBAAiB,oBAAmB,GAC7C,EAAE;EACT,CAAC;;;;;AAMJ,MAAMC,eAAqC,EACzC,WAAW,QACX,YAAY,IACZ,aAAa,QACb,UAAU,QACV,cAAc,EAAE,EAChB,OAAO,UACP,UAAU,UACV,GAAG,gBACiC;CACpC,MAAM,EACJ,eACA,SACA,mBACA,eACA,WAAW,YACX,MACA,WACEC,sBAAc;CAClB,MAAM,EAAC,MAAKC,uBAAe,aAAa,KAAK;CAC7C,MAAM,CAAC,YAAY,iBAAiB,SAAkC,EAAE,CAAC;CACzE,MAAM,CAAC,gBAAgB,qBAAqB,SAAkC,EAAE,CAAC;CACjF,MAAM,CAAC,eAAe,oBAAoB,SAAiC,EAAE,CAAC;CAC9E,MAAM,CAAC,mBAAmB,wBAAwB,SAEhD,KAAK;CACP,MAAM,CAAC,aAAa,kBAAkB,SAAS,GAAG;CAClD,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,GAAG;CACxD,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,MAAM;CAC3D,MAAM,oBAAoB,OAAO,GAAG;CACpC,MAAM,CAAC,OAAO,YAAY,SAAuB,KAAK;CACtD,MAAM,CAAC,WAAW,gBAAgB,eAAe;AAC/C,MACE,OAAO,WAAW,eAClB;GAAC;GAAQ;GAAS;GAAQ,CAAC,MAAM,cAAsB,IAAI,IAAI,OAAO,SAAS,KAAK,CAAC,aAAa,IAAI,UAAU,CAAC,CAEjH,QAAO;AAET,MAAI,cAAe,QAAO;AAC1B,SAAO,OAAO,WAAW,eAAe,QAAQ,IAAI,IAAI,OAAO,SAAS,KAAK,CAAC,aAAa,IAAI,gBAAgB,CAAC;GAChH;CACF,MAAM,CAAC,QAAQ,aAAa,SAA4B,KAAK;CAC7D,MAAM,CAAC,mBAAmB,wBAAwB,SAAS,MAAM;CACjE,MAAM,qBAAqB,OAAO,MAAM;CACxC,MAAM,6BAA6B,OAAO,MAAM;CAChD,MAAM,wBAAwB,OAAO,MAAM;CAC3C,MAAM,wBAAwB,GAAG,OAAO;AAExC,iBAAgB;AACd,MAAI,CAAC,iBAAiB,WAAY;AAClC,GAAM,YAA2B;AAC/B,OAAI;IAGF,MAAM,wBADgB,OADC,MAAM,qBAAqB,GACN,kBAAkB,IACjB;AAC7C,QAAI,OAAO,yBAAyB,UAAU;AAC5C,uBAAkB,UAAU;AAC5B,uBAAkB,qBAAqB;;WAEnC,WAEE;AACR,sBAAkB,KAAK;;MAEvB;IACH;EAAC;EAAmB;EAAe;EAAW,CAAC;CAElD,MAAM,qBAAqB,aACxB,OAA4B;AAC3B,iBAAe,MAAM,GAAG;AACxB,MAAI,GAAI,gBAAe,QAAQ,uBAAuB,GAAG;MACpD,gBAAe,WAAW,sBAAsB;IAEvD,CAAC,sBAAsB,CACxB;CAED,MAAM,wBAAwB,YAC5B,OAAO,UAAwC;AAC7C,oBAAkB,UAAU,SAAS;AACrC,oBAAkB,SAAS,GAAG;AAC9B,MAAI;GACF,MAAM,iBAAiB,MAAM,mBAAmB;AAChD,OAAI,eACF,KAAI,MAAO,OAAM,eAAe,0BAA0B,kBAAkB,MAAM;OAC7E,OAAM,eAAe,6BAA6B,iBAAiB;UAEpE;IAIV,CAAC,kBAAkB,CACpB;CAED,MAAM,cAAc,aACjB,cAA2B;AAC1B,gBAAc,EAAE,CAAC;AACjB,oBAAkB,EAAE,CAAC;AACrB,uBAAqB,KAAK;AAC1B,WAAS,UAAU;AACnB,YAAU,UAAU;AACpB,YAAU,UAAU;IAEtB,CAAC,QAAQ,CACV;CAED,MAAM,gBAAgB,YACpB,OAAO,aAA6D;EAClE,MAAM,cACH,SAAS,OAA+C,kBAAkB,SAAS;AACtF,MAAI,SAAS,SAAS,uBAAuB,eAAe,OAAO,gBAAgB,UAAU;AAC3F,sBAAmB,SAAS,eAAe,KAAK;AAChD,SAAM,sBAAsB,SAAS,kBAAkB,KAAK;AAC5D,yBAAsB,aAAa,OAAO;AAC1C;;EAGF,MAAM,aAAa,sBACjB,UACA,GACA;GACE,cAAc;GACd,qBAAqB;GACtB,EACD,KACD;AACD,qBACE,SAAS,eAAe,yBAAyB,aAAc,SAAS,eAAe,OAAQ,KAChG;AACD,QAAM,sBACJ,SAAS,eAAe,yBAAyB,aAAc,SAAS,kBAAkB,OAAQ,KACnG;AACD,gBAAc,WAAW,WAAW;AACpC,oBAAkB,WAAW,eAAe;AAC5C,uBAAqB,MAAM;AAC3B,uBAAqB,SAAS,MAAM,eAAe,KAAK;AACxD,WAAS,SAAS,QAAQ,IAAI,MAAM,oBAAoB,UAAU,EAAE,CAAC,GAAG,KAAK;AAE7E,MAAI,SAAS,eAAe,yBAAyB,UAAU;AAC7D,aAAU,UAAU;AACpB,gBAAa,SAAS;AACtB;;AAGF,MAAI,SAAS,eAAe,yBAAyB,OAAO;GAC1D,MAAM,YAAY,IAAI,sBACpB,oBAAoB,UAAU,GAAG,8BAA8B,EAC/D,sBACA,QACD;AACD,YAAS,UAAU;AACnB,aAAU,UAAU;AACpB,aAAU,UAAU;AACpB;;AAGF,YAAU,KAAK;IAEjB;EAAC;EAAM;EAAY;EAAS;EAAuB;EAAoB;EAAG;EAAO,CAClF;CAED,MAAM,YAAY,YAChB,OAAO,OAAe,aAAqC;AACzD,eAAa,KAAK;AAClB,WAAS,KAAK;AACd,MAAI;GACF,MAAM,uBAAuB,kBAAkB;GAC/C,IAAIC;AACJ,OAAI;AACF,eAAW,MAAM,+BAA+B;KAC9C;KACA,SAAS,WACL;MAAC,aAAa;MAAU,GAAI,uBAAuB,EAAC,gBAAgB,sBAAqB,GAAG,EAAE;MAAE,GAChG;MAAC,eAAe;MAAO,UAAU,iBAAiB;MAAa,QAAQ,EAAE;MAAC;KAC/E,CAAC;YACK,aAAa;AACpB,QAAI,YAAY,uBAAuB,qBAAqB,YAAY,eAAe,KAAK;AAC1F,wBAAmB,KAAK;AACxB,WAAM,sBAAsB,KAAK;AACjC,WAAM,IAAI,sBAAsB,EAAE,8BAA8B,EAAE,sBAAsB,QAAQ;;AAElG,UAAM;;AAER,OAAI,CAAC,SAAU,8BAA6B;AAC5C,SAAM,cAAc,SAAS;WACtB,WAAW;AAClB,eAAY,mBAAmB,WAAW,EAAE,8BAA8B,CAAC,CAAC;YACpE;AACR,gBAAa,MAAM;;IAGvB;EAAC;EAAe;EAAS;EAAa;EAAuB;EAAoB;EAAE,CACpF;AAED,iBAAgB;EACd,MAAM,MAAM,IAAI,IAAI,OAAO,SAAS,KAAK;AACzC,MAAI,CAAC,2BAA2B,SAAS;AACvC,8BAA2B,UAAU;GACrC,MAAM,WAAW,IAAI,aAAa,IAAI,YAAY,IAAI;AACtD,OAAI,SAAU,kBAAiB,EAAC,WAAW,UAAS,CAAC;;EAGvD,MAAM,uBAAuB,IAAI,aAAa,IAAI,gBAAgB;EAClE,MAAM,QAAQ,sBAAsB,MAAM,GAAG,uBAAuB;EACpE,MAAM,oBAAoB,eAAe,QAAQ,sBAAsB;EACvE,MAAM,mBACJ,IAAI,aAAa,IAAI,OAAO,IAAI,IAAI,aAAa,IAAI,QAAQ,IAAI,IAAI,aAAa,IAAI,QAAQ;AAChG,MAAI,CAAC,iBAAiB,cAAc,CAAC,kBAAkB,oBAAoB,sBAAsB,QAAS;AAC1G,MAAI,mBAAmB,QAAS;AAChC,MAAI,CAAC,SAAS,CAAC,mBAAmB;AAChC,sBAAmB,UAAU;AAC7B,eAAY,IAAI,sBAAsB,EAAE,2BAA2B,EAAE,sBAAsB,QAAQ,CAAC;AACpG;;AAGF,qBAAmB,UAAU;AAC7B,EAAK,UAAU,SAAS,IAAI,qBAAqB,OAAU;IAC1D;EAAC;EAAe;EAAuB;EAAa;EAAe;EAAgB;EAAY;EAAW;EAAE,CAAC;CAEhH,MAAM,SAAS,OAAO,SAAoC,aAAqC;AAC7F,eAAa,KAAK;AAClB,WAAS,KAAK;AACd,YAAU,KAAK;AACf,MAAI;AACF,OAAI,CAAC,YAAa;GAElB,MAAM,SAAS,QAAQ,UAAU;GACjC,MAAM,SAAS,qBAAqB,QAAQ,UAAU,EAAE,EAAE,QAAQ,YAAY,eAAe;AAC7F,OAAI,CAAC,iBAAiB,WAAW,CAAE,QAAO,eAAe;AAWzD,SAAM,cATW,MAAM,+BAA+B;IACpD;IACA,SAAS;KACP;KACA,gBAAgB,kBAAkB,WAAW;KAC7C;KACA;KACD;IACF,CAAC,CAC2B;WACtB,WAAW;AAClB,eAAY,mBAAmB,WAAW,EAAE,8BAA8B,CAAC,CAAC;YACpE;AACR,gBAAa,MAAM;;;CAIvB,MAAM,oBAAoB,OAIvB;EAAC,YAAY,EAAE;EAAE,kBAAkB;EAAO;EAAO,CAAC;AACrD,mBAAkB,UAAU;EAAC;EAAY,kBAAkB,QAAQ,eAAe,iBAAiB;EAAE;EAAO;CAC5G,MAAM,yBAAyB,OAAsB,KAAK;AAE1D,iBAAgB;EACd,MAAM,YAAY,OAAO,eAAe,eAAe,IAAI;AAC3D,MAAI,aAAa,KAAK,CAAC,aAAa;AAClC,wBAAqB,MAAM;AAC3B;;EAGF,MAAM,UAAU,iBACR;AACJ,wBAAqB,KAAK;GAC1B,MAAM,eAAe,IAAI,sBACvB,EAAE,wBAAwB,IAAI,kDAC9B,sBACA,QACD;GACD,MAAM,UAAU,kBAAkB;GAClC,MAAM,WAAW,QAAQ,mBAAmB,0BAA0B,QAAQ,WAAW,GAAG;AAC5F,OAAI,YAAY,uBAAuB,YAAY,WAAW;AAC5D,2BAAuB,UAAU;AACjC,IAAK,QAAQ,OAAO;KAAC,QAAQ;KAAU,QAAQ,GAAE,qBAAqB,iBAAiB,gBAAe;KAAC,CAAC;AACxG;;AAEF,eAAY,aAAa;KAE3B,KAAK,IAAI,GAAG,YAAY,KAAK,KAAK,CAAC,CACpC;AAED,eAAa,aAAa,QAAQ;IACjC;EAAC;EAAgB;EAAa;EAAa;EAAE,CAAC;AAEjD,kBAAiB;EACf,oBAAoB,eAAe;EACnC;EACA,eAAe,iBAAiB,CAAC,cAAc;EAC/C,cAAc;EACd,UAAU,kBAA2B;AACnC,sBAAmB,KAAK;AACxB,GAAK,sBAAsB,KAAK;AAChC,eAAY,mBAAmB,eAAe,OAAO,cAAc,CAAC,CAAC;;EAEvE,eAAe,aAA8C,KAAK,cAAc,SAAS;EACzF,UAAU,OAAO,EAAC,aAAa,qBAAqB,aAAY;AAC9D,gBAAa,KAAK;AAClB,OAAI;AACF,WAAO,MAAM,+BAA+B;KAC1C;KACA,SAAS;MACP,gBAAgB,kBAAkB,WAAW;MAC7C,aAAa;MACb;MACD;KACF,CAAC;aACM;AACR,iBAAa,MAAM;;;EAGvB,cAAc;EACd,gBAAgB;EACjB,CAAC;CAEF,MAAM,oBAAoB,SAAoC,cAC5D,OAAO,SAAS,UAAU,GAAG;CAE/B,MAAM,cAAc,cAAsC;EACxD,MAAMC,SAAiC,EAAE;AACzC,GAAC,qBAAqB,EAAE,EAAE,SAAS,eAAsC;AACvE,OAAI,EAAE,WAAW,cAAc,QAAS,QAAO,WAAW,cAAc,WAAW;IACnF;AAEF,SAAO;IACN,CAAC,kBAAkB,CAAC;AAEvB,KAAI,SACF,QACE,4CACG,SAAS;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,UAAU;EACV;EACD,CAAC,GACD;AAIP,QACE,oBAACC;EACC,GAAI;EACO;EACC;EACI;EACT;EACQ;EACJ;EACQ;EACnB,SAAS;EACT,UAAU;EACG;EACL;EACW;EACb;EACG;GACT;;AAIN,0BAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"SignIn.d.ts","sourceRoot":"","sources":["../../../../src/components/auth/SignIn/SignIn.tsx"],"names":[],"mappings":"AAGA,OAAO,EAIL,qBAAqB,EAGrB,yBAAyB,EAIzB,oBAAoB,EACpB,WAAW,EAKZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,EAAE,EAA6C,SAAS,EAAC,MAAM,OAAO,CAAC;AAE/E,OAAmB,EAAC,eAAe,EAAC,MAAM,cAAc,CAAC;AASzD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAErC;;OAEG;IACH,UAAU,EAAE,qBAAqB,EAAE,CAAC;IAEpC;;OAEG;IACH,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAEpB;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpC;;OAEG;IACH,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC;;OAEG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;OAEG;IACH,IAAI,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAElC;;OAEG;IACH,QAAQ,EAAE,CAAC,OAAO,EAAE,yBAAyB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACjE;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC;IAEnD;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAEpD;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;;;;OAKG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;IAEtC;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IAEpC;;OAEG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;CACtC;AAwCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,QAAA,MAAM,MAAM,EAAE,EAAE,CAAC,WAAW,CAw2B3B,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"SignIn.d.ts","sourceRoot":"","sources":["../../../../src/components/auth/SignIn/SignIn.tsx"],"names":[],"mappings":"AAGA,OAAO,EAIL,qBAAqB,EAGrB,yBAAyB,EAIzB,oBAAoB,EACpB,WAAW,EAGZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAC,EAAE,EAA6C,SAAS,EAAC,MAAM,OAAO,CAAC;AAE/E,OAAmB,EAAC,eAAe,EAAC,MAAM,cAAc,CAAC;AASzD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAErC;;OAEG;IACH,UAAU,EAAE,qBAAqB,EAAE,CAAC;IAEpC;;OAEG;IACH,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAEpB;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpC;;OAEG;IACH,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC;;OAEG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;OAEG;IACH,IAAI,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAElC;;OAEG;IACH,QAAQ,EAAE,CAAC,OAAO,EAAE,yBAAyB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACjE;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC;IAEnD;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAEpD;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;;;;OAKG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;IAEtC;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IAEpC;;OAEG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;CACtC;AAwCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,QAAA,MAAM,MAAM,EAAE,EAAE,CAAC,WAAW,CA40B3B,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -6,7 +6,7 @@ import BaseSignIn_default from "./BaseSignIn.js";
6
6
  import { useOAuthCallback } from "../../../hooks/auth/useOAuthCallback.js";
7
7
  import { initiateOAuthRedirect } from "../../../utils/oauth.js";
8
8
  import { handlePasskeyAuthentication, handlePasskeyRegistration } from "../../../utils/passkey.js";
9
- import { CONSENT_INVALID_KEY_PREFIX, CONSENT_MODIFIED_KEY_PREFIX, ConsentConstants, EmbeddedFlowType, EmbeddedSignInFlowStatus, EmbeddedSignInFlowType, ThunderIDAPIError, ThunderIDRuntimeError, getConsentElementDecision, logger } from "@thunderid/browser";
9
+ import { ConsentConstants, EmbeddedFlowType, EmbeddedSignInFlowStatus, EmbeddedSignInFlowType, ThunderIDAPIError, ThunderIDRuntimeError, compileConsentDecisions, logger } from "@thunderid/browser";
10
10
  import { useEffect, useRef, useState } from "react";
11
11
  import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
12
12
 
@@ -405,7 +405,7 @@ const SignIn = ({ className, preferences, size = "medium", onSuccess, onError, v
405
405
  const handleSubmit = async (payload) => {
406
406
  const effectiveExecutionId = payload.executionId || currentExecutionId;
407
407
  if (!effectiveExecutionId) throw new Error("No active flow ID");
408
- const processedInputs = { ...payload.inputs };
408
+ let processedInputs = { ...payload.inputs };
409
409
  const consentReason = processedInputs[CONSENT_REASON_KEY];
410
410
  delete processedInputs[CONSENT_REASON_KEY];
411
411
  if (additionalData?.["consentPrompt"]) try {
@@ -429,25 +429,9 @@ const SignIn = ({ className, preferences, size = "medium", onSuccess, onError, v
429
429
  if (consentReason === ConsentConstants.REASON_TIMEOUT) isDeny = true;
430
430
  if (!isDeny && isConsentApprovalBlocked_default(purposes, processedInputs)) return;
431
431
  const reason = consentReason ?? (isDeny ? ConsentConstants.REASON_USER_DENIED : void 0);
432
- const decisions = {
432
+ processedInputs = compileConsentDecisions(processedInputs, purposes, {
433
433
  approved: !isDeny,
434
- ...reason ? { reason } : {},
435
- purposes: purposes.map((p) => ({
436
- approved: !isDeny,
437
- elements: [...(p.essential || []).map((e) => ({
438
- approved: !isDeny,
439
- name: e.name
440
- })), ...(p.optional || []).map((e) => {
441
- const key = `__consent_opt__${p.purposeId}__${e.name}`;
442
- const approved = !isDeny && processedInputs[key] === "true";
443
- return getConsentElementDecision(p.purposeId, e, approved, processedInputs);
444
- })],
445
- purposeName: p.purposeName
446
- }))
447
- };
448
- processedInputs["consent_decisions"] = JSON.stringify(decisions);
449
- Object.keys(processedInputs).forEach((key) => {
450
- if (key.startsWith("__consent_opt__") || key.startsWith(CONSENT_MODIFIED_KEY_PREFIX) || key.startsWith(CONSENT_INVALID_KEY_PREFIX)) delete processedInputs[key];
434
+ ...reason ? { reason } : {}
451
435
  });
452
436
  } catch (e) {}
453
437
  try {