@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.
- package/dist/cjs/components/auth/DeviceGrant/DeviceGrant.cjs +220 -28
- package/dist/cjs/components/auth/DeviceGrant/DeviceGrant.cjs.map +1 -1
- package/dist/cjs/components/auth/SignIn/SignIn.cjs +3 -19
- package/dist/cjs/components/auth/SignIn/SignIn.cjs.map +1 -1
- package/dist/cjs/index.cjs +1 -1
- package/dist/components/auth/DeviceGrant/DeviceGrant.d.ts +2 -0
- package/dist/components/auth/DeviceGrant/DeviceGrant.d.ts.map +1 -1
- package/dist/components/auth/DeviceGrant/DeviceGrant.js +221 -29
- package/dist/components/auth/DeviceGrant/DeviceGrant.js.map +1 -1
- package/dist/components/auth/SignIn/SignIn.d.ts.map +1 -1
- package/dist/components/auth/SignIn/SignIn.js +4 -20
- package/dist/components/auth/SignIn/SignIn.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
|
@@ -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 {
|
|
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
|
-
|
|
52
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
89
|
-
|
|
199
|
+
throw resumeError;
|
|
200
|
+
}
|
|
201
|
+
if (!resumeId) clearVerificationParameters();
|
|
202
|
+
await applyResponse(response);
|
|
90
203
|
} catch (flowError) {
|
|
91
|
-
handleError(flowError
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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":"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"}
|
|
@@ -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,
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|