@ollaid/native-sso 2.6.0 → 2.7.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.
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  * } />
10
10
  * ```
11
11
  *
12
- * @version 2.6.0
12
+ * @version 2.7.0
13
13
  */
14
14
  export { NativeSSOPage } from './components/NativeSSOPage';
15
15
  export type { NativeSSOPageProps } from './components/NativeSSOPage';
package/dist/index.js CHANGED
@@ -289,17 +289,18 @@ function OTPInput({
289
289
  disabled = false,
290
290
  autoFocus = true
291
291
  }) {
292
+ const safeLength = Number.isFinite(length) ? Math.max(1, Math.min(12, Math.trunc(length))) : 6;
292
293
  const inputRefs = useRef([]);
293
294
  const [activeIndex, setActiveIndex] = useState(0);
294
295
  useEffect(() => {
295
- inputRefs.current = inputRefs.current.slice(0, length);
296
- }, [length]);
296
+ inputRefs.current = inputRefs.current.slice(0, safeLength);
297
+ }, [safeLength]);
297
298
  useEffect(() => {
298
299
  if (autoFocus && inputRefs.current[0]) inputRefs.current[0].focus();
299
300
  }, [autoFocus]);
300
301
  useEffect(() => {
301
- if (value.length === length && onComplete) onComplete(value);
302
- }, [value, length, onComplete]);
302
+ if (value.length === safeLength && onComplete) onComplete(value);
303
+ }, [value, safeLength, onComplete]);
303
304
  const handleChange = (index, char) => {
304
305
  var _a;
305
306
  if (disabled) return;
@@ -307,8 +308,8 @@ function OTPInput({
307
308
  if (!digit) return;
308
309
  const newValue = value.split("");
309
310
  newValue[index] = digit;
310
- onChange(newValue.join("").slice(0, length));
311
- if (index < length - 1) {
311
+ onChange(newValue.join("").slice(0, safeLength));
312
+ if (index < safeLength - 1) {
312
313
  (_a = inputRefs.current[index + 1]) == null ? void 0 : _a.focus();
313
314
  setActiveIndex(index + 1);
314
315
  }
@@ -331,7 +332,7 @@ function OTPInput({
331
332
  } else if (e.key === "ArrowLeft" && index > 0) {
332
333
  (_b = inputRefs.current[index - 1]) == null ? void 0 : _b.focus();
333
334
  setActiveIndex(index - 1);
334
- } else if (e.key === "ArrowRight" && index < length - 1) {
335
+ } else if (e.key === "ArrowRight" && index < safeLength - 1) {
335
336
  (_c = inputRefs.current[index + 1]) == null ? void 0 : _c.focus();
336
337
  setActiveIndex(index + 1);
337
338
  }
@@ -342,14 +343,14 @@ function OTPInput({
342
343
  e.preventDefault();
343
344
  const pasted = e.clipboardData.getData("text").replace(/[^0-9]/g, "");
344
345
  if (pasted) {
345
- const newValue = pasted.slice(0, length);
346
+ const newValue = pasted.slice(0, safeLength);
346
347
  onChange(newValue);
347
- const focusIndex = Math.min(newValue.length, length - 1);
348
+ const focusIndex = Math.min(newValue.length, safeLength - 1);
348
349
  (_a = inputRefs.current[focusIndex]) == null ? void 0 : _a.focus();
349
350
  setActiveIndex(focusIndex);
350
351
  }
351
352
  };
352
- return /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: "0.5rem" }, children: Array.from({ length }).map((_, index) => /* @__PURE__ */ jsx(
353
+ return /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: "0.5rem" }, children: Array.from({ length: safeLength }).map((_, index) => /* @__PURE__ */ jsx(
353
354
  "input",
354
355
  {
355
356
  ref: (el) => inputRefs.current[index] = el,
@@ -7555,8 +7556,12 @@ function getHeaders(token, includeConfigPrefix = false) {
7555
7556
  let credentials = null;
7556
7557
  let credentialsLoadedAt = 0;
7557
7558
  let credentialsTtl = 300;
7559
+ let refreshInFlight = null;
7560
+ let lastSuccessfulRefreshAt = 0;
7561
+ let lastSuccessfulRefreshResponse = null;
7558
7562
  const DEFAULT_TTL = 300;
7559
7563
  const REFRESH_MARGIN = 30;
7564
+ const REFRESH_COOLDOWN_MS = 60 * 1e3;
7560
7565
  const nativeAuthService = {
7561
7566
  hasCredentials() {
7562
7567
  return credentials !== null;
@@ -7855,6 +7860,16 @@ const nativeAuthService = {
7855
7860
  }
7856
7861
  },
7857
7862
  async refresh() {
7863
+ const now = Date.now();
7864
+ if (refreshInFlight) {
7865
+ return refreshInFlight;
7866
+ }
7867
+ if ((lastSuccessfulRefreshResponse == null ? void 0 : lastSuccessfulRefreshResponse.success) && lastSuccessfulRefreshAt && now - lastSuccessfulRefreshAt < REFRESH_COOLDOWN_MS) {
7868
+ if (isDebugMode()) {
7869
+ console.log("🔄 [SaaS] POST /native/refresh — cooldown actif, réponse réutilisée");
7870
+ }
7871
+ return lastSuccessfulRefreshResponse;
7872
+ }
7858
7873
  const cfg = getNativeAuthConfig();
7859
7874
  if (!cfg.saasApiUrl) {
7860
7875
  throw new ApiError("saasApiUrl non configurée", "unknown");
@@ -7867,51 +7882,62 @@ const nativeAuthService = {
7867
7882
  if (isDebugMode()) {
7868
7883
  console.log("📤 [SaaS] POST /native/refresh");
7869
7884
  }
7870
- let response;
7871
- try {
7872
- response = await fetchWithTimeout(
7873
- `${cfg.saasApiUrl}/native/refresh`,
7874
- {
7875
- method: "POST",
7876
- headers: getHeaders(void 0, true),
7877
- body: JSON.stringify({ refresh_token: refreshToken })
7878
- },
7879
- cfg.timeout || 3e4
7880
- );
7881
- } catch (err) {
7882
- if (err instanceof ApiError) {
7883
- if (err.statusCode === 401) {
7884
- return {
7885
- success: false,
7886
- error_type: err.errorType || "invalid_refresh",
7887
- message: err.message
7888
- };
7885
+ refreshInFlight = (async () => {
7886
+ try {
7887
+ let response;
7888
+ try {
7889
+ response = await fetchWithTimeout(
7890
+ `${cfg.saasApiUrl}/native/refresh`,
7891
+ {
7892
+ method: "POST",
7893
+ headers: getHeaders(void 0, true),
7894
+ body: JSON.stringify({ refresh_token: refreshToken })
7895
+ },
7896
+ cfg.timeout || 3e4
7897
+ );
7898
+ } catch (err) {
7899
+ if (err instanceof ApiError) {
7900
+ if (err.statusCode === 401) {
7901
+ response = {
7902
+ success: false,
7903
+ error_type: err.errorType || "invalid_refresh",
7904
+ message: err.message
7905
+ };
7906
+ } else if (err.statusCode === 404) {
7907
+ response = {
7908
+ success: false,
7909
+ error_type: "not_supported",
7910
+ message: "Endpoint refresh non disponible sur ce SaaS"
7911
+ };
7912
+ } else {
7913
+ throw err;
7914
+ }
7915
+ } else {
7916
+ throw err;
7917
+ }
7889
7918
  }
7890
- if (err.statusCode === 404) {
7891
- return {
7892
- success: false,
7893
- error_type: "not_supported",
7894
- message: "Endpoint refresh non disponible sur ce SaaS"
7895
- };
7919
+ if (response.success) {
7920
+ if (response.token) {
7921
+ setAuthToken(response.token);
7922
+ }
7923
+ const storage = getNativeStorage();
7924
+ if (response.expires_at) storage.setItem(STORAGE.TOKEN_EXPIRES_AT, response.expires_at);
7925
+ if (response.refresh_token) storage.setItem(STORAGE.REFRESH_TOKEN, response.refresh_token);
7926
+ if (response.refresh_expires_at) storage.setItem(STORAGE.REFRESH_EXPIRES_AT, response.refresh_expires_at);
7927
+ if (response.app_access_token_ref) storage.setItem(STORAGE.APP_ACCESS_TOKEN_REF, response.app_access_token_ref);
7928
+ if (response.alias_reference) storage.setItem(STORAGE.ALIAS_REFERENCE, response.alias_reference);
7929
+ if (response.user) {
7930
+ setAuthUser(response.user);
7931
+ }
7932
+ lastSuccessfulRefreshAt = Date.now();
7933
+ lastSuccessfulRefreshResponse = response;
7896
7934
  }
7935
+ return response;
7936
+ } finally {
7937
+ refreshInFlight = null;
7897
7938
  }
7898
- throw err;
7899
- }
7900
- if (response.success) {
7901
- if (response.token) {
7902
- setAuthToken(response.token);
7903
- }
7904
- const storage = getNativeStorage();
7905
- if (response.expires_at) storage.setItem(STORAGE.TOKEN_EXPIRES_AT, response.expires_at);
7906
- if (response.refresh_token) storage.setItem(STORAGE.REFRESH_TOKEN, response.refresh_token);
7907
- if (response.refresh_expires_at) storage.setItem(STORAGE.REFRESH_EXPIRES_AT, response.refresh_expires_at);
7908
- if (response.app_access_token_ref) storage.setItem(STORAGE.APP_ACCESS_TOKEN_REF, response.app_access_token_ref);
7909
- if (response.alias_reference) storage.setItem(STORAGE.ALIAS_REFERENCE, response.alias_reference);
7910
- if (response.user) {
7911
- setAuthUser(response.user);
7912
- }
7913
- }
7914
- return response;
7939
+ })();
7940
+ return refreshInFlight;
7915
7941
  },
7916
7942
  async logout(token) {
7917
7943
  const config2 = getNativeAuthConfig();
@@ -7960,11 +7986,17 @@ const nativeAuthService = {
7960
7986
  clearAuthToken();
7961
7987
  credentials = null;
7962
7988
  credentialsLoadedAt = 0;
7989
+ refreshInFlight = null;
7990
+ lastSuccessfulRefreshAt = 0;
7991
+ lastSuccessfulRefreshResponse = null;
7963
7992
  return { success: true };
7964
7993
  },
7965
7994
  clearCredentials() {
7966
7995
  credentials = null;
7967
7996
  credentialsLoadedAt = 0;
7997
+ refreshInFlight = null;
7998
+ lastSuccessfulRefreshAt = 0;
7999
+ lastSuccessfulRefreshResponse = null;
7968
8000
  },
7969
8001
  // ============================================
7970
8002
  // High-level methods