@tern-secure/nextjs 3.2.37 → 3.2.38

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.
@@ -38,7 +38,7 @@ function TernSecureClientProvider({
38
38
  const auth = (0, import_react.useMemo)(() => import_client_init.ternSecureAuth, []);
39
39
  const router = (0, import_navigation.useRouter)();
40
40
  const pathname = (0, import_navigation.usePathname)();
41
- const intervalRef = (0, import_react.useRef)(null);
41
+ const lastTokenCheckRef = (0, import_react.useRef)(0);
42
42
  const [authState, setAuthState] = (0, import_react.useState)(() => ({
43
43
  userId: null,
44
44
  isLoaded: false,
@@ -47,10 +47,6 @@ function TernSecureClientProvider({
47
47
  token: null
48
48
  }));
49
49
  const handleSignOut = (0, import_react.useCallback)(async (error) => {
50
- if (intervalRef.current) {
51
- clearInterval(intervalRef.current);
52
- intervalRef.current = null;
53
- }
54
50
  await auth.signOut();
55
51
  setAuthState({
56
52
  isLoaded: true,
@@ -61,36 +57,33 @@ function TernSecureClientProvider({
61
57
  });
62
58
  router.push(loginPath);
63
59
  }, [auth, router, loginPath]);
64
- const checkTokenValidity = (0, import_react.useCallback)(async (user) => {
60
+ const checkTokenValidity = (0, import_react.useCallback)(async (user, forceCheck = false) => {
65
61
  if (user) {
66
- try {
67
- const token = await user.getIdToken(true);
68
- const decodedToken = await (0, import_sessionTernSecure.verifyTernIdToken)(token);
69
- const isValid = decodedToken.valid;
70
- if (isValid) {
71
- return { isValid: true, token, userId: user.uid };
62
+ const now = Date.now();
63
+ const timeSinceLastCheck = now - lastTokenCheckRef.current;
64
+ if (forceCheck || timeSinceLastCheck > 5 * 60 * 1e3) {
65
+ try {
66
+ const token = await user.getIdToken(forceCheck);
67
+ const decodedToken = await (0, import_sessionTernSecure.verifyTernIdToken)(token);
68
+ const isValid = decodedToken.valid;
69
+ lastTokenCheckRef.current = now;
70
+ if (isValid) {
71
+ return { isValid: true, token, userId: user.uid };
72
+ }
73
+ } catch (error) {
74
+ console.error("Token validation error:", error);
75
+ await handleSignOut(error instanceof Error ? error : new Error("Authentication token is invalid"));
76
+ return { isValid: false, token: null, userId: null };
72
77
  }
73
- } catch (error) {
74
- console.error("Token validation error:", error);
75
- await handleSignOut(error instanceof Error ? error : new Error("Authentication token is invalid"));
76
- return { isValid: false, token: null, userId: null };
78
+ } else {
79
+ return { isValid: true, token: authState.token, userId: user.uid };
77
80
  }
78
81
  }
79
82
  return { isValid: false, token: null, userId: null };
80
- }, [handleSignOut]);
81
- const startTokenCheckInterval = (0, import_react.useCallback)(() => {
82
- if (intervalRef.current) {
83
- clearInterval(intervalRef.current);
84
- }
85
- if (pathname !== loginPath) {
86
- intervalRef.current = setInterval(() => {
87
- checkTokenValidity(auth.currentUser);
88
- }, 3e4);
89
- }
90
- }, [auth, checkTokenValidity, loginPath, pathname]);
83
+ }, [handleSignOut, authState.token]);
91
84
  const handleAuthStateChange = (0, import_react.useCallback)(async (user) => {
92
85
  try {
93
- const { isValid, token, userId } = await checkTokenValidity(user);
86
+ const { isValid, token, userId } = await checkTokenValidity(user, true);
94
87
  setAuthState({
95
88
  isLoaded: true,
96
89
  userId,
@@ -104,12 +97,6 @@ function TernSecureClientProvider({
104
97
  if (!isValid && pathname !== loginPath) {
105
98
  router.push(loginPath);
106
99
  }
107
- if (user && isValid) {
108
- startTokenCheckInterval();
109
- } else if (intervalRef.current) {
110
- clearInterval(intervalRef.current);
111
- intervalRef.current = null;
112
- }
113
100
  } catch (error) {
114
101
  console.error("Auth state change error:", error);
115
102
  setAuthState({
@@ -120,21 +107,22 @@ function TernSecureClientProvider({
120
107
  error: error instanceof Error ? error : new Error("An unknown error occurred")
121
108
  });
122
109
  }
123
- }, [checkTokenValidity, onUserChanged, router, loginPath, pathname, startTokenCheckInterval]);
110
+ }, [checkTokenValidity, onUserChanged, router, loginPath, pathname]);
124
111
  (0, import_react.useEffect)(() => {
125
112
  const unsubscribeAuthState = (0, import_auth.onAuthStateChanged)(auth, handleAuthStateChange);
113
+ const unsubscribeTokenChanged = (0, import_auth.onIdTokenChanged)(auth, handleAuthStateChange);
126
114
  return () => {
127
115
  unsubscribeAuthState();
128
- if (intervalRef.current) {
129
- clearInterval(intervalRef.current);
130
- }
116
+ unsubscribeTokenChanged();
131
117
  };
132
118
  }, [auth, handleAuthStateChange]);
133
119
  const contextValue = (0, import_react.useMemo)(() => ({
134
120
  ...authState,
135
- checkTokenValidity: () => handleAuthStateChange(auth.currentUser),
121
+ checkTokenValidity: async () => {
122
+ await checkTokenValidity(auth.currentUser, true);
123
+ },
136
124
  signOut: handleSignOut
137
- }), [authState, handleAuthStateChange, auth, handleSignOut]);
125
+ }), [authState, checkTokenValidity, auth, handleSignOut]);
138
126
  if (!authState.isLoaded) {
139
127
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TernSecureCtx.TernSecureCtx.Provider, { value: contextValue, children: loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "aria-live": "polite", "aria-busy": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "sr-only", children: "Loading authentication state..." }) }) });
140
128
  }
@@ -1 +1 @@
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 loadingComponent?: React.ReactNode;\r\n}\r\n\r\nexport function TernSecureClientProvider({ \r\n children, \r\n onUserChanged,\r\n loginPath = '/sign-in',\r\n loadingComponent\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 try {\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 } catch (error) {\r\n console.error('Auth state change error:', error);\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n isValid: false,\r\n token: null,\r\n error: error instanceof Error ? error : new Error('An unknown error occurred')\r\n });\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 if (!authState.isLoaded) {\r\n return (\r\n <TernSecureCtx.Provider value={contextValue}>\r\n {loadingComponent || (\r\n <div aria-live=\"polite\" aria-busy=\"true\">\r\n <span className=\"sr-only\">Loading authentication state...</span>\r\n </div>\r\n )}\r\n </TernSecureCtx.Provider>\r\n );\r\n }\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
+ {"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, onIdTokenChanged } 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 loadingComponent?: React.ReactNode;\r\n}\r\n\r\nexport function TernSecureClientProvider({ \r\n children, \r\n onUserChanged,\r\n loginPath = '/sign-in',\r\n loadingComponent\r\n}: TernSecureClientProviderProps) {\r\n const auth = useMemo(() => ternSecureAuth, []);\r\n const router = useRouter();\r\n const pathname = usePathname();\r\n const lastTokenCheckRef = useRef<number>(0);\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, forceCheck: boolean = false) => {\r\n if (user) {\r\n const now = Date.now();\r\n const timeSinceLastCheck = now - lastTokenCheckRef.current;\r\n\r\n // Only check if forced or if it's been more than 5 minutes since the last check\r\n if (forceCheck || timeSinceLastCheck > 5 * 60 * 1000) {\r\n try {\r\n const token = await user.getIdToken(forceCheck);\r\n const decodedToken = await verifyTernIdToken(token);\r\n const isValid = decodedToken.valid;\r\n\r\n lastTokenCheckRef.current = now;\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 } else {\r\n // If we're not checking, assume the token is still valid\r\n return { isValid: true, token: authState.token, userId: user.uid };\r\n }\r\n }\r\n return { isValid: false, token: null, userId: null };\r\n }, [handleSignOut, authState.token]);\r\n\r\n const handleAuthStateChange = useCallback(async (user: User | null) => {\r\n try {\r\n const { isValid, token, userId } = await checkTokenValidity(user, true);\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 } catch (error) {\r\n console.error('Auth state change error:', error);\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n isValid: false,\r\n token: null,\r\n error: error instanceof Error ? error : new Error('An unknown error occurred')\r\n });\r\n }\r\n }, [checkTokenValidity, onUserChanged, router, loginPath, pathname]);\r\n\r\n useEffect(() => {\r\n const unsubscribeAuthState = onAuthStateChanged(auth, handleAuthStateChange);\r\n const unsubscribeTokenChanged = onIdTokenChanged(auth, handleAuthStateChange);\r\n \r\n return () => {\r\n unsubscribeAuthState();\r\n unsubscribeTokenChanged();\r\n };\r\n }, [auth, handleAuthStateChange]);\r\n\r\n const contextValue: TernSecureCtxValue = useMemo(() => ({\r\n ...authState,\r\n checkTokenValidity: async () => {\r\n await checkTokenValidity(auth.currentUser, true);\r\n },\r\n signOut: handleSignOut,\r\n }), [authState, checkTokenValidity, auth, handleSignOut]);\r\n \r\n if (!authState.isLoaded) {\r\n return (\r\n <TernSecureCtx.Provider value={contextValue}>\r\n {loadingComponent || (\r\n <div aria-live=\"polite\" aria-busy=\"true\">\r\n <span className=\"sr-only\">Loading authentication state...</span>\r\n </div>\r\n )}\r\n </TernSecureCtx.Provider>\r\n );\r\n }\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":[]}
@@ -2,7 +2,7 @@
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useMemo, useCallback, useRef } from "react";
4
4
  import { ternSecureAuth } from "../utils/client-init";
5
- import { onAuthStateChanged } from "firebase/auth";
5
+ import { onAuthStateChanged, onIdTokenChanged } from "firebase/auth";
6
6
  import { TernSecureCtx } from "./TernSecureCtx";
7
7
  import { useRouter, usePathname } from "next/navigation";
8
8
  import { verifyTernIdToken } from "../app-router/server/sessionTernSecure";
@@ -15,7 +15,7 @@ function TernSecureClientProvider({
15
15
  const auth = useMemo(() => ternSecureAuth, []);
16
16
  const router = useRouter();
17
17
  const pathname = usePathname();
18
- const intervalRef = useRef(null);
18
+ const lastTokenCheckRef = useRef(0);
19
19
  const [authState, setAuthState] = useState(() => ({
20
20
  userId: null,
21
21
  isLoaded: false,
@@ -24,10 +24,6 @@ function TernSecureClientProvider({
24
24
  token: null
25
25
  }));
26
26
  const handleSignOut = useCallback(async (error) => {
27
- if (intervalRef.current) {
28
- clearInterval(intervalRef.current);
29
- intervalRef.current = null;
30
- }
31
27
  await auth.signOut();
32
28
  setAuthState({
33
29
  isLoaded: true,
@@ -38,36 +34,33 @@ function TernSecureClientProvider({
38
34
  });
39
35
  router.push(loginPath);
40
36
  }, [auth, router, loginPath]);
41
- const checkTokenValidity = useCallback(async (user) => {
37
+ const checkTokenValidity = useCallback(async (user, forceCheck = false) => {
42
38
  if (user) {
43
- try {
44
- const token = await user.getIdToken(true);
45
- const decodedToken = await verifyTernIdToken(token);
46
- const isValid = decodedToken.valid;
47
- if (isValid) {
48
- return { isValid: true, token, userId: user.uid };
39
+ const now = Date.now();
40
+ const timeSinceLastCheck = now - lastTokenCheckRef.current;
41
+ if (forceCheck || timeSinceLastCheck > 5 * 60 * 1e3) {
42
+ try {
43
+ const token = await user.getIdToken(forceCheck);
44
+ const decodedToken = await verifyTernIdToken(token);
45
+ const isValid = decodedToken.valid;
46
+ lastTokenCheckRef.current = now;
47
+ if (isValid) {
48
+ return { isValid: true, token, userId: user.uid };
49
+ }
50
+ } catch (error) {
51
+ console.error("Token validation error:", error);
52
+ await handleSignOut(error instanceof Error ? error : new Error("Authentication token is invalid"));
53
+ return { isValid: false, token: null, userId: null };
49
54
  }
50
- } catch (error) {
51
- console.error("Token validation error:", error);
52
- await handleSignOut(error instanceof Error ? error : new Error("Authentication token is invalid"));
53
- return { isValid: false, token: null, userId: null };
55
+ } else {
56
+ return { isValid: true, token: authState.token, userId: user.uid };
54
57
  }
55
58
  }
56
59
  return { isValid: false, token: null, userId: null };
57
- }, [handleSignOut]);
58
- const startTokenCheckInterval = useCallback(() => {
59
- if (intervalRef.current) {
60
- clearInterval(intervalRef.current);
61
- }
62
- if (pathname !== loginPath) {
63
- intervalRef.current = setInterval(() => {
64
- checkTokenValidity(auth.currentUser);
65
- }, 3e4);
66
- }
67
- }, [auth, checkTokenValidity, loginPath, pathname]);
60
+ }, [handleSignOut, authState.token]);
68
61
  const handleAuthStateChange = useCallback(async (user) => {
69
62
  try {
70
- const { isValid, token, userId } = await checkTokenValidity(user);
63
+ const { isValid, token, userId } = await checkTokenValidity(user, true);
71
64
  setAuthState({
72
65
  isLoaded: true,
73
66
  userId,
@@ -81,12 +74,6 @@ function TernSecureClientProvider({
81
74
  if (!isValid && pathname !== loginPath) {
82
75
  router.push(loginPath);
83
76
  }
84
- if (user && isValid) {
85
- startTokenCheckInterval();
86
- } else if (intervalRef.current) {
87
- clearInterval(intervalRef.current);
88
- intervalRef.current = null;
89
- }
90
77
  } catch (error) {
91
78
  console.error("Auth state change error:", error);
92
79
  setAuthState({
@@ -97,21 +84,22 @@ function TernSecureClientProvider({
97
84
  error: error instanceof Error ? error : new Error("An unknown error occurred")
98
85
  });
99
86
  }
100
- }, [checkTokenValidity, onUserChanged, router, loginPath, pathname, startTokenCheckInterval]);
87
+ }, [checkTokenValidity, onUserChanged, router, loginPath, pathname]);
101
88
  useEffect(() => {
102
89
  const unsubscribeAuthState = onAuthStateChanged(auth, handleAuthStateChange);
90
+ const unsubscribeTokenChanged = onIdTokenChanged(auth, handleAuthStateChange);
103
91
  return () => {
104
92
  unsubscribeAuthState();
105
- if (intervalRef.current) {
106
- clearInterval(intervalRef.current);
107
- }
93
+ unsubscribeTokenChanged();
108
94
  };
109
95
  }, [auth, handleAuthStateChange]);
110
96
  const contextValue = useMemo(() => ({
111
97
  ...authState,
112
- checkTokenValidity: () => handleAuthStateChange(auth.currentUser),
98
+ checkTokenValidity: async () => {
99
+ await checkTokenValidity(auth.currentUser, true);
100
+ },
113
101
  signOut: handleSignOut
114
- }), [authState, handleAuthStateChange, auth, handleSignOut]);
102
+ }), [authState, checkTokenValidity, auth, handleSignOut]);
115
103
  if (!authState.isLoaded) {
116
104
  return /* @__PURE__ */ jsx(TernSecureCtx.Provider, { value: contextValue, children: loadingComponent || /* @__PURE__ */ jsx("div", { "aria-live": "polite", "aria-busy": "true", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading authentication state..." }) }) });
117
105
  }
@@ -1 +1 @@
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 loadingComponent?: React.ReactNode;\r\n}\r\n\r\nexport function TernSecureClientProvider({ \r\n children, \r\n onUserChanged,\r\n loginPath = '/sign-in',\r\n loadingComponent\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 try {\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 } catch (error) {\r\n console.error('Auth state change error:', error);\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n isValid: false,\r\n token: null,\r\n error: error instanceof Error ? error : new Error('An unknown error occurred')\r\n });\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 if (!authState.isLoaded) {\r\n return (\r\n <TernSecureCtx.Provider value={contextValue}>\r\n {loadingComponent || (\r\n <div aria-live=\"polite\" aria-busy=\"true\">\r\n <span className=\"sr-only\">Loading authentication state...</span>\r\n </div>\r\n )}\r\n </TernSecureCtx.Provider>\r\n );\r\n }\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
+ {"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, onIdTokenChanged } 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 loadingComponent?: React.ReactNode;\r\n}\r\n\r\nexport function TernSecureClientProvider({ \r\n children, \r\n onUserChanged,\r\n loginPath = '/sign-in',\r\n loadingComponent\r\n}: TernSecureClientProviderProps) {\r\n const auth = useMemo(() => ternSecureAuth, []);\r\n const router = useRouter();\r\n const pathname = usePathname();\r\n const lastTokenCheckRef = useRef<number>(0);\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, forceCheck: boolean = false) => {\r\n if (user) {\r\n const now = Date.now();\r\n const timeSinceLastCheck = now - lastTokenCheckRef.current;\r\n\r\n // Only check if forced or if it's been more than 5 minutes since the last check\r\n if (forceCheck || timeSinceLastCheck > 5 * 60 * 1000) {\r\n try {\r\n const token = await user.getIdToken(forceCheck);\r\n const decodedToken = await verifyTernIdToken(token);\r\n const isValid = decodedToken.valid;\r\n\r\n lastTokenCheckRef.current = now;\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 } else {\r\n // If we're not checking, assume the token is still valid\r\n return { isValid: true, token: authState.token, userId: user.uid };\r\n }\r\n }\r\n return { isValid: false, token: null, userId: null };\r\n }, [handleSignOut, authState.token]);\r\n\r\n const handleAuthStateChange = useCallback(async (user: User | null) => {\r\n try {\r\n const { isValid, token, userId } = await checkTokenValidity(user, true);\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 } catch (error) {\r\n console.error('Auth state change error:', error);\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n isValid: false,\r\n token: null,\r\n error: error instanceof Error ? error : new Error('An unknown error occurred')\r\n });\r\n }\r\n }, [checkTokenValidity, onUserChanged, router, loginPath, pathname]);\r\n\r\n useEffect(() => {\r\n const unsubscribeAuthState = onAuthStateChanged(auth, handleAuthStateChange);\r\n const unsubscribeTokenChanged = onIdTokenChanged(auth, handleAuthStateChange);\r\n \r\n return () => {\r\n unsubscribeAuthState();\r\n unsubscribeTokenChanged();\r\n };\r\n }, [auth, handleAuthStateChange]);\r\n\r\n const contextValue: TernSecureCtxValue = useMemo(() => ({\r\n ...authState,\r\n checkTokenValidity: async () => {\r\n await checkTokenValidity(auth.currentUser, true);\r\n },\r\n signOut: handleSignOut,\r\n }), [authState, checkTokenValidity, auth, handleSignOut]);\r\n \r\n if (!authState.isLoaded) {\r\n return (\r\n <TernSecureCtx.Provider value={contextValue}>\r\n {loadingComponent || (\r\n <div aria-live=\"polite\" aria-busy=\"true\">\r\n <span className=\"sr-only\">Loading authentication state...</span>\r\n </div>\r\n )}\r\n </TernSecureCtx.Provider>\r\n );\r\n }\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,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;IACnB,gBAAgB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACpC;AAED,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,aAAa,EACb,SAAsB,EACtB,gBAAgB,EACjB,EAAE,6BAA6B,2CAoI/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,EAAwC,MAAM,eAAe,CAAA;AAK1E,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;IACnB,gBAAgB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACpC;AAED,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,aAAa,EACb,SAAsB,EACtB,gBAAgB,EACjB,EAAE,6BAA6B,2CA0H/B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tern-secure/nextjs",
3
- "version": "3.2.37",
3
+ "version": "3.2.38",
4
4
  "packageManager": "npm@10.9.0",
5
5
  "publishConfig": {
6
6
  "access": "public"