@stokr/components-library 3.0.77 → 3.1.0-alpha.1

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.
Files changed (74) hide show
  1. package/dist/components/Input/InputPassword.js +3 -5
  2. package/dist/components/Input/OtpInput.js +1 -3
  3. package/dist/components/MenuNav/MenuNav.styles.js +1 -2
  4. package/dist/components/Modal/Modal.styles.js +2 -3
  5. package/dist/components/Newsletter/Newsletter.js +1 -3
  6. package/dist/components/NotificationCounter/NotificationCounter.styles.js +1 -2
  7. package/dist/components/SmartLink/SmartLink.js +0 -2
  8. package/dist/components/StepController/StepController.js +3 -5
  9. package/dist/components/StepController/StepController.styles.js +102 -1
  10. package/dist/components/StepController/StepControllerProgress.js +1 -1
  11. package/dist/components/Switch/Switch.js +1 -3
  12. package/dist/components/ToDoList/ToDoListTask.js +15 -3
  13. package/dist/components/taxId/register-taxid.js +7 -5
  14. package/dist/index.js +67 -141
  15. package/dist/runtime-config.js +4 -70
  16. package/dist/styles/global.js +1 -2
  17. package/dist/utils/check-todo-status.js +1 -2
  18. package/package.json +8 -14
  19. package/dist/api/authenticationApi.js +0 -13
  20. package/dist/auth/index.js +0 -34
  21. package/dist/components/2FA/Connect2FA.js +0 -58
  22. package/dist/components/2FA/EnterCode.js +0 -74
  23. package/dist/components/2FA/InstallAuthApp.js +0 -68
  24. package/dist/components/2FA/ResetCode.js +0 -34
  25. package/dist/components/2FA/Sucess2FA.js +0 -63
  26. package/dist/components/2FA/disable-2fa-flow.js +0 -104
  27. package/dist/components/2FA/enable-2fa-flow.js +0 -167
  28. package/dist/components/2FA/login-with-otp-flow.js +0 -262
  29. package/dist/components/2FA/main-flow.js +0 -273
  30. package/dist/components/Footer/Footer.js +0 -69
  31. package/dist/components/Footer/Footer.styles.js +0 -277
  32. package/dist/components/Footer/FooterLayout.js +0 -150
  33. package/dist/components/Footer/FooterMenu.js +0 -107
  34. package/dist/components/Footer/FooterMenu.styles.js +0 -334
  35. package/dist/components/ForgotPasswordModal/ForgotPasswordModal.js +0 -121
  36. package/dist/components/Header/Header.js +0 -441
  37. package/dist/components/Header/Header.styles.js +0 -568
  38. package/dist/components/Layout/Layout.js +0 -76
  39. package/dist/components/LoginModal/LoginModal.js +0 -165
  40. package/dist/components/MainMenu/DynamicMainMenu.js +0 -38
  41. package/dist/components/MainMenu/MainMenu.js +0 -220
  42. package/dist/components/MainMenu/MainMenu.styles.js +0 -362
  43. package/dist/components/Modal/NewVentureModal/NewVentureModal.js +0 -273
  44. package/dist/components/RegisterConfirmModal/RegisterConfirmModal.js +0 -74
  45. package/dist/components/RegisterConfirmModal/RegisterConfirmModal.styles.js +0 -20
  46. package/dist/components/RegisterModal/RegisterModal.js +0 -278
  47. package/dist/components/ResetConfirmModal/ResetConfirmModal.js +0 -40
  48. package/dist/components/ResetConfirmModal/ResetConfirmModal.styles.js +0 -41
  49. package/dist/components/ResetPasswordModal/ResetPasswordModal.js +0 -145
  50. package/dist/components/StepsProgress/StepIndicator.js +0 -40
  51. package/dist/components/StepsProgress/StepIndicator.styles.js +0 -69
  52. package/dist/components/StepsProgress/StepsProgress.js +0 -67
  53. package/dist/components/StepsProgress/StepsProgress.styles.js +0 -105
  54. package/dist/components/TermsModal/TermsModal.js +0 -145
  55. package/dist/components/TermsModal/_styles.js +0 -313
  56. package/dist/components/VerifyEmailModal/VerifyEmailModal.js +0 -122
  57. package/dist/components/headerHo/HeaderHo.js +0 -707
  58. package/dist/config.js +0 -33
  59. package/dist/context/Auth.js +0 -423
  60. package/dist/context/AuthContext.js +0 -1016
  61. package/dist/firebase-config.js +0 -56
  62. package/dist/hooks/useNewVentureForm.js +0 -235
  63. package/dist/static/images/background3.png.js +0 -4
  64. package/dist/static/images/google_auth.png.js +0 -4
  65. package/dist/static/images/iso27001-black.svg.js +0 -4
  66. package/dist/static/images/mangopay.svg.js +0 -4
  67. package/dist/static/images/social/Facebook_Logo.png.js +0 -4
  68. package/dist/static/images/social/LI-In-Bug.png.js +0 -4
  69. package/dist/static/images/social/Telegram-Logo.png.js +0 -4
  70. package/dist/static/images/social/X-logo-black.png.js +0 -4
  71. package/dist/static/images/social/youtube_social_circle_red.png.js +0 -4
  72. package/dist/utils/show-account-locked-modal.js +0 -23
  73. package/dist/utils/user-identity.js +0 -10
  74. package/dist/utils/withRouter.js +0 -26
@@ -1,1016 +0,0 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import PropTypes from "prop-types";
3
- import axiosInstance from "../model/axios.js";
4
- import { configure } from "../config.js";
5
- import React__default, { Component } from "react";
6
- import fetchData from "../api/fetchData.js";
7
- import { reset, identify } from "../analytics/index.js";
8
- import { withRouter } from "../utils/withRouter.js";
9
- import { Text } from "../components/Text/Text.styles.js";
10
- import { Auth, DEFAULT_TOKEN_EXPIRY_MS } from "./Auth.js";
11
- import stdin_default from "../components/Button/Button.styles.js";
12
- import { hasLoggedInSession } from "../utils/user-identity.js";
13
- import { getConfig, getPlatformURL } from "../runtime-config.js";
14
- import avatarPlaceholder from "../static/images/avatar-placeholder.png.js";
15
- import { getFirebaseAuth, isValidFirebaseConfig } from "../firebase-config.js";
16
- import { isAlreadyOnOnboardingFlow } from "../utils/app-urls.js";
17
- import { signOut, getMultiFactorResolver, PhoneMultiFactorGenerator, TotpMultiFactorGenerator, multiFactor } from "firebase/auth";
18
- import { navigateToHref } from "../routing/navigate-app.js";
19
- import { SuccessModal as SuccessModalComponent } from "../components/Modal/SuccessModal/SuccessModal.js";
20
- const AuthContext = React__default.createContext();
21
- const FALLBACK_AUTH_CONTEXT_VALUE = { user: null, isFetchingUser: false };
22
- const INACTIVITY_TIME_MS = 5 * 60 * 1e3;
23
- const INACTIVITY_WARNING_BEFORE_MS = 30 * 1e3;
24
- const INACTIVITY_EVENTS = ["mousemove", "keydown", "click", "scroll", "touchstart"];
25
- class AuthProviderClass extends Component {
26
- userRef = React__default.createRef(null);
27
- inactivityTimerRef = null;
28
- inactivityWarningTimerRef = null;
29
- cookieExpiryTimerRef = null;
30
- // Timestamp of the last inactivity timer reset; used to throttle the reset
31
- // to at most once per second so frequent DOM events (mousemove fires 30-60×/s)
32
- // don't continuously create and destroy setTimeout objects.
33
- _lastActivityAt = 0;
34
- state = {
35
- user: void 0,
36
- firebaseUser: void 0,
37
- // Start as `true` when a session cookie already exists so the app
38
- // shows a loader immediately instead of flashing the login form
39
- // for a frame before componentDidMount fires getUser().
40
- // When there is no cookie the value stays `false` so the login
41
- // page appears instantly without any loader.
42
- isFetchingUser: !!Auth.getAccessToken(),
43
- avatar: avatarPlaceholder,
44
- userMfaEnrollment: null,
45
- waitingFor2fa: false,
46
- firebaseError: null,
47
- verifyEmailError: null,
48
- isVerifyingEmail: false,
49
- loggedOutDueToInactivity: false,
50
- loggedOutDueToCookieExpiry: false,
51
- /** When set, show session modal but keep session until user dismisses (avoids login + session modals at once). */
52
- sessionExpiryPendingReason: null,
53
- /** Shown ~30s before inactivity logout; user can extend the session from the modal. */
54
- showInactivityWarningModal: false
55
- };
56
- componentDidMount() {
57
- if (this.props.config) {
58
- configure(this.props.config);
59
- if (this.props.config.firebase == null && typeof console !== "undefined" && console.warn) {
60
- console.warn(
61
- "[@stokr/components-library] <AuthProvider> config has no `firebase` property. Auth (login, 2FA, session) may fail with auth/internal-error or auth/network-request-failed. Pass config.firebase with your Firebase project config (apiKey, authDomain, projectId, etc.)."
62
- );
63
- }
64
- } else if (typeof console !== "undefined" && console.warn) {
65
- console.warn(
66
- "[@stokr/components-library] <AuthProvider> rendered without a `config` prop. API URLs, Firebase, and cookie domain will use values baked at library build time. Pass a config prop to use your app’s environment variables at runtime. See: https://github.com/stokr-io/components-library#runtime-config"
67
- );
68
- }
69
- const isActivatingUser = window.location.href.includes("verifyEmail");
70
- if (isActivatingUser) {
71
- this.handleVerifyEmail();
72
- } else if (getFirebaseAuth()) {
73
- this.getUser().catch((err) => {
74
- if (typeof console !== "undefined" && console.debug) {
75
- console.debug("[AuthProvider] getUser() on mount failed:", err?.message || err);
76
- }
77
- });
78
- } else if (typeof console !== "undefined" && console.debug) {
79
- console.debug(
80
- "[@stokr/components-library] getUser() not run on mount: Firebase not initialized. Pass config.firebase to AuthProvider."
81
- );
82
- }
83
- INACTIVITY_EVENTS.forEach((event) => window.addEventListener(event, this.resetInactivityTimer));
84
- }
85
- componentDidUpdate(prevProps, prevState) {
86
- const hadSession = hasLoggedInSession(prevState.user);
87
- const hasSession = hasLoggedInSession(this.state.user);
88
- if (!hadSession && hasSession) {
89
- this.resetInactivityTimer();
90
- this.scheduleCookieExpiryLogout();
91
- } else if (hadSession && !hasSession) {
92
- this.clearInactivityTimer();
93
- this.clearCookieExpiryTimer();
94
- }
95
- const sessionJustEnded = hadSession && !hasSession;
96
- const sessionExpiryJustPending = !prevState.sessionExpiryPendingReason && this.state.sessionExpiryPendingReason;
97
- if (this.state.showInactivityWarningModal && (sessionJustEnded || sessionExpiryJustPending)) {
98
- this.setState({ showInactivityWarningModal: false });
99
- }
100
- }
101
- componentWillUnmount() {
102
- INACTIVITY_EVENTS.forEach((event) => window.removeEventListener(event, this.resetInactivityTimer));
103
- this.clearInactivityTimer();
104
- this.clearCookieExpiryTimer();
105
- if (this.avatarObjectUrl) URL.revokeObjectURL(this.avatarObjectUrl);
106
- }
107
- checkTokenIsValid = (redirect = false) => {
108
- const accessToken = Auth.getAccessToken();
109
- if (!accessToken) {
110
- this.logoutUser(redirect, { dueToCookieExpiry: true });
111
- }
112
- return !!accessToken;
113
- };
114
- clearInactivityWarningTimer = () => {
115
- if (this.inactivityWarningTimerRef) {
116
- clearTimeout(this.inactivityWarningTimerRef);
117
- this.inactivityWarningTimerRef = null;
118
- }
119
- };
120
- clearInactivityTimer = () => {
121
- this.clearInactivityWarningTimer();
122
- if (this.inactivityTimerRef) {
123
- clearTimeout(this.inactivityTimerRef);
124
- this.inactivityTimerRef = null;
125
- }
126
- };
127
- clearCookieExpiryTimer = () => {
128
- if (this.cookieExpiryTimerRef != null) {
129
- clearTimeout(this.cookieExpiryTimerRef);
130
- this.cookieExpiryTimerRef = null;
131
- }
132
- };
133
- scheduleCookieExpiryLogout = () => {
134
- if (this.cookieExpiryTimerRef != null) {
135
- clearTimeout(this.cookieExpiryTimerRef);
136
- this.cookieExpiryTimerRef = null;
137
- }
138
- const { user } = this.state;
139
- if (!hasLoggedInSession(user)) return;
140
- const expiresAt = Auth.getAccessTokenExpiresAt();
141
- if (!expiresAt) return;
142
- const delay = expiresAt - Date.now();
143
- if (delay <= 0) {
144
- Auth.logout();
145
- this.setState({ sessionExpiryPendingReason: "cookie" });
146
- return;
147
- }
148
- this.cookieExpiryTimerRef = setTimeout(() => {
149
- this.cookieExpiryTimerRef = null;
150
- Auth.logout();
151
- this.setState({ sessionExpiryPendingReason: "cookie" });
152
- }, delay);
153
- };
154
- getInactivityTimeMs = () => {
155
- const { inactivityTimeMs } = this.props;
156
- return typeof inactivityTimeMs === "number" && inactivityTimeMs > 0 ? inactivityTimeMs : INACTIVITY_TIME_MS;
157
- };
158
- getInactivityWarningBeforeMs = () => {
159
- const { inactivityWarningBeforeMs } = this.props;
160
- return typeof inactivityWarningBeforeMs === "number" && inactivityWarningBeforeMs > 0 ? inactivityWarningBeforeMs : INACTIVITY_WARNING_BEFORE_MS;
161
- };
162
- /** Cookie lifetime for `STOKR_ACCESS_TOKEN` — not the same as inactivity timeout. */
163
- getAccessTokenExpiryMs = () => {
164
- const n = Number(this.props.accessTokenExpiryMs);
165
- return Number.isFinite(n) && n > 0 ? n : DEFAULT_TOKEN_EXPIRY_MS;
166
- };
167
- /**
168
- * Diagnostic: after `Auth.setAccessToken`, the cookie must be readable on this origin
169
- * or refresh/login will not persist (often misconfigured `cookieDomain`).
170
- */
171
- logAccessTokenCookieAfterWrite = (phase) => {
172
- if (typeof console === "undefined" || !console.log) return;
173
- const readable = Boolean(Auth.getAccessToken());
174
- if (readable) {
175
- console.log(
176
- `[@stokr/components-library] ${phase}: STOKR_ACCESS_TOKEN cookie readable — next getUser() can authorize API calls.`,
177
- {
178
- host: typeof window !== "undefined" ? window.location.hostname : void 0
179
- }
180
- );
181
- } else {
182
- console.log(
183
- `[@stokr/components-library] ${phase}: cookie NOT readable after write — session will not survive reloads. Fix cookieDomain (empty string = host-only for localhost).`,
184
- {
185
- cookieDomain: getConfig("cookieDomain") ?? "",
186
- origin: typeof window !== "undefined" ? window.location.origin : void 0
187
- }
188
- );
189
- }
190
- };
191
- resetInactivityTimer = (force = false) => {
192
- if (this.state.sessionExpiryPendingReason) return;
193
- const { user } = this.state;
194
- if (!hasLoggedInSession(user)) {
195
- this.clearInactivityTimer();
196
- if (this.state.showInactivityWarningModal) {
197
- this.setState({ showInactivityWarningModal: false });
198
- }
199
- return;
200
- }
201
- const now = Date.now();
202
- if (!force && now - this._lastActivityAt < 1e3) return;
203
- this._lastActivityAt = now;
204
- this.clearInactivityTimer();
205
- if (this.state.showInactivityWarningModal) {
206
- this.setState({ showInactivityWarningModal: false });
207
- }
208
- const delay = this.getInactivityTimeMs();
209
- const warningDelay = Math.max(0, delay - this.getInactivityWarningBeforeMs());
210
- this.inactivityWarningTimerRef = setTimeout(() => {
211
- this.inactivityWarningTimerRef = null;
212
- if (!hasLoggedInSession(this.state.user) || this.state.sessionExpiryPendingReason) return;
213
- this.setState({ showInactivityWarningModal: true });
214
- }, warningDelay);
215
- this.inactivityTimerRef = setTimeout(this.handleInactivityTimeout, delay);
216
- };
217
- /** Fires when the inactivity deadline is reached — always closes the warning modal first. */
218
- handleInactivityTimeout = () => {
219
- this.inactivityTimerRef = null;
220
- this.clearInactivityWarningTimer();
221
- if (!hasLoggedInSession(this.state.user)) {
222
- this.setState({ showInactivityWarningModal: false });
223
- return;
224
- }
225
- Auth.logout();
226
- this.setState({
227
- sessionExpiryPendingReason: "inactivity",
228
- showInactivityWarningModal: false
229
- });
230
- };
231
- /** User chose to stay signed in from the pre-logout warning modal. */
232
- extendSessionFromInactivityWarning = () => {
233
- this.setState({ showInactivityWarningModal: false }, () => {
234
- this.resetInactivityTimer(true);
235
- });
236
- };
237
- /** Close the pre-logout warning without extending the session (logout still scheduled). */
238
- dismissInactivityWarningModal = () => {
239
- this.setState({ showInactivityWarningModal: false });
240
- };
241
- loginUser = async (email, password, customToken) => {
242
- try {
243
- let user;
244
- if (customToken) {
245
- user = await Auth.signInWithToken(customToken);
246
- } else {
247
- user = await Auth.login(email, password);
248
- }
249
- const token = await user.getIdToken();
250
- Auth.setAccessToken(token, { expiresInMs: this.getAccessTokenExpiryMs() });
251
- this.logAccessTokenCookieAfterWrite("loginUser");
252
- this.setState({
253
- firebaseUser: user
254
- });
255
- await this.getUser(token, user);
256
- return user;
257
- } catch (error) {
258
- console.log("🚀 ~ file: AuthContext.js:56 ~ error:", error);
259
- const httpStatus = error?.response?.status;
260
- if (httpStatus != null) {
261
- const auth = getFirebaseAuth();
262
- if (auth) signOut(auth).catch(() => {
263
- });
264
- Auth.logout();
265
- this.setState({ firebaseUser: null });
266
- if (httpStatus === 401 || httpStatus === 403) {
267
- error.message = "Your account was authenticated but the server rejected the session. Please verify that the API and Firebase environments match, or contact support.";
268
- }
269
- throw error;
270
- }
271
- switch (error.code) {
272
- case "auth/internal-error":
273
- error.message = "Authentication service error. Ensure AuthProvider receives config.firebase and your domain is authorized in Firebase Console.";
274
- throw error;
275
- case "auth/multi-factor-auth-required":
276
- this.setState({ waitingFor2fa: true, firebaseError: error });
277
- throw error;
278
- case "auth/requires-recent-login":
279
- await this.logoutUser();
280
- error.message = "Your session has expired. Please log in again to continue.";
281
- throw error;
282
- case "auth/invalid-login-credentials":
283
- case "auth/invalid-credential":
284
- if (email) {
285
- Auth.reportFailedLoginAttempt(email).catch(() => {
286
- });
287
- }
288
- error.message = "The credentials are not correct. Try again?";
289
- throw error;
290
- case "auth/user-disabled":
291
- error.message = "Your account has been locked due to too many failed login attempts. Please contact support at support.stokr.io to reactivate.";
292
- throw error;
293
- case "auth/too-many-requests":
294
- error.message = "Too many sign-in attempts. Please wait a few minutes before trying again.";
295
- throw error;
296
- case "auth/invalid-custom-token":
297
- case "auth/argument-error":
298
- error.message = "This sign-in link is invalid, expired, or was already used. Please sign in again or request a new link.";
299
- throw error;
300
- case "auth/network-request-failed":
301
- error.message = "Unable to reach the authentication server. Check your connection and try again.";
302
- throw error;
303
- default:
304
- console.log({ error });
305
- throw error;
306
- }
307
- }
308
- };
309
- createUser = async (email, password) => {
310
- try {
311
- await Auth.createUser(email, password);
312
- } catch (error) {
313
- console.log("🚀 ~ file: AuthContext.js:56 ~ error:", error);
314
- throw error;
315
- }
316
- };
317
- loginUserWithTotp = async (verificationCode, error) => {
318
- const auth = getFirebaseAuth();
319
- if (!auth) {
320
- throw new Error("Firebase is not configured.");
321
- }
322
- const mfaResolver = getMultiFactorResolver(auth, error);
323
- const selectedIndex = 0;
324
- switch (mfaResolver.hints[selectedIndex].factorId) {
325
- case TotpMultiFactorGenerator.FACTOR_ID: {
326
- const otpFromAuthenticator = verificationCode;
327
- const multiFactorAssertion = TotpMultiFactorGenerator.assertionForSignIn(
328
- mfaResolver.hints[selectedIndex].uid,
329
- otpFromAuthenticator
330
- );
331
- try {
332
- const userCredential = await mfaResolver.resolveSignIn(multiFactorAssertion);
333
- const user = userCredential.user;
334
- const token = await user.getIdToken();
335
- Auth.setAccessToken(token, { expiresInMs: this.getAccessTokenExpiryMs() });
336
- this.logAccessTokenCookieAfterWrite("loginUserWithTotp");
337
- this.setState({ firebaseUser: user, waitingFor2fa: false });
338
- await this.getUser(token, user);
339
- } catch (_error) {
340
- console.log({ _error });
341
- throw _error;
342
- }
343
- break;
344
- }
345
- case PhoneMultiFactorGenerator.FACTOR_ID:
346
- break;
347
- }
348
- };
349
- generateTotpSecret = async (user = getFirebaseAuth()?.currentUser, appName = "STOKR") => {
350
- try {
351
- const multiFactorSession = await multiFactor(user).getSession();
352
- const totpSecret = await TotpMultiFactorGenerator.generateSecret(multiFactorSession);
353
- const totpUri = totpSecret.generateQrCodeUrl(user.email, appName);
354
- return { totpUri, totpSecret };
355
- } catch (error) {
356
- console.log("🚀 ~ file: AuthContext.js:51 ~ error:", error);
357
- throw error;
358
- }
359
- };
360
- unenrollUser2FA = async () => {
361
- const { userMfaEnrollment } = this.state;
362
- if (!userMfaEnrollment || userMfaEnrollment.length === 0) {
363
- console.log("User is not enrolled in 2FA");
364
- return;
365
- }
366
- const firebaseUser = this.state.firebaseUser || getFirebaseAuth()?.currentUser;
367
- if (!firebaseUser) throw new Error("Firebase user not available for MFA unenroll.");
368
- try {
369
- for (let i = 0; i < userMfaEnrollment.length; i++) {
370
- await multiFactor(firebaseUser).unenroll(userMfaEnrollment[i].uid);
371
- }
372
- } catch (error) {
373
- console.log({ error });
374
- throw error;
375
- }
376
- };
377
- checkMfaEnrollment = (user = getFirebaseAuth()?.currentUser) => {
378
- const checkMfaEnrollment = multiFactor(user).enrolledFactors;
379
- this.setState({
380
- userMfaEnrollment: checkMfaEnrollment
381
- });
382
- return checkMfaEnrollment;
383
- };
384
- enrollUserToTotp = async (user, totpSecret, verificationCode, displayName) => {
385
- const multiFactorAssertion = TotpMultiFactorGenerator.assertionForEnrollment(totpSecret, verificationCode);
386
- await multiFactor(user).enroll(multiFactorAssertion, displayName);
387
- };
388
- logoutUser = async (redirect = false, options = {}) => {
389
- console.log("🚀 ~ file: AuthContext.js:303 ~ logoutUser ~ redirect:", redirect);
390
- this.clearCookieExpiryTimer();
391
- this.clearInactivityTimer();
392
- try {
393
- const auth = getFirebaseAuth();
394
- if (auth) signOut(auth).catch(() => {
395
- });
396
- Auth.logout();
397
- } catch (_e) {
398
- }
399
- delete axiosInstance.defaults.headers.common.Authorization;
400
- reset();
401
- this.userRef.current = null;
402
- if (this.avatarObjectUrl) {
403
- URL.revokeObjectURL(this.avatarObjectUrl);
404
- this.avatarObjectUrl = void 0;
405
- }
406
- this.setState({
407
- user: null,
408
- firebaseUser: null,
409
- firebaseError: null,
410
- verifyEmailError: null,
411
- userMfaEnrollment: null,
412
- waitingFor2fa: false,
413
- isVerifyingEmail: false,
414
- avatar: avatarPlaceholder,
415
- isFetchingUser: false,
416
- sessionExpiryPendingReason: null,
417
- showInactivityWarningModal: false,
418
- loggedOutDueToInactivity: options.dueToInactivity === true,
419
- loggedOutDueToCookieExpiry: options.dueToCookieExpiry === true
420
- });
421
- if (redirect) {
422
- const home = getPlatformURL();
423
- navigateToHref(this.props.navigate, home);
424
- }
425
- };
426
- clearLoggedOutDueToInactivity = () => {
427
- this.setState({ loggedOutDueToInactivity: false });
428
- };
429
- clearLoggedOutDueToCookieExpiry = () => {
430
- this.setState({ loggedOutDueToCookieExpiry: false });
431
- };
432
- /** Dismiss session modal: if expiry was deferred, perform logout; otherwise clear post-logout flags only. */
433
- dismissSessionExpiryModal = () => {
434
- if (this.state.sessionExpiryPendingReason) {
435
- this.logoutUser(false, {});
436
- return;
437
- }
438
- this.clearLoggedOutDueToInactivity();
439
- this.clearLoggedOutDueToCookieExpiry();
440
- };
441
- /** Call when user cancels the 2FA step (e.g. goes back to login to try another account). */
442
- reset2faFlow = () => {
443
- this.setState({ waitingFor2fa: false, firebaseError: null });
444
- };
445
- patchUserObject = (user, firebaseUser) => {
446
- if (!user) return;
447
- if (!firebaseUser) {
448
- return { ...user, active: !!user.emailVerified };
449
- }
450
- const merged = Object.assign(Object.create(Object.getPrototypeOf(firebaseUser)), firebaseUser, user);
451
- merged.active = merged.emailVerified;
452
- return merged;
453
- };
454
- // Fetch the authenticated user's avatar from service-media as a Blob and expose it as an
455
- // object URL. Falls back to the placeholder on 404/error. The endpoint requires the Bearer
456
- // token, so it can't be used directly as an <img src> — hence the blob round-trip.
457
- loadAvatar = async () => {
458
- try {
459
- const blob = await Auth.getAvatar();
460
- this.setAvatarObjectUrl(blob ? URL.createObjectURL(blob) : avatarPlaceholder);
461
- } catch (error) {
462
- console.log("error loading avatar: ", error);
463
- this.setAvatarObjectUrl(avatarPlaceholder);
464
- }
465
- };
466
- setAvatarObjectUrl = (nextAvatar) => {
467
- if (this.avatarObjectUrl && this.avatarObjectUrl !== nextAvatar) {
468
- URL.revokeObjectURL(this.avatarObjectUrl);
469
- }
470
- this.avatarObjectUrl = nextAvatar?.startsWith("blob:") ? nextAvatar : void 0;
471
- this.setState({ avatar: nextAvatar });
472
- };
473
- // Kept for backward compatibility; refetches the current user's avatar.
474
- checkUserPhoto = () => this.loadAvatar();
475
- getUser = async (accessToken = Auth.getAccessToken(), firebaseUserOverride) => {
476
- if (!accessToken) {
477
- this.setState({
478
- isFetchingUser: false,
479
- user: null,
480
- firebaseUser: null
481
- });
482
- if (typeof console !== "undefined" && console.debug) {
483
- console.debug(
484
- "[@stokr/components-library] getUser: skipped — no STOKR_ACCESS_TOKEN (normal when logged out). If login just ran, check loginUser cookie log and cookieDomain."
485
- );
486
- }
487
- return;
488
- }
489
- this.setState({
490
- isFetchingUser: true
491
- });
492
- axiosInstance.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
493
- const { customValidateGetUser } = this.props;
494
- try {
495
- const existingFirebaseUser = firebaseUserOverride || this.state.firebaseUser || getFirebaseAuth()?.currentUser;
496
- const [result, tokenRes] = await Promise.all([
497
- Auth.getUser(),
498
- !existingFirebaseUser ? fetchData("auth/get-custom-token").catch((e) => {
499
- const status = e?.response?.status;
500
- if (status === 401 || status === 403) {
501
- if (typeof console !== "undefined" && console.warn) {
502
- console.warn(
503
- "[@stokr/components-library] auth/get-custom-token rejected (" + status + ") — Firebase session unavailable. STOKR session will be preserved if user/get succeeds."
504
- );
505
- }
506
- if (typeof console !== "undefined" && console.log) {
507
- console.log(
508
- "[@stokr/components-library] getUser: auth/get-custom-token failed — trying user/get without Firebase custom token.",
509
- {
510
- status
511
- }
512
- );
513
- }
514
- return null;
515
- }
516
- throw e;
517
- }) : Promise.resolve(null)
518
- ]);
519
- let firebaseUser = existingFirebaseUser;
520
- if (!firebaseUser) {
521
- const customToken = tokenRes?.customToken ?? tokenRes?.custom_token ?? tokenRes?.token ?? tokenRes;
522
- try {
523
- firebaseUser = await Auth.signInWithToken(customToken);
524
- } catch (firebaseErr) {
525
- const fbCode = firebaseErr?.code;
526
- if (fbCode === "auth/internal-error" || fbCode === "auth/network-request-failed" || fbCode === "auth/invalid-custom-token") {
527
- if (typeof console !== "undefined" && console.warn) {
528
- console.warn(
529
- "[@stokr/components-library] Firebase signIn failed (" + fbCode + ") — session preserved. Add this domain to Firebase Console → Authentication → Authorized domains."
530
- );
531
- }
532
- if (typeof console !== "undefined" && console.log) {
533
- console.log(
534
- "[@stokr/components-library] getUser: continuing with API user only (no Firebase user on merged object).",
535
- {
536
- code: fbCode
537
- }
538
- );
539
- }
540
- } else {
541
- throw firebaseErr;
542
- }
543
- }
544
- }
545
- const user = this.patchUserObject(result.user, firebaseUser);
546
- this.checkUserIsValid(user);
547
- customValidateGetUser?.(user);
548
- this.loadAvatar();
549
- this.setUser(user);
550
- this.setState({
551
- isFetchingUser: false
552
- });
553
- const identityKey = user._id ?? user.id ?? user.uid;
554
- if (typeof console !== "undefined" && console.log) {
555
- console.log(
556
- "[@stokr/components-library] getUser: user/get succeeded — context User updated; HeaderHo shows Dashboard when hasLoggedInSession is true.",
557
- {
558
- identity: identityKey,
559
- hasLoggedInSession: hasLoggedInSession(user)
560
- }
561
- );
562
- }
563
- identify(user._id ?? user.id, {
564
- role: user.user_type,
565
- is_internal: user.email?.endsWith("@stokr.io") ?? false
566
- });
567
- this.replaceLocationPathName();
568
- return user;
569
- } catch (error) {
570
- this.setState({
571
- isFetchingUser: false
572
- });
573
- const code = error?.code;
574
- const httpStatus = error?.response?.status;
575
- if (typeof console !== "undefined" && console.log) {
576
- console.log("[@stokr/components-library] getUser: request failed or validation error", {
577
- code,
578
- httpStatus,
579
- message: error?.message
580
- });
581
- }
582
- if (code === "auth/internal-error" || code === "auth/network-request-failed") {
583
- if (typeof console !== "undefined" && console.warn) {
584
- console.warn(
585
- "[@stokr/components-library] getUser() encountered Firebase error (" + code + "). Ensure AuthProvider receives config.firebase and the domain is in Firebase Console → Authorized domains."
586
- );
587
- }
588
- return;
589
- }
590
- if (httpStatus === 401 || httpStatus === 403) {
591
- Auth.logout();
592
- this.setState({ user: null, firebaseUser: null });
593
- if (typeof console !== "undefined" && console.warn) {
594
- console.warn(
595
- "[@stokr/components-library] getUser() rejected by server (" + httpStatus + ") — stale session cleared."
596
- );
597
- }
598
- return;
599
- }
600
- console.log(`Error in getUser: ${error}`);
601
- throw error;
602
- }
603
- };
604
- checkUserIsValid = (user) => {
605
- if (!user) {
606
- throw new Error("User is not defined");
607
- }
608
- const isOnVerifyEmailPage = window.location.href.includes("verifyEmail");
609
- if (isOnVerifyEmailPage) {
610
- return true;
611
- }
612
- if (!user?.emailVerified && !isAlreadyOnOnboardingFlow()) {
613
- navigateToHref(this.props.navigate, `${getConfig("onboardingUrl")}/resend-activation-email`);
614
- return false;
615
- } else if (!user?.country && user?.user_type === "investor" && !isAlreadyOnOnboardingFlow()) {
616
- navigateToHref(this.props.navigate, `${getConfig("onboardingUrl")}/welcome`);
617
- return false;
618
- }
619
- return true;
620
- };
621
- uploadPhoto = async (file) => {
622
- try {
623
- const { user } = this.state;
624
- if (!user || !file) {
625
- return;
626
- }
627
- const formData = new FormData();
628
- formData.append("file", file);
629
- const blob = await Auth.uploadPhoto(formData);
630
- this.setAvatarObjectUrl(blob ? URL.createObjectURL(blob) : avatarPlaceholder);
631
- return blob;
632
- } catch (error) {
633
- console.log(`Error: ${error}`);
634
- throw error;
635
- }
636
- };
637
- deletePhoto = async () => {
638
- const { user } = this.state;
639
- if (!user) {
640
- return;
641
- }
642
- await Auth.deletePhoto();
643
- this.setAvatarObjectUrl(avatarPlaceholder);
644
- };
645
- replaceLocationPathName = () => {
646
- const pathname = this.props.location?.pathname ?? "";
647
- if (pathname === "/login" || pathname === "/signup") {
648
- this.props.navigate("/");
649
- }
650
- };
651
- updateUser = async (data) => {
652
- try {
653
- const existingFirebaseUser = this.state.firebaseUser;
654
- const [{ user }, tokenRes] = await Promise.all([
655
- Auth.updateUser(data),
656
- !existingFirebaseUser ? fetchData("auth/get-custom-token") : Promise.resolve(null)
657
- ]);
658
- let firebaseUser = existingFirebaseUser;
659
- if (!firebaseUser) {
660
- const customToken = tokenRes?.customToken ?? tokenRes?.custom_token ?? tokenRes?.token ?? tokenRes;
661
- firebaseUser = await Auth.signInWithToken(customToken);
662
- }
663
- const newUser = this.patchUserObject(user, firebaseUser);
664
- this.setUser(newUser);
665
- return user;
666
- } catch (error) {
667
- console.log(`Error updating the user: ${error}`);
668
- throw error;
669
- }
670
- };
671
- checkIfPrivateInvestor = async (project, user = this.state.user, returnData = false) => {
672
- if (!user || !project) {
673
- return;
674
- }
675
- try {
676
- let dataToSend = {
677
- userId: user._id,
678
- projectId: project._id
679
- };
680
- const isPrivateInvestor = await Auth.checkPrivateInvestor(dataToSend);
681
- const userCopy = { ...user };
682
- userCopy.isPrivateInvestor = isPrivateInvestor.isAllowed;
683
- userCopy.privateInvestorStatus = isPrivateInvestor.status;
684
- if (returnData) {
685
- return userCopy;
686
- } else {
687
- this.setState({ user: userCopy });
688
- }
689
- } catch (error) {
690
- console.log("🚀 ~ error in checkPrivateInvestor", error);
691
- throw error;
692
- }
693
- };
694
- checkPrivateInvestorAll = async (user = this.state.user, returnData = false) => {
695
- try {
696
- const { list } = await Auth.checkPrivateInvestor();
697
- if (list) {
698
- const userCopy = { ...user };
699
- userCopy.privateInvestorProjects = list;
700
- if (returnData) {
701
- return userCopy;
702
- } else {
703
- this.setState({ user: userCopy });
704
- }
705
- }
706
- } catch (error) {
707
- console.log("🚀 ~ error in checkPrivateInvestorAll", error);
708
- throw error;
709
- }
710
- };
711
- checkIfUserSubscribed = async (project, user = this.state.user, returnData = false) => {
712
- if (!user || !project) {
713
- return;
714
- }
715
- try {
716
- let dataToSend = {
717
- listName: project.name,
718
- email: user.email
719
- };
720
- const isSubscribed = await Auth.checkIfUserSubscribed(dataToSend);
721
- const userCopy = { ...user };
722
- userCopy.isSubscribed = isSubscribed;
723
- if (returnData) {
724
- return userCopy;
725
- } else {
726
- this.setState({ user: userCopy });
727
- }
728
- } catch (error) {
729
- console.log("🚀 ~ error in checkIfUserSubscribed", error);
730
- throw error;
731
- }
732
- };
733
- checkIfUserSubscribedAll = async (user = this.state.user, returnData = false) => {
734
- if (!user) {
735
- return;
736
- }
737
- try {
738
- let dataToSend = {
739
- email: user.email
740
- };
741
- const result = await Auth.checkIfUserSubscribed(dataToSend);
742
- const userCopy = { ...user };
743
- userCopy.subscribedProjects = result;
744
- if (returnData) {
745
- return userCopy;
746
- } else {
747
- this.setState({ user: userCopy });
748
- }
749
- } catch (error) {
750
- console.log("🚀 ~ error in checkIfUserSubscribed", error);
751
- throw error;
752
- }
753
- };
754
- handleVerifyEmail = async () => {
755
- this.setState({ isVerifyingEmail: true });
756
- const { location } = this.props;
757
- const query = new URLSearchParams(location.search);
758
- try {
759
- let oobCode = query.get("oobCode");
760
- await Auth.handleVerifyEmail(oobCode);
761
- this.setState({ isVerifyingEmail: false });
762
- let customToken = query.get("customtoken");
763
- if (customToken) {
764
- await this.loginUser(null, null, customToken);
765
- }
766
- } catch (error) {
767
- switch (error?.code) {
768
- case "auth/expired-action-code": {
769
- error.message = "The activation link has expired. We are sending you a new one right now.";
770
- let email = query.get("email");
771
- if (email) {
772
- error.email = email;
773
- }
774
- break;
775
- }
776
- case "auth/invalid-action-code":
777
- error.message = "This link is invalid. Please try verifying email again.";
778
- break;
779
- case "auth/user-disabled":
780
- error.message = "Your account has been disabled. Please contact our support.";
781
- break;
782
- }
783
- console.log("🚀 ~ file: AuthContext.js:446 ~ error:", error);
784
- this.setState({
785
- verifyEmailError: error,
786
- isVerifyingEmail: false,
787
- isFetchingUser: false
788
- });
789
- }
790
- };
791
- handleResetPassword = async (password) => {
792
- const { location } = this.props;
793
- try {
794
- const query = new URLSearchParams(location.search);
795
- let oobCode = query.get("oobCode");
796
- await Auth.handleResetPassword(oobCode, password);
797
- } catch (error) {
798
- switch (error?.code) {
799
- case "auth/expired-action-code":
800
- error.message = "This link has expired. Please try reseting password again.";
801
- break;
802
- case "auth/invalid-action-code":
803
- error.message = "This link is invalid. Please try reseting password again.";
804
- break;
805
- case "auth/user-disabled":
806
- error.message = "Your account has been disabled. Please contact our support.";
807
- break;
808
- }
809
- throw error;
810
- }
811
- };
812
- sendWelcomeEmail = async () => {
813
- try {
814
- await Auth.sendWelcomeEmail();
815
- } catch (error) {
816
- console.log(`Error sending welcome email: ${error}`);
817
- }
818
- };
819
- uploaProofOfAddress = async (data, ip) => {
820
- try {
821
- const user = await Auth.uploaProofOfAddress(data, ip);
822
- this.setUser(user);
823
- } catch (error) {
824
- console.log(`Error uploading proof of address: ${error}`);
825
- throw error;
826
- }
827
- };
828
- validateGreenWalletId = async (id) => {
829
- try {
830
- return await Auth.validateGreenWalletId(id);
831
- } catch (error) {
832
- console.log(`Error validating Green Wallet ID: ${error}`);
833
- throw error;
834
- }
835
- };
836
- createWallet = async (data) => {
837
- try {
838
- const wallet = await Auth.createWallet(data);
839
- return wallet;
840
- } catch (error) {
841
- console.log(`Error creating wallet: ${error}`);
842
- throw error;
843
- }
844
- };
845
- setUser = (user) => {
846
- this.userRef.current = user;
847
- this.setState({ user });
848
- };
849
- fetchLastTime = async (projectName, page, userId) => {
850
- try {
851
- let filterBody = {
852
- projectName,
853
- page
854
- };
855
- if (userId) {
856
- filterBody.userId = userId;
857
- }
858
- let response = await Auth.fetchTrackingUserLastTime({
859
- filter: filterBody
860
- });
861
- return response;
862
- } catch (error) {
863
- console.log(" error", error);
864
- }
865
- };
866
- render() {
867
- const { children } = this.props;
868
- return /* @__PURE__ */ jsxs(
869
- AuthContext.Provider,
870
- {
871
- value: {
872
- ...this.state,
873
- userRef: this.userRef,
874
- checkUserIsValid: this.checkUserIsValid,
875
- checkTokenIsValid: this.checkTokenIsValid,
876
- checkUserPhoto: this.checkUserPhoto,
877
- uploadPhoto: this.uploadPhoto,
878
- deletePhoto: this.deletePhoto,
879
- unenrollUser2FA: this.unenrollUser2FA,
880
- loginUserWithTotp: this.loginUserWithTotp,
881
- checkMfaEnrollment: this.checkMfaEnrollment,
882
- generateTotpSecret: this.generateTotpSecret,
883
- enrollUserToTotp: this.enrollUserToTotp,
884
- loginUser: this.loginUser,
885
- logoutUser: this.logoutUser,
886
- getUser: this.getUser,
887
- updateUser: this.updateUser,
888
- checkIfPrivateInvestor: this.checkIfPrivateInvestor,
889
- checkPrivateInvestorAll: this.checkPrivateInvestorAll,
890
- checkIfUserSubscribed: this.checkIfUserSubscribed,
891
- checkIfUserSubscribedAll: this.checkIfUserSubscribedAll,
892
- sendWelcomeEmail: this.sendWelcomeEmail,
893
- resendActivationEmail: Auth.resendActivationEmail,
894
- uploaProofOfAddress: this.uploaProofOfAddress,
895
- validateGreenWalletId: this.validateGreenWalletId,
896
- fetchLastTime: this.fetchLastTime,
897
- createWallet: this.createWallet,
898
- requestUpdateEmail: Auth.requestUpdateEmail,
899
- updatePassword: Auth.updateUserPassword,
900
- createUser: this.createUser,
901
- updateFirebaseUser: Auth.updateFirebaseUser,
902
- handleResetPassword: this.handleResetPassword,
903
- handleVerifyEmail: this.handleVerifyEmail,
904
- setUser: this.setUser,
905
- refreshIdToken: Auth.refreshIdToken,
906
- clearLoggedOutDueToInactivity: this.clearLoggedOutDueToInactivity,
907
- clearLoggedOutDueToCookieExpiry: this.clearLoggedOutDueToCookieExpiry,
908
- dismissSessionExpiryModal: this.dismissSessionExpiryModal,
909
- extendSessionFromInactivityWarning: this.extendSessionFromInactivityWarning,
910
- dismissInactivityWarningModal: this.dismissInactivityWarningModal,
911
- reset2faFlow: this.reset2faFlow
912
- },
913
- children: [
914
- children,
915
- !this.props.hideInactivityModal && this.state.showInactivityWarningModal && /* @__PURE__ */ jsx(
916
- SuccessModalComponent,
917
- {
918
- isOpen: hasLoggedInSession(this.state.user) && !this.state.sessionExpiryPendingReason,
919
- onClose: this.dismissInactivityWarningModal,
920
- variant: "progress",
921
- title: "Session is about to expire",
922
- subtitle: "Your session will expire due to inactivity. Click below to stay signed in.",
923
- content: /* @__PURE__ */ jsx(Text, { children: /* @__PURE__ */ jsx(stdin_default, { onClick: this.extendSessionFromInactivityWarning, children: "To continue, press here" }) }),
924
- maxWidth: "600px"
925
- }
926
- ),
927
- (this.state.loggedOutDueToInactivity || this.state.loggedOutDueToCookieExpiry || this.state.sessionExpiryPendingReason && hasLoggedInSession(this.state.user)) && !this.props.hideInactivityModal && /* @__PURE__ */ jsx(
928
- SuccessModalComponent,
929
- {
930
- isOpen: true,
931
- onClose: this.dismissSessionExpiryModal,
932
- variant: "progress",
933
- title: "Session expired",
934
- content: /* @__PURE__ */ jsxs(Text, { children: [
935
- /* @__PURE__ */ jsx("p", { style: { fontSize: 16 }, children: this.state.sessionExpiryPendingReason === "inactivity" || this.state.loggedOutDueToInactivity ? "You have been logged out due to inactivity." : "Your session has expired. Please log in again." }),
936
- /* @__PURE__ */ jsx(
937
- stdin_default,
938
- {
939
- onClick: () => {
940
- this.dismissSessionExpiryModal();
941
- this.props.onSessionExpiredLoginClick?.();
942
- },
943
- children: "Log in"
944
- }
945
- )
946
- ] }),
947
- maxWidth: "600px"
948
- }
949
- )
950
- ]
951
- }
952
- );
953
- }
954
- }
955
- AuthProviderClass.propTypes = {
956
- children: PropTypes.node.isRequired,
957
- /** Runtime config for the library. Sets API URLs, Firebase config, cookie domain, etc.
958
- * When omitted, the library falls back to Vite env via getConfig() (Storybook / dev).
959
- * When consumed as an npm package, pass this prop so values are available at runtime. */
960
- config: PropTypes.shape({
961
- apiUrl: PropTypes.string,
962
- baseUrlPublic: PropTypes.string,
963
- cookieDomain: PropTypes.string,
964
- websiteDomain: PropTypes.string,
965
- /** Full base URL for the onboarding / sign-up app (e.g. `'https://signup.stokr.io'` or `'https://stokr.io/signin'`). */
966
- onboardingUrl: PropTypes.string,
967
- /** Full base URL for the investor dashboard (e.g. `'https://dashboard.stokr.io'`). */
968
- dashboardUrl: PropTypes.string,
969
- /** Full base URL for the admin / venture dashboard (e.g. `'https://admin.stokr.io'`). */
970
- adminUrl: PropTypes.string,
971
- /** URL for the registration entry point (e.g. `'https://stokr.io/signup'`). */
972
- registerUrl: PropTypes.string,
973
- firebase: PropTypes.shape({
974
- apiKey: PropTypes.string,
975
- authDomain: PropTypes.string,
976
- projectId: PropTypes.string,
977
- storageBucket: PropTypes.string,
978
- messagingSenderId: PropTypes.string,
979
- appId: PropTypes.string,
980
- measurementId: PropTypes.string
981
- })
982
- }),
983
- /** Override inactivity timeout in ms (e.g. for Storybook). Production uses 5 minutes. */
984
- inactivityTimeMs: PropTypes.number,
985
- /** How long before inactivity logout to show the warning modal (ms). Default 30 seconds. */
986
- inactivityWarningBeforeMs: PropTypes.number,
987
- /** When true, the inactivity modal is not shown after auto-logout. Default false. */
988
- hideInactivityModal: PropTypes.bool,
989
- /** Override access token cookie lifetime in ms (e.g. for Storybook). Production uses 1 hour. */
990
- accessTokenExpiryMs: PropTypes.number,
991
- /** A function to be called when the session expires. Default undefined. */
992
- onSessionExpiredLoginClick: PropTypes.func
993
- };
994
- function resolveFirebaseConfigForGuard(props) {
995
- if (props.config?.firebase != null) return props.config.firebase;
996
- return getConfig("firebase");
997
- }
998
- function AuthProviderGuard(props) {
999
- if (props.config) {
1000
- configure(props.config);
1001
- }
1002
- const firebase = resolveFirebaseConfigForGuard(props);
1003
- if (!isValidFirebaseConfig(firebase)) {
1004
- return /* @__PURE__ */ jsx(AuthContext.Provider, { value: FALLBACK_AUTH_CONTEXT_VALUE, children: props.children });
1005
- }
1006
- return /* @__PURE__ */ jsx(AuthProviderClass, { ...props });
1007
- }
1008
- AuthProviderGuard.propTypes = AuthProviderClass.propTypes;
1009
- const AuthConsumer = AuthContext.Consumer;
1010
- const AuthProvider = withRouter(AuthProviderGuard);
1011
- export {
1012
- AuthConsumer,
1013
- AuthContext,
1014
- AuthProvider,
1015
- DEFAULT_TOKEN_EXPIRY_MS
1016
- };