@stokr/components-library 3.0.27 → 3.0.29

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.
@@ -15,6 +15,7 @@ import { SocialLink, ArrowDown } from "../Footer/FooterMenu.styles.js";
15
15
  import { Collapse } from "react-collapse";
16
16
  import { usePrevious, useMobileView } from "../../utils/customHooks.js";
17
17
  import { checkTodoStatus } from "../../utils/check-todo-status.js";
18
+ import { hasLoggedInSession } from "../../utils/user-identity.js";
18
19
  import { Breakdown } from "../breakdown/Breakdown.js";
19
20
  import { socialSvg } from "../Footer/FooterMenu.js";
20
21
  import { Wrapper, FlexContainer } from "../Grid/Grid.styles.js";
@@ -255,7 +256,7 @@ function HeaderView({
255
256
  }
256
257
  ),
257
258
  isMobile && checkTodoStatus(user) > 0 && !signupFlow && /* @__PURE__ */ jsx(NotificationCounter, { mobile: true, isActive: currentActiveMenu === "main", children: checkTodoStatus(user) }),
258
- /* @__PURE__ */ jsx(HeaderQuickNav, { isUserLogged: user?._id || user?.uid, children: /* @__PURE__ */ jsx(Fragment, { children: user?._id || user?.uid ? /* @__PURE__ */ jsx(Fragment, { children: signupFlow ? /* @__PURE__ */ jsx(Button, { onClick: logoutUser, children: "Log Out" }) : /* @__PURE__ */ jsxs(FlexContainer, { itemsCenter: true, children: [
259
+ /* @__PURE__ */ jsx(HeaderQuickNav, { isUserLogged: hasLoggedInSession(user), children: /* @__PURE__ */ jsx(Fragment, { children: hasLoggedInSession(user) ? /* @__PURE__ */ jsx(Fragment, { children: signupFlow ? /* @__PURE__ */ jsx(Button, { onClick: logoutUser, children: "Log Out" }) : /* @__PURE__ */ jsxs(FlexContainer, { itemsCenter: true, children: [
259
260
  /* @__PURE__ */ jsx(Breakdown, { children: /* @__PURE__ */ jsx(
260
261
  LoginButton,
261
262
  {
@@ -324,7 +325,7 @@ function HeaderView({
324
325
  ),
325
326
  /* @__PURE__ */ jsx(MobileMenuItem, { children: /* @__PURE__ */ jsx(MobileMenuLink, { href: newPlatformUrl + "/team", children: "Team" }) })
326
327
  ] }) }) }) }),
327
- user?._id || user?.uid ? /* @__PURE__ */ jsxs(Fragment, { children: [
328
+ hasLoggedInSession(user) ? /* @__PURE__ */ jsxs(Fragment, { children: [
328
329
  !signupFlow && /* @__PURE__ */ jsx(MobileMenuPart, { withPadding: true, flexColumn: true, borderTop: true, children: /* @__PURE__ */ jsx(MenuNav, { mobile: true, children: /* @__PURE__ */ jsx("ul", { children: isAdmin ? /* @__PURE__ */ jsx(Fragment, { children: isVentureDashboard ? /* @__PURE__ */ jsx(MobileMenuItem, { children: /* @__PURE__ */ jsx("a", { href: "/settings", children: "Settings" }) }) : /* @__PURE__ */ jsx(MobileMenuItem, { children: /* @__PURE__ */ jsx(
329
330
  "a",
330
331
  {
@@ -8,6 +8,7 @@ import { IoniconsStyles } from "../../styles/ioniconsStyles.js";
8
8
  import { getConfig } from "../../runtime-config.js";
9
9
  import { navigateToHref } from "../../routing/navigate-app.js";
10
10
  import { withRouter } from "../../utils/withRouter.js";
11
+ import { hasLoggedInSession } from "../../utils/user-identity.js";
11
12
  class MainMenuClass extends PureComponent {
12
13
  state = {
13
14
  isSettingsActive: false,
@@ -60,7 +61,7 @@ class MainMenuClass extends PureComponent {
60
61
  letterSpacing: "1.5px",
61
62
  cursor: "pointer"
62
63
  };
63
- const isUserLogged = !!user?._id || user?.uid;
64
+ const isUserLogged = hasLoggedInSession(user);
64
65
  return /* @__PURE__ */ jsxs(StyledMainMenu, { children: [
65
66
  /* @__PURE__ */ jsx(IoniconsStyles, {}),
66
67
  /* @__PURE__ */ jsx(Collapse, { isOpened: isMenuActive && isUserLogged, hasNestedCollapse: true, children: /* @__PURE__ */ jsxs(MainMenuContainer, { children: [
package/dist/config.js CHANGED
@@ -1,4 +1,4 @@
1
- import { initFirebase } from "./firebase-config.js";
1
+ import { isValidFirebaseConfig, initFirebase } from "./firebase-config.js";
2
2
  import axiosInstance from "./model/axios.js";
3
3
  import axiosInstance$1 from "./model/axiosPublic.js";
4
4
  import { assignRuntimeConfig } from "./runtime-config.js";
@@ -12,9 +12,14 @@ function configure(config = {}) {
12
12
  if (config.baseUrlPublic != null) {
13
13
  axiosInstance$1.defaults.baseURL = config.baseUrlPublic;
14
14
  }
15
- if (config.firebase) {
16
- _lastFirebaseConfig = config.firebase;
17
- initFirebase(config.firebase);
15
+ if (Object.prototype.hasOwnProperty.call(config, "firebase")) {
16
+ const fb = config.firebase;
17
+ if (fb != null && isValidFirebaseConfig(fb)) {
18
+ _lastFirebaseConfig = fb;
19
+ initFirebase(fb);
20
+ } else {
21
+ _lastFirebaseConfig = null;
22
+ }
18
23
  }
19
24
  }
20
25
  function getLastFirebaseConfig() {
@@ -1,22 +1,23 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import PropTypes from "prop-types";
3
3
  import axiosInstance from "../model/axios.js";
4
+ import { configure } from "../config.js";
4
5
  import React__default, { Component } from "react";
5
6
  import fetchData from "../api/fetchData.js";
6
7
  import { identify, reset } from "../analytics/index.js";
7
8
  import { withRouter } from "../utils/withRouter.js";
8
- import { configure } from "../config.js";
9
- import { getPlatformURL, getConfig } from "../runtime-config.js";
10
- import { isAlreadyOnOnboardingFlow } from "../utils/app-urls.js";
11
9
  import { Text } from "../components/Text/Text.styles.js";
12
- import { getFirebaseAuth } from "../firebase-config.js";
13
- import { Auth } from "./Auth.js";
14
- import { DEFAULT_TOKEN_EXPIRY_MS } from "./Auth.js";
10
+ import { Auth, DEFAULT_TOKEN_EXPIRY_MS } from "./Auth.js";
11
+ import { hasLoggedInSession } from "../utils/user-identity.js";
12
+ import { getConfig, getPlatformURL } from "../runtime-config.js";
15
13
  import { ConfirmModal as ConfirmModalComponent } from "../components/ConfirmModal/ConfirmModal.js";
14
+ import { getFirebaseAuth, isValidFirebaseConfig } from "../firebase-config.js";
16
15
  import avatarPlaceholder from "../static/images/avatar-placeholder.png.js";
16
+ import { isAlreadyOnOnboardingFlow } from "../utils/app-urls.js";
17
17
  import { signOut, getMultiFactorResolver, PhoneMultiFactorGenerator, TotpMultiFactorGenerator, multiFactor } from "firebase/auth";
18
18
  import { navigateToHref } from "../routing/navigate-app.js";
19
19
  const AuthContext = React__default.createContext();
20
+ const FALLBACK_AUTH_CONTEXT_VALUE = { user: null, isFetchingUser: false };
20
21
  const INACTIVITY_TIME_MS = 5 * 60 * 1e3;
21
22
  const INACTIVITY_EVENTS = ["mousemove", "keydown", "click", "scroll", "touchstart"];
22
23
  class AuthProviderClass extends Component {
@@ -134,6 +135,35 @@ class AuthProviderClass extends Component {
134
135
  const { inactivityTimeMs } = this.props;
135
136
  return typeof inactivityTimeMs === "number" && inactivityTimeMs > 0 ? inactivityTimeMs : INACTIVITY_TIME_MS;
136
137
  };
138
+ /** Cookie lifetime for `STOKR_ACCESS_TOKEN` — not the same as inactivity timeout. */
139
+ getAccessTokenExpiryMs = () => {
140
+ const n = Number(this.props.accessTokenExpiryMs);
141
+ return Number.isFinite(n) && n > 0 ? n : DEFAULT_TOKEN_EXPIRY_MS;
142
+ };
143
+ /**
144
+ * Diagnostic: after `Auth.setAccessToken`, the cookie must be readable on this origin
145
+ * or refresh/login will not persist (often misconfigured `cookieDomain`).
146
+ */
147
+ logAccessTokenCookieAfterWrite = (phase) => {
148
+ if (typeof console === "undefined" || !console.log) return;
149
+ const readable = Boolean(Auth.getAccessToken());
150
+ if (readable) {
151
+ console.log(
152
+ `[@stokr/components-library] ${phase}: STOKR_ACCESS_TOKEN cookie readable — next getUser() can authorize API calls.`,
153
+ {
154
+ host: typeof window !== "undefined" ? window.location.hostname : void 0
155
+ }
156
+ );
157
+ } else {
158
+ console.log(
159
+ `[@stokr/components-library] ${phase}: cookie NOT readable after write — session will not survive reloads. Fix cookieDomain (empty string = host-only for localhost).`,
160
+ {
161
+ cookieDomain: getConfig("cookieDomain") ?? "",
162
+ origin: typeof window !== "undefined" ? window.location.origin : void 0
163
+ }
164
+ );
165
+ }
166
+ };
137
167
  resetInactivityTimer = () => {
138
168
  if (this.state.sessionExpiryPendingReason) return;
139
169
  const now = Date.now();
@@ -158,8 +188,8 @@ class AuthProviderClass extends Component {
158
188
  user = await Auth.login(email, password);
159
189
  }
160
190
  const token = await user.getIdToken();
161
- const expiryMs = Number(this.props.accessTokenExpiryMs);
162
- Auth.setAccessToken(token, Number.isFinite(expiryMs) && expiryMs > 0 ? { expiresInMs: expiryMs } : void 0);
191
+ Auth.setAccessToken(token, { expiresInMs: this.getAccessTokenExpiryMs() });
192
+ this.logAccessTokenCookieAfterWrite("loginUser");
163
193
  this.setState({
164
194
  firebaseUser: user
165
195
  });
@@ -232,8 +262,8 @@ class AuthProviderClass extends Component {
232
262
  const userCredential = await mfaResolver.resolveSignIn(multiFactorAssertion);
233
263
  const user = userCredential.user;
234
264
  const token = await user.getIdToken();
235
- const expiryMs = Number(this.props.accessTokenExpiryMs);
236
- Auth.setAccessToken(token, Number.isFinite(expiryMs) && expiryMs > 0 ? { expiresInMs: expiryMs } : void 0);
265
+ Auth.setAccessToken(token, { expiresInMs: this.getAccessTokenExpiryMs() });
266
+ this.logAccessTokenCookieAfterWrite("loginUserWithTotp");
237
267
  this.setState({ firebaseUser: user, waitingFor2fa: false });
238
268
  await this.getUser(token, user);
239
269
  } catch (_error) {
@@ -338,7 +368,10 @@ class AuthProviderClass extends Component {
338
368
  this.setState({ waitingFor2fa: false, firebaseError: null });
339
369
  };
340
370
  patchUserObject = (user, firebaseUser) => {
341
- if (!user || !firebaseUser) return;
371
+ if (!user) return;
372
+ if (!firebaseUser) {
373
+ return { ...user, active: !!user.emailVerified };
374
+ }
342
375
  const merged = Object.assign(Object.create(Object.getPrototypeOf(firebaseUser)), firebaseUser, user);
343
376
  merged.active = merged.emailVerified;
344
377
  return merged;
@@ -366,7 +399,7 @@ class AuthProviderClass extends Component {
366
399
  });
367
400
  if (typeof console !== "undefined" && console.debug) {
368
401
  console.debug(
369
- "[@stokr/components-library] getUser() skipped: no access token. If you expected a session, ensure config.cookieDomain matches this origin (e.g. omit or use empty for localhost)."
402
+ "[@stokr/components-library] getUser: skipped no STOKR_ACCESS_TOKEN (normal when logged out). If login just ran, check loginUser cookie log and cookieDomain."
370
403
  );
371
404
  }
372
405
  return;
@@ -380,23 +413,76 @@ class AuthProviderClass extends Component {
380
413
  const existingFirebaseUser = firebaseUserOverride || this.state.firebaseUser || getFirebaseAuth()?.currentUser;
381
414
  const [result, tokenRes] = await Promise.all([
382
415
  Auth.getUser(),
383
- !existingFirebaseUser ? fetchData("auth/get-custom-token") : Promise.resolve(null)
416
+ !existingFirebaseUser ? fetchData("auth/get-custom-token").catch((e) => {
417
+ const status = e?.response?.status;
418
+ if (status === 401 || status === 403) {
419
+ if (typeof console !== "undefined" && console.warn) {
420
+ console.warn(
421
+ "[@stokr/components-library] auth/get-custom-token rejected (" + status + ") — Firebase session unavailable. STOKR session will be preserved if user/get succeeds."
422
+ );
423
+ }
424
+ if (typeof console !== "undefined" && console.log) {
425
+ console.log(
426
+ "[@stokr/components-library] getUser: auth/get-custom-token failed — trying user/get without Firebase custom token.",
427
+ {
428
+ status
429
+ }
430
+ );
431
+ }
432
+ return null;
433
+ }
434
+ throw e;
435
+ }) : Promise.resolve(null)
384
436
  ]);
385
437
  let firebaseUser = existingFirebaseUser;
386
438
  if (!firebaseUser) {
387
439
  const customToken = tokenRes?.customToken ?? tokenRes?.custom_token ?? tokenRes?.token ?? tokenRes;
388
- firebaseUser = await Auth.signInWithToken(customToken);
440
+ try {
441
+ firebaseUser = await Auth.signInWithToken(customToken);
442
+ } catch (firebaseErr) {
443
+ const fbCode = firebaseErr?.code;
444
+ if (fbCode === "auth/internal-error" || fbCode === "auth/network-request-failed" || fbCode === "auth/invalid-custom-token") {
445
+ if (typeof console !== "undefined" && console.warn) {
446
+ console.warn(
447
+ "[@stokr/components-library] Firebase signIn failed (" + fbCode + ") — session preserved. Add this domain to Firebase Console → Authentication → Authorized domains."
448
+ );
449
+ }
450
+ if (typeof console !== "undefined" && console.log) {
451
+ console.log(
452
+ "[@stokr/components-library] getUser: continuing with API user only (no Firebase user on merged object).",
453
+ {
454
+ code: fbCode
455
+ }
456
+ );
457
+ }
458
+ } else {
459
+ throw firebaseErr;
460
+ }
461
+ }
389
462
  }
390
463
  const user = this.patchUserObject(result.user, firebaseUser);
391
464
  this.checkUserIsValid(user);
392
465
  customValidateGetUser?.(user);
393
- const userAvatar = `${getConfig("photoApiUrl")}/media/picture/view/${user._id}`;
394
- this.checkUserPhoto(userAvatar);
466
+ const userIdForMedia = user._id ?? user.id;
467
+ if (userIdForMedia) {
468
+ const userAvatar = `${getConfig("photoApiUrl")}/media/picture/view/${userIdForMedia}`;
469
+ this.checkUserPhoto(userAvatar);
470
+ }
395
471
  this.setUser(user);
396
472
  this.setState({
397
473
  isFetchingUser: false
398
474
  });
399
- identify(user._id, {
475
+ const identityKey = user._id ?? user.id ?? user.uid;
476
+ if (typeof console !== "undefined" && console.log) {
477
+ console.log(
478
+ "[@stokr/components-library] getUser: user/get succeeded — context User updated; HeaderHo shows Dashboard when hasLoggedInSession is true.",
479
+ {
480
+ identity: identityKey,
481
+ hasLoggedInSession: hasLoggedInSession(user)
482
+ }
483
+ );
484
+ }
485
+ identify(user._id ?? user.id, {
400
486
  role: user.user_type,
401
487
  is_internal: user.email?.endsWith("@stokr.io") ?? false
402
488
  });
@@ -408,13 +494,17 @@ class AuthProviderClass extends Component {
408
494
  });
409
495
  const code = error?.code;
410
496
  const httpStatus = error?.response?.status;
497
+ if (typeof console !== "undefined" && console.log) {
498
+ console.log("[@stokr/components-library] getUser: request failed or validation error", {
499
+ code,
500
+ httpStatus,
501
+ message: error?.message
502
+ });
503
+ }
411
504
  if (code === "auth/internal-error" || code === "auth/network-request-failed") {
412
- Auth.logout();
413
505
  if (typeof console !== "undefined" && console.warn) {
414
506
  console.warn(
415
- "[@stokr/components-library] getUser() failed with",
416
- code,
417
- "— session cleared. Ensure AuthProvider receives config.firebase for this origin and that the domain is authorized in Firebase Console (Authentication → Settings → Authorized domains)."
507
+ "[@stokr/components-library] getUser() encountered Firebase error (" + code + "). Ensure AuthProvider receives config.firebase and the domain is in Firebase Console → Authorized domains."
418
508
  );
419
509
  }
420
510
  return;
@@ -798,8 +888,23 @@ AuthProviderClass.propTypes = {
798
888
  /** Override access token cookie lifetime in ms (e.g. for Storybook). Production uses 1 hour. */
799
889
  accessTokenExpiryMs: PropTypes.number
800
890
  };
891
+ function resolveFirebaseConfigForGuard(props) {
892
+ if (props.config?.firebase != null) return props.config.firebase;
893
+ return getConfig("firebase");
894
+ }
895
+ function AuthProviderGuard(props) {
896
+ if (props.config) {
897
+ configure(props.config);
898
+ }
899
+ const firebase = resolveFirebaseConfigForGuard(props);
900
+ if (!isValidFirebaseConfig(firebase)) {
901
+ return /* @__PURE__ */ jsx(AuthContext.Provider, { value: FALLBACK_AUTH_CONTEXT_VALUE, children: props.children });
902
+ }
903
+ return /* @__PURE__ */ jsx(AuthProviderClass, { ...props });
904
+ }
905
+ AuthProviderGuard.propTypes = AuthProviderClass.propTypes;
801
906
  const AuthConsumer = AuthContext.Consumer;
802
- const AuthProvider = withRouter(AuthProviderClass);
907
+ const AuthProvider = withRouter(AuthProviderGuard);
803
908
  export {
804
909
  AuthConsumer,
805
910
  AuthContext,
@@ -4,7 +4,7 @@ import { getConfig } from "./runtime-config.js";
4
4
  const __vite_import_meta_env__ = {};
5
5
  let app = null;
6
6
  let auth = null;
7
- function isValidConfig(config) {
7
+ function isValidFirebaseConfig(config) {
8
8
  return config?.apiKey != null && String(config.apiKey).trim() !== "";
9
9
  }
10
10
  function getFirebaseAuth() {
@@ -12,7 +12,7 @@ function getFirebaseAuth() {
12
12
  }
13
13
  function initFirebase(configOverride) {
14
14
  const firebaseConfig = configOverride != null ? configOverride : getConfig("firebase");
15
- if (!isValidConfig(firebaseConfig)) {
15
+ if (!isValidFirebaseConfig(firebaseConfig)) {
16
16
  if (configOverride && typeof console !== "undefined" && console.warn) {
17
17
  console.warn(
18
18
  "[firebase-config] initFirebase() received a config with no valid apiKey. Ensure all VITE_FIREBASE_* variables are defined in your .env file and the dev server was restarted."
@@ -49,11 +49,12 @@ function initFirebase(configOverride) {
49
49
  }
50
50
  }
51
51
  }
52
- if (typeof __vite_import_meta_env__ !== "undefined" && isValidConfig(getConfig("firebase"))) {
52
+ if (typeof __vite_import_meta_env__ !== "undefined" && isValidFirebaseConfig(getConfig("firebase"))) {
53
53
  initFirebase();
54
54
  }
55
55
  export {
56
56
  auth,
57
57
  getFirebaseAuth,
58
- initFirebase
58
+ initFirebase,
59
+ isValidFirebaseConfig
59
60
  };
@@ -1,4 +1,4 @@
1
- const __vite_import_meta_env__ = { "BASE_URL": "/", "DEV": false, "MODE": "production", "PROD": true, "SSR": false, "VITE_ADMIN_URL": "https://admin.stokr.info", "VITE_API_URL": "https://platform-api.stokr.info/api/v1", "VITE_BASE_URL_PUBLIC": "https://platform-api-no-auth.stokr.info/api/v1", "VITE_COOKIE_DOMAIN": "stokr.info", "VITE_DASHBOARD_URL": "https://dashboard.stokr.info", "VITE_FIREBASE_API_KEY": "AIzaSyBBp_3Romnfv--YpUuV0mJgDymvSp3oq0c", "VITE_FIREBASE_APP_ID": "1:568229412804:web:2391857e3e2a0b02346e91", "VITE_FIREBASE_AUTH_DOMAIN": "stokr-development-env.firebaseapp.com", "VITE_FIREBASE_MEASUREMENT_ID": "G-CP53SZVSMN", "VITE_FIREBASE_MESSAGING_SENDER_ID": "568229412804", "VITE_FIREBASE_PROJECT_ID": "stokr-development-env", "VITE_FIREBASE_STORAGE_BUCKET": "stokr-development-env.appspot.com", "VITE_MIXPANEL_TOKEN": "a7bb1e881f9b2600762fded84d8ce0ea", "VITE_ONBOARDING_URL": "https://signup.stokr.info", "VITE_PHOTO_API_URL": "https://platform-api.stokr.info/api/v1", "VITE_REGISTER_URL": "https://stokr.info/signup", "VITE_WEBSITE_DOMAIN": "stokr.info" };
1
+ const __vite_import_meta_env__ = { "BASE_URL": "/", "DEV": false, "MODE": "production", "PROD": true, "SSR": false, "VITE_ADMIN_URL": "https://admin.stokr.info", "VITE_API_URL": "https://platform-api.stokr.info/api/v1", "VITE_BASE_URL_PUBLIC": "https://platform-api-no-auth.stokr.info/api/v1", "VITE_COOKIE_DOMAIN": "localhost", "VITE_DASHBOARD_URL": "https://dashboard.stokr.info", "VITE_FIREBASE_API_KEY": "AIzaSyBBp_3Romnfv--YpUuV0mJgDymvSp3oq0c", "VITE_FIREBASE_APP_ID": "1:568229412804:web:2391857e3e2a0b02346e91", "VITE_FIREBASE_AUTH_DOMAIN": "stokr-development-env.firebaseapp.com", "VITE_FIREBASE_MEASUREMENT_ID": "G-CP53SZVSMN", "VITE_FIREBASE_MESSAGING_SENDER_ID": "568229412804", "VITE_FIREBASE_PROJECT_ID": "stokr-development-env", "VITE_FIREBASE_STORAGE_BUCKET": "stokr-development-env.appspot.com", "VITE_MIXPANEL_TOKEN": "a7bb1e881f9b2600762fded84d8ce0ea", "VITE_ONBOARDING_URL": "https://signup.stokr.info", "VITE_PHOTO_API_URL": "https://platform-api.stokr.info/api/v1", "VITE_REGISTER_URL": "https://stokr.info/signup", "VITE_WEBSITE_DOMAIN": "stokr.info" };
2
2
  const _overrides = {};
3
3
  const ENV_KEY_BY_CONFIG = {
4
4
  apiUrl: "VITE_API_URL",
@@ -0,0 +1,10 @@
1
+ function hasLoggedInSession(user) {
2
+ if (user == null || typeof user !== "object") return false;
3
+ if (user._id != null && user._id !== "") return true;
4
+ if (user.uid != null && user.uid !== "") return true;
5
+ if (user.id != null && user.id !== "") return true;
6
+ return false;
7
+ }
8
+ export {
9
+ hasLoggedInSession
10
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stokr/components-library",
3
- "version": "3.0.27",
3
+ "version": "3.0.29",
4
4
  "description": "STOKR - Components Library",
5
5
  "author": "Bilal Hodzic <bilal@stokr.io>",
6
6
  "license": "MIT",