@tern-secure/nextjs 3.2.34 → 3.2.35

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.
@@ -36,6 +36,8 @@ function TernSecureClientProvider({
36
36
  }) {
37
37
  const auth = (0, import_react.useMemo)(() => import_client_init.ternSecureAuth, []);
38
38
  const router = (0, import_navigation.useRouter)();
39
+ const pathname = (0, import_navigation.usePathname)();
40
+ const intervalRef = (0, import_react.useRef)(null);
39
41
  const [authState, setAuthState] = (0, import_react.useState)(() => ({
40
42
  userId: null,
41
43
  isLoaded: false,
@@ -44,6 +46,10 @@ function TernSecureClientProvider({
44
46
  token: null
45
47
  }));
46
48
  const handleSignOut = (0, import_react.useCallback)(async (error) => {
49
+ if (intervalRef.current) {
50
+ clearInterval(intervalRef.current);
51
+ intervalRef.current = null;
52
+ }
47
53
  await auth.signOut();
48
54
  setAuthState({
49
55
  isLoaded: true,
@@ -71,6 +77,16 @@ function TernSecureClientProvider({
71
77
  }
72
78
  return { isValid: false, token: null, userId: null };
73
79
  }, [handleSignOut]);
80
+ const startTokenCheckInterval = (0, import_react.useCallback)(() => {
81
+ if (intervalRef.current) {
82
+ clearInterval(intervalRef.current);
83
+ }
84
+ if (pathname !== loginPath) {
85
+ intervalRef.current = setInterval(() => {
86
+ checkTokenValidity(auth.currentUser);
87
+ }, 3e4);
88
+ }
89
+ }, [auth, checkTokenValidity, loginPath, pathname]);
74
90
  const handleAuthStateChange = (0, import_react.useCallback)(async (user) => {
75
91
  const { isValid, token, userId } = await checkTokenValidity(user);
76
92
  setAuthState({
@@ -83,19 +99,23 @@ function TernSecureClientProvider({
83
99
  if (onUserChanged) {
84
100
  await onUserChanged(user);
85
101
  }
86
- if (!isValid) {
102
+ if (!isValid && pathname !== loginPath) {
87
103
  router.push(loginPath);
88
104
  }
89
- }, [checkTokenValidity, onUserChanged, router, loginPath]);
105
+ if (user && isValid) {
106
+ startTokenCheckInterval();
107
+ } else if (intervalRef.current) {
108
+ clearInterval(intervalRef.current);
109
+ intervalRef.current = null;
110
+ }
111
+ }, [checkTokenValidity, onUserChanged, router, loginPath, pathname, startTokenCheckInterval]);
90
112
  (0, import_react.useEffect)(() => {
91
113
  const unsubscribeAuthState = (0, import_auth.onAuthStateChanged)(auth, handleAuthStateChange);
92
- handleAuthStateChange(auth.currentUser);
93
- const intervalId = setInterval(() => {
94
- handleAuthStateChange(auth.currentUser);
95
- }, 3e4);
96
114
  return () => {
97
115
  unsubscribeAuthState();
98
- clearInterval(intervalId);
116
+ if (intervalRef.current) {
117
+ clearInterval(intervalRef.current);
118
+ }
99
119
  };
100
120
  }, [auth, handleAuthStateChange]);
101
121
  const contextValue = (0, import_react.useMemo)(() => ({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport React, { useState, useEffect, useMemo, useCallback } from 'react'\r\nimport { ternSecureAuth } from '../utils/client-init'\r\nimport { User, onAuthStateChanged } from \"firebase/auth\"\r\nimport { TernSecureCtx, TernSecureState, TernSecureCtxValue } from './TernSecureCtx'\r\nimport { useRouter } from 'next/navigation'\r\nimport { verifyTernIdToken } from '../app-router/server/sessionTernSecure'\r\n\r\ninterface TernSecureClientProviderProps {\r\n children: React.ReactNode;\r\n onUserChanged?: (user: User | null) => Promise<void>;\r\n loginPath?: string;\r\n}\r\n\r\nexport function TernSecureClientProvider({ \r\n children, \r\n onUserChanged,\r\n loginPath = '/sign-in'\r\n}: TernSecureClientProviderProps) {\r\n const auth = useMemo(() => ternSecureAuth, []);\r\n const router = useRouter();\r\n\r\n const [authState, setAuthState] = useState<TernSecureState>(() => ({\r\n userId: null,\r\n isLoaded: false,\r\n error: null,\r\n isValid: false,\r\n token: null\r\n }));\r\n\r\n const handleSignOut = useCallback(async (error?: Error) => {\r\n await auth.signOut();\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n error: error || null,\r\n isValid: false,\r\n token: null\r\n });\r\n router.push(loginPath);\r\n }, [auth, router, loginPath]);\r\n\r\n const checkTokenValidity = useCallback(async (user: User | null) => {\r\n if (user) {\r\n try {\r\n const token = await user.getIdToken(true);\r\n const decodedToken = await verifyTernIdToken(token);\r\n const isValid = decodedToken.valid\r\n\r\n if(isValid) {\r\n return { isValid: true, token, userId: user.uid };\r\n }\r\n } catch (error) {\r\n console.error('Token validation error:', error);\r\n await handleSignOut(error instanceof Error ? error : new Error('Authentication token is invalid'));\r\n return { isValid: false, token: null, userId: null };\r\n }\r\n }\r\n return { isValid: false, token: null, userId: null };\r\n }, [handleSignOut]);\r\n\r\n const handleAuthStateChange = useCallback(async (user: User | null) => {\r\n const { isValid, token, userId } = await checkTokenValidity(user);\r\n \r\n setAuthState({\r\n isLoaded: true,\r\n userId,\r\n isValid,\r\n token,\r\n error: null\r\n });\r\n\r\n if (onUserChanged) {\r\n await onUserChanged(user);\r\n }\r\n\r\n if (!isValid) {\r\n router.push(loginPath);\r\n }\r\n }, [checkTokenValidity, onUserChanged, router, loginPath]);\r\n\r\n useEffect(() => {\r\n const unsubscribeAuthState = onAuthStateChanged(auth, handleAuthStateChange);\r\n \r\n // Initial check\r\n handleAuthStateChange(auth.currentUser);\r\n\r\n // Set up an interval to periodically check token validity\r\n const intervalId = setInterval(() => {\r\n handleAuthStateChange(auth.currentUser);\r\n }, 30000); // Check every 30 seconds\r\n\r\n return () => {\r\n unsubscribeAuthState();\r\n clearInterval(intervalId);\r\n };\r\n }, [auth, handleAuthStateChange]);\r\n\r\n const contextValue: TernSecureCtxValue = useMemo(() => ({\r\n ...authState,\r\n checkTokenValidity: () => handleAuthStateChange(auth.currentUser),\r\n signOut: handleSignOut,\r\n }), [authState, handleAuthStateChange, auth, handleSignOut]);\r\n\r\n return (\r\n <TernSecureCtx.Provider value={contextValue}>\r\n {children}\r\n </TernSecureCtx.Provider>\r\n );\r\n}"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA0GI;AAxGJ,mBAAiE;AACjE,yBAA+B;AAC/B,kBAAyC;AACzC,2BAAmE;AACnE,wBAA0B;AAC1B,+BAAkC;AAQ3B,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AAAA,EACA,YAAY;AACd,GAAkC;AAChC,QAAM,WAAO,sBAAQ,MAAM,mCAAgB,CAAC,CAAC;AAC7C,QAAM,aAAS,6BAAU;AAEzB,QAAM,CAAC,WAAW,YAAY,QAAI,uBAA0B,OAAO;AAAA,IACjE,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,OAAO;AAAA,IACP,SAAS;AAAA,IACT,OAAO;AAAA,EACT,EAAE;AAEF,QAAM,oBAAgB,0BAAY,OAAO,UAAkB;AACzD,UAAM,KAAK,QAAQ;AACnB,iBAAa;AAAA,MACX,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,OAAO,SAAS;AAAA,MAChB,SAAS;AAAA,MACT,OAAO;AAAA,IACT,CAAC;AACD,WAAO,KAAK,SAAS;AAAA,EACvB,GAAG,CAAC,MAAM,QAAQ,SAAS,CAAC;AAE5B,QAAM,yBAAqB,0BAAY,OAAO,SAAsB;AAClE,QAAI,MAAM;AACR,UAAI;AACF,cAAM,QAAQ,MAAM,KAAK,WAAW,IAAI;AACxC,cAAM,eAAe,UAAM,4CAAkB,KAAK;AAClD,cAAM,UAAU,aAAa;AAE7B,YAAG,SAAS;AACV,iBAAO,EAAE,SAAS,MAAM,OAAO,QAAQ,KAAK,IAAI;AAAA,QAClD;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,2BAA2B,KAAK;AAC9C,cAAM,cAAc,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,iCAAiC,CAAC;AACjG,eAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ,KAAK;AAAA,MACrD;AAAA,IACF;AACA,WAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ,KAAK;AAAA,EACrD,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,4BAAwB,0BAAY,OAAO,SAAsB;AACrE,UAAM,EAAE,SAAS,OAAO,OAAO,IAAI,MAAM,mBAAmB,IAAI;AAEhE,iBAAa;AAAA,MACX,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IACT,CAAC;AAED,QAAI,eAAe;AACjB,YAAM,cAAc,IAAI;AAAA,IAC1B;AAEA,QAAI,CAAC,SAAS;AACZ,aAAO,KAAK,SAAS;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,oBAAoB,eAAe,QAAQ,SAAS,CAAC;AAEzD,8BAAU,MAAM;AACd,UAAM,2BAAuB,gCAAmB,MAAM,qBAAqB;AAG3E,0BAAsB,KAAK,WAAW;AAGtC,UAAM,aAAa,YAAY,MAAM;AACnC,4BAAsB,KAAK,WAAW;AAAA,IACxC,GAAG,GAAK;AAER,WAAO,MAAM;AACX,2BAAqB;AACrB,oBAAc,UAAU;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,MAAM,qBAAqB,CAAC;AAEhC,QAAM,mBAAmC,sBAAQ,OAAO;AAAA,IACtD,GAAG;AAAA,IACH,oBAAoB,MAAM,sBAAsB,KAAK,WAAW;AAAA,IAChE,SAAS;AAAA,EACX,IAAI,CAAC,WAAW,uBAAuB,MAAM,aAAa,CAAC;AAE3D,SACE,4CAAC,mCAAc,UAAd,EAAuB,OAAO,cAC5B,UACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport React, { useState, useEffect, useMemo, useCallback, useRef } from 'react'\r\nimport { ternSecureAuth } from '../utils/client-init'\r\nimport { User, onAuthStateChanged } from \"firebase/auth\"\r\nimport { TernSecureCtx, TernSecureState, TernSecureCtxValue } from './TernSecureCtx'\r\nimport { useRouter, usePathname } from 'next/navigation'\r\nimport { verifyTernIdToken } from '../app-router/server/sessionTernSecure'\r\n\r\ninterface TernSecureClientProviderProps {\r\n children: React.ReactNode;\r\n onUserChanged?: (user: User | null) => Promise<void>;\r\n loginPath?: string;\r\n}\r\n\r\nexport function TernSecureClientProvider({ \r\n children, \r\n onUserChanged,\r\n loginPath = '/sign-in'\r\n}: TernSecureClientProviderProps) {\r\n const auth = useMemo(() => ternSecureAuth, []);\r\n const router = useRouter();\r\n const pathname = usePathname();\r\n const intervalRef = useRef<NodeJS.Timeout | null>(null);\r\n\r\n const [authState, setAuthState] = useState<TernSecureState>(() => ({\r\n userId: null,\r\n isLoaded: false,\r\n error: null,\r\n isValid: false,\r\n token: null\r\n }));\r\n\r\n const handleSignOut = useCallback(async (error?: Error) => {\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n intervalRef.current = null;\r\n }\r\n await auth.signOut();\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n error: error || null,\r\n isValid: false,\r\n token: null\r\n });\r\n router.push(loginPath);\r\n }, [auth, router, loginPath]);\r\n\r\n const checkTokenValidity = useCallback(async (user: User | null) => {\r\n if (user) {\r\n try {\r\n const token = await user.getIdToken(true);\r\n const decodedToken = await verifyTernIdToken(token);\r\n const isValid = decodedToken.valid\r\n\r\n if(isValid) {\r\n return { isValid: true, token, userId: user.uid };\r\n }\r\n } catch (error) {\r\n console.error('Token validation error:', error);\r\n await handleSignOut(error instanceof Error ? error : new Error('Authentication token is invalid'));\r\n return { isValid: false, token: null, userId: null };\r\n }\r\n }\r\n return { isValid: false, token: null, userId: null };\r\n }, [handleSignOut]);\r\n\r\n const startTokenCheckInterval = useCallback(() => {\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n }\r\n if (pathname !== loginPath) {\r\n intervalRef.current = setInterval(() => {\r\n checkTokenValidity(auth.currentUser);\r\n }, 30000); // Check every 30 seconds\r\n }\r\n }, [auth, checkTokenValidity, loginPath, pathname]);\r\n\r\n const handleAuthStateChange = useCallback(async (user: User | null) => {\r\n const { isValid, token, userId } = await checkTokenValidity(user);\r\n \r\n setAuthState({\r\n isLoaded: true,\r\n userId,\r\n isValid,\r\n token,\r\n error: null\r\n });\r\n\r\n if (onUserChanged) {\r\n await onUserChanged(user);\r\n }\r\n\r\n if (!isValid && pathname !== loginPath) {\r\n router.push(loginPath);\r\n }\r\n\r\n if (user && isValid) {\r\n startTokenCheckInterval();\r\n } else if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n intervalRef.current = null;\r\n }\r\n }, [checkTokenValidity, onUserChanged, router, loginPath, pathname, startTokenCheckInterval]);\r\n\r\n useEffect(() => {\r\n const unsubscribeAuthState = onAuthStateChanged(auth, handleAuthStateChange);\r\n \r\n return () => {\r\n unsubscribeAuthState();\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n }\r\n };\r\n }, [auth, handleAuthStateChange]);\r\n\r\n const contextValue: TernSecureCtxValue = useMemo(() => ({\r\n ...authState,\r\n checkTokenValidity: () => handleAuthStateChange(auth.currentUser),\r\n signOut: handleSignOut,\r\n }), [authState, handleAuthStateChange, auth, handleSignOut]);\r\n\r\n return (\r\n <TernSecureCtx.Provider value={contextValue}>\r\n {children}\r\n </TernSecureCtx.Provider>\r\n );\r\n}\r\n\r\n"],"mappings":"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","names":[]}
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
- import { useState, useEffect, useMemo, useCallback } from "react";
3
+ import { useState, useEffect, useMemo, useCallback, useRef } from "react";
4
4
  import { ternSecureAuth } from "../utils/client-init";
5
5
  import { onAuthStateChanged } from "firebase/auth";
6
6
  import { TernSecureCtx } from "./TernSecureCtx";
7
- import { useRouter } from "next/navigation";
7
+ import { useRouter, usePathname } from "next/navigation";
8
8
  import { verifyTernIdToken } from "../app-router/server/sessionTernSecure";
9
9
  function TernSecureClientProvider({
10
10
  children,
@@ -13,6 +13,8 @@ function TernSecureClientProvider({
13
13
  }) {
14
14
  const auth = useMemo(() => ternSecureAuth, []);
15
15
  const router = useRouter();
16
+ const pathname = usePathname();
17
+ const intervalRef = useRef(null);
16
18
  const [authState, setAuthState] = useState(() => ({
17
19
  userId: null,
18
20
  isLoaded: false,
@@ -21,6 +23,10 @@ function TernSecureClientProvider({
21
23
  token: null
22
24
  }));
23
25
  const handleSignOut = useCallback(async (error) => {
26
+ if (intervalRef.current) {
27
+ clearInterval(intervalRef.current);
28
+ intervalRef.current = null;
29
+ }
24
30
  await auth.signOut();
25
31
  setAuthState({
26
32
  isLoaded: true,
@@ -48,6 +54,16 @@ function TernSecureClientProvider({
48
54
  }
49
55
  return { isValid: false, token: null, userId: null };
50
56
  }, [handleSignOut]);
57
+ const startTokenCheckInterval = useCallback(() => {
58
+ if (intervalRef.current) {
59
+ clearInterval(intervalRef.current);
60
+ }
61
+ if (pathname !== loginPath) {
62
+ intervalRef.current = setInterval(() => {
63
+ checkTokenValidity(auth.currentUser);
64
+ }, 3e4);
65
+ }
66
+ }, [auth, checkTokenValidity, loginPath, pathname]);
51
67
  const handleAuthStateChange = useCallback(async (user) => {
52
68
  const { isValid, token, userId } = await checkTokenValidity(user);
53
69
  setAuthState({
@@ -60,19 +76,23 @@ function TernSecureClientProvider({
60
76
  if (onUserChanged) {
61
77
  await onUserChanged(user);
62
78
  }
63
- if (!isValid) {
79
+ if (!isValid && pathname !== loginPath) {
64
80
  router.push(loginPath);
65
81
  }
66
- }, [checkTokenValidity, onUserChanged, router, loginPath]);
82
+ if (user && isValid) {
83
+ startTokenCheckInterval();
84
+ } else if (intervalRef.current) {
85
+ clearInterval(intervalRef.current);
86
+ intervalRef.current = null;
87
+ }
88
+ }, [checkTokenValidity, onUserChanged, router, loginPath, pathname, startTokenCheckInterval]);
67
89
  useEffect(() => {
68
90
  const unsubscribeAuthState = onAuthStateChanged(auth, handleAuthStateChange);
69
- handleAuthStateChange(auth.currentUser);
70
- const intervalId = setInterval(() => {
71
- handleAuthStateChange(auth.currentUser);
72
- }, 3e4);
73
91
  return () => {
74
92
  unsubscribeAuthState();
75
- clearInterval(intervalId);
93
+ if (intervalRef.current) {
94
+ clearInterval(intervalRef.current);
95
+ }
76
96
  };
77
97
  }, [auth, handleAuthStateChange]);
78
98
  const contextValue = useMemo(() => ({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport React, { useState, useEffect, useMemo, useCallback } from 'react'\r\nimport { ternSecureAuth } from '../utils/client-init'\r\nimport { User, onAuthStateChanged } from \"firebase/auth\"\r\nimport { TernSecureCtx, TernSecureState, TernSecureCtxValue } from './TernSecureCtx'\r\nimport { useRouter } from 'next/navigation'\r\nimport { verifyTernIdToken } from '../app-router/server/sessionTernSecure'\r\n\r\ninterface TernSecureClientProviderProps {\r\n children: React.ReactNode;\r\n onUserChanged?: (user: User | null) => Promise<void>;\r\n loginPath?: string;\r\n}\r\n\r\nexport function TernSecureClientProvider({ \r\n children, \r\n onUserChanged,\r\n loginPath = '/sign-in'\r\n}: TernSecureClientProviderProps) {\r\n const auth = useMemo(() => ternSecureAuth, []);\r\n const router = useRouter();\r\n\r\n const [authState, setAuthState] = useState<TernSecureState>(() => ({\r\n userId: null,\r\n isLoaded: false,\r\n error: null,\r\n isValid: false,\r\n token: null\r\n }));\r\n\r\n const handleSignOut = useCallback(async (error?: Error) => {\r\n await auth.signOut();\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n error: error || null,\r\n isValid: false,\r\n token: null\r\n });\r\n router.push(loginPath);\r\n }, [auth, router, loginPath]);\r\n\r\n const checkTokenValidity = useCallback(async (user: User | null) => {\r\n if (user) {\r\n try {\r\n const token = await user.getIdToken(true);\r\n const decodedToken = await verifyTernIdToken(token);\r\n const isValid = decodedToken.valid\r\n\r\n if(isValid) {\r\n return { isValid: true, token, userId: user.uid };\r\n }\r\n } catch (error) {\r\n console.error('Token validation error:', error);\r\n await handleSignOut(error instanceof Error ? error : new Error('Authentication token is invalid'));\r\n return { isValid: false, token: null, userId: null };\r\n }\r\n }\r\n return { isValid: false, token: null, userId: null };\r\n }, [handleSignOut]);\r\n\r\n const handleAuthStateChange = useCallback(async (user: User | null) => {\r\n const { isValid, token, userId } = await checkTokenValidity(user);\r\n \r\n setAuthState({\r\n isLoaded: true,\r\n userId,\r\n isValid,\r\n token,\r\n error: null\r\n });\r\n\r\n if (onUserChanged) {\r\n await onUserChanged(user);\r\n }\r\n\r\n if (!isValid) {\r\n router.push(loginPath);\r\n }\r\n }, [checkTokenValidity, onUserChanged, router, loginPath]);\r\n\r\n useEffect(() => {\r\n const unsubscribeAuthState = onAuthStateChanged(auth, handleAuthStateChange);\r\n \r\n // Initial check\r\n handleAuthStateChange(auth.currentUser);\r\n\r\n // Set up an interval to periodically check token validity\r\n const intervalId = setInterval(() => {\r\n handleAuthStateChange(auth.currentUser);\r\n }, 30000); // Check every 30 seconds\r\n\r\n return () => {\r\n unsubscribeAuthState();\r\n clearInterval(intervalId);\r\n };\r\n }, [auth, handleAuthStateChange]);\r\n\r\n const contextValue: TernSecureCtxValue = useMemo(() => ({\r\n ...authState,\r\n checkTokenValidity: () => handleAuthStateChange(auth.currentUser),\r\n signOut: handleSignOut,\r\n }), [authState, handleAuthStateChange, auth, handleSignOut]);\r\n\r\n return (\r\n <TernSecureCtx.Provider value={contextValue}>\r\n {children}\r\n </TernSecureCtx.Provider>\r\n );\r\n}"],"mappings":";AA0GI;AAxGJ,SAAgB,UAAU,WAAW,SAAS,mBAAmB;AACjE,SAAS,sBAAsB;AAC/B,SAAe,0BAA0B;AACzC,SAAS,qBAA0D;AACnE,SAAS,iBAAiB;AAC1B,SAAS,yBAAyB;AAQ3B,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AAAA,EACA,YAAY;AACd,GAAkC;AAChC,QAAM,OAAO,QAAQ,MAAM,gBAAgB,CAAC,CAAC;AAC7C,QAAM,SAAS,UAAU;AAEzB,QAAM,CAAC,WAAW,YAAY,IAAI,SAA0B,OAAO;AAAA,IACjE,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,OAAO;AAAA,IACP,SAAS;AAAA,IACT,OAAO;AAAA,EACT,EAAE;AAEF,QAAM,gBAAgB,YAAY,OAAO,UAAkB;AACzD,UAAM,KAAK,QAAQ;AACnB,iBAAa;AAAA,MACX,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,OAAO,SAAS;AAAA,MAChB,SAAS;AAAA,MACT,OAAO;AAAA,IACT,CAAC;AACD,WAAO,KAAK,SAAS;AAAA,EACvB,GAAG,CAAC,MAAM,QAAQ,SAAS,CAAC;AAE5B,QAAM,qBAAqB,YAAY,OAAO,SAAsB;AAClE,QAAI,MAAM;AACR,UAAI;AACF,cAAM,QAAQ,MAAM,KAAK,WAAW,IAAI;AACxC,cAAM,eAAe,MAAM,kBAAkB,KAAK;AAClD,cAAM,UAAU,aAAa;AAE7B,YAAG,SAAS;AACV,iBAAO,EAAE,SAAS,MAAM,OAAO,QAAQ,KAAK,IAAI;AAAA,QAClD;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,2BAA2B,KAAK;AAC9C,cAAM,cAAc,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,iCAAiC,CAAC;AACjG,eAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ,KAAK;AAAA,MACrD;AAAA,IACF;AACA,WAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ,KAAK;AAAA,EACrD,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,wBAAwB,YAAY,OAAO,SAAsB;AACrE,UAAM,EAAE,SAAS,OAAO,OAAO,IAAI,MAAM,mBAAmB,IAAI;AAEhE,iBAAa;AAAA,MACX,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IACT,CAAC;AAED,QAAI,eAAe;AACjB,YAAM,cAAc,IAAI;AAAA,IAC1B;AAEA,QAAI,CAAC,SAAS;AACZ,aAAO,KAAK,SAAS;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,oBAAoB,eAAe,QAAQ,SAAS,CAAC;AAEzD,YAAU,MAAM;AACd,UAAM,uBAAuB,mBAAmB,MAAM,qBAAqB;AAG3E,0BAAsB,KAAK,WAAW;AAGtC,UAAM,aAAa,YAAY,MAAM;AACnC,4BAAsB,KAAK,WAAW;AAAA,IACxC,GAAG,GAAK;AAER,WAAO,MAAM;AACX,2BAAqB;AACrB,oBAAc,UAAU;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,MAAM,qBAAqB,CAAC;AAEhC,QAAM,eAAmC,QAAQ,OAAO;AAAA,IACtD,GAAG;AAAA,IACH,oBAAoB,MAAM,sBAAsB,KAAK,WAAW;AAAA,IAChE,SAAS;AAAA,EACX,IAAI,CAAC,WAAW,uBAAuB,MAAM,aAAa,CAAC;AAE3D,SACE,oBAAC,cAAc,UAAd,EAAuB,OAAO,cAC5B,UACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport React, { useState, useEffect, useMemo, useCallback, useRef } from 'react'\r\nimport { ternSecureAuth } from '../utils/client-init'\r\nimport { User, onAuthStateChanged } from \"firebase/auth\"\r\nimport { TernSecureCtx, TernSecureState, TernSecureCtxValue } from './TernSecureCtx'\r\nimport { useRouter, usePathname } from 'next/navigation'\r\nimport { verifyTernIdToken } from '../app-router/server/sessionTernSecure'\r\n\r\ninterface TernSecureClientProviderProps {\r\n children: React.ReactNode;\r\n onUserChanged?: (user: User | null) => Promise<void>;\r\n loginPath?: string;\r\n}\r\n\r\nexport function TernSecureClientProvider({ \r\n children, \r\n onUserChanged,\r\n loginPath = '/sign-in'\r\n}: TernSecureClientProviderProps) {\r\n const auth = useMemo(() => ternSecureAuth, []);\r\n const router = useRouter();\r\n const pathname = usePathname();\r\n const intervalRef = useRef<NodeJS.Timeout | null>(null);\r\n\r\n const [authState, setAuthState] = useState<TernSecureState>(() => ({\r\n userId: null,\r\n isLoaded: false,\r\n error: null,\r\n isValid: false,\r\n token: null\r\n }));\r\n\r\n const handleSignOut = useCallback(async (error?: Error) => {\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n intervalRef.current = null;\r\n }\r\n await auth.signOut();\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n error: error || null,\r\n isValid: false,\r\n token: null\r\n });\r\n router.push(loginPath);\r\n }, [auth, router, loginPath]);\r\n\r\n const checkTokenValidity = useCallback(async (user: User | null) => {\r\n if (user) {\r\n try {\r\n const token = await user.getIdToken(true);\r\n const decodedToken = await verifyTernIdToken(token);\r\n const isValid = decodedToken.valid\r\n\r\n if(isValid) {\r\n return { isValid: true, token, userId: user.uid };\r\n }\r\n } catch (error) {\r\n console.error('Token validation error:', error);\r\n await handleSignOut(error instanceof Error ? error : new Error('Authentication token is invalid'));\r\n return { isValid: false, token: null, userId: null };\r\n }\r\n }\r\n return { isValid: false, token: null, userId: null };\r\n }, [handleSignOut]);\r\n\r\n const startTokenCheckInterval = useCallback(() => {\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n }\r\n if (pathname !== loginPath) {\r\n intervalRef.current = setInterval(() => {\r\n checkTokenValidity(auth.currentUser);\r\n }, 30000); // Check every 30 seconds\r\n }\r\n }, [auth, checkTokenValidity, loginPath, pathname]);\r\n\r\n const handleAuthStateChange = useCallback(async (user: User | null) => {\r\n const { isValid, token, userId } = await checkTokenValidity(user);\r\n \r\n setAuthState({\r\n isLoaded: true,\r\n userId,\r\n isValid,\r\n token,\r\n error: null\r\n });\r\n\r\n if (onUserChanged) {\r\n await onUserChanged(user);\r\n }\r\n\r\n if (!isValid && pathname !== loginPath) {\r\n router.push(loginPath);\r\n }\r\n\r\n if (user && isValid) {\r\n startTokenCheckInterval();\r\n } else if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n intervalRef.current = null;\r\n }\r\n }, [checkTokenValidity, onUserChanged, router, loginPath, pathname, startTokenCheckInterval]);\r\n\r\n useEffect(() => {\r\n const unsubscribeAuthState = onAuthStateChanged(auth, handleAuthStateChange);\r\n \r\n return () => {\r\n unsubscribeAuthState();\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n }\r\n };\r\n }, [auth, handleAuthStateChange]);\r\n\r\n const contextValue: TernSecureCtxValue = useMemo(() => ({\r\n ...authState,\r\n checkTokenValidity: () => handleAuthStateChange(auth.currentUser),\r\n signOut: handleSignOut,\r\n }), [authState, handleAuthStateChange, auth, handleSignOut]);\r\n\r\n return (\r\n <TernSecureCtx.Provider value={contextValue}>\r\n {children}\r\n </TernSecureCtx.Provider>\r\n );\r\n}\r\n\r\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"TernSecureClientProvider.d.ts","sourceRoot":"","sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAoD,MAAM,OAAO,CAAA;AAExE,OAAO,EAAE,IAAI,EAAsB,MAAM,eAAe,CAAA;AAKxD,UAAU,6BAA6B;IACrC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,aAAa,EACb,SAAsB,EACvB,EAAE,6BAA6B,2CA2F/B"}
1
+ {"version":3,"file":"TernSecureClientProvider.d.ts","sourceRoot":"","sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4D,MAAM,OAAO,CAAA;AAEhF,OAAO,EAAE,IAAI,EAAsB,MAAM,eAAe,CAAA;AAKxD,UAAU,6BAA6B;IACrC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,aAAa,EACb,SAAsB,EACvB,EAAE,6BAA6B,2CA6G/B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tern-secure/nextjs",
3
- "version": "3.2.34",
3
+ "version": "3.2.35",
4
4
  "packageManager": "npm@10.9.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -35,6 +35,7 @@
35
35
  "prettier": "^3.3.3",
36
36
  "rimraf": "^6.0.1",
37
37
  "tsup": "^8.3.5",
38
+ "turbo": "^2.3.3",
38
39
  "typescript": "^5.7.2"
39
40
  },
40
41
  "types": "./dist/types/index.d.ts",