@phygitallabs/tapquest-core 2.7.0 → 2.9.0
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/README.md +1 -1
- package/dist/index.cjs +1035 -750
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -35
- package/dist/index.d.ts +29 -35
- package/dist/index.js +1017 -717
- package/dist/index.js.map +1 -1
- package/package.json +10 -3
- package/src/modules/auth/constants/index.ts +6 -0
- package/src/modules/auth/helpers/index.ts +1 -4
- package/src/modules/auth/hooks/index.ts +2 -0
- package/src/modules/auth/hooks/useGoogleLogin.ts +169 -0
- package/src/modules/auth/hooks/useTokenRefresher.ts +39 -0
- package/src/modules/auth/index.ts +8 -2
- package/src/modules/auth/providers/AuthProvider.tsx +214 -186
- package/src/modules/auth/store/authStore.ts +577 -0
- package/src/modules/auth/types/auth.ts +29 -0
- package/src/modules/auth/types/user-data.ts +38 -0
- package/src/modules/auth/utils/user.ts +21 -0
- package/src/modules/data-tracking/hooks/index.ts +25 -1
- package/src/modules/generate-certificate/helpers/index.ts +3 -0
- package/src/modules/generate-certificate/hooks/index.ts +15 -6
- package/src/modules/generate-certificate/index.ts +3 -1
- package/src/modules/notification/providers/index.tsx +3 -3
- package/src/modules/reward/hooks/useRewardService.ts +6 -6
- package/src/modules/session-replay/README.md +334 -0
- package/src/modules/session-replay/hooks/useSessionReplay.ts +16 -0
- package/src/modules/session-replay/index.ts +10 -0
- package/src/modules/session-replay/providers/SessionReplayProvider.tsx +189 -0
- package/src/modules/session-replay/types/index.ts +147 -0
- package/src/modules/session-replay/utils/index.ts +12 -0
- package/src/providers/ServicesProvider.tsx +4 -76
- package/src/providers/TapquestCoreProvider.tsx +31 -36
- package/src/modules/auth/helpers/refreshToken.ts +0 -63
- package/src/modules/auth/store/authSlice.ts +0 -137
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { useRef, useCallback, useEffect } from "react";
|
|
2
|
+
import { useAuth } from "../store/authStore";
|
|
3
|
+
import { tokenStorage } from "@phygitallabs/authentication";
|
|
4
|
+
import { ALLOWED_ORIGINS } from "../constants";
|
|
5
|
+
|
|
6
|
+
export interface UseGoogleLoginOptions {
|
|
7
|
+
onSuccess?: () => void;
|
|
8
|
+
onError?: (error: string) => void;
|
|
9
|
+
onPopupBlocked?: () => void;
|
|
10
|
+
onPopupClosed?: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UseGoogleLoginReturn {
|
|
14
|
+
signIn: () => Promise<void>;
|
|
15
|
+
isLoading: boolean;
|
|
16
|
+
error: string | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function useGoogleLogin(options: UseGoogleLoginOptions = {}): UseGoogleLoginReturn {
|
|
20
|
+
const { onSuccess, onError, onPopupBlocked, onPopupClosed } = options;
|
|
21
|
+
const { signInWithGoogle, refreshToken, setIsSignedIn, setIsLoading, isLoading } = useAuth();
|
|
22
|
+
|
|
23
|
+
const popupRef = useRef<Window | null>(null);
|
|
24
|
+
const gotDataRef = useRef(false);
|
|
25
|
+
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
|
26
|
+
const errorRef = useRef<string | null>(null);
|
|
27
|
+
|
|
28
|
+
const handleTokenRefresh = useCallback(
|
|
29
|
+
async (refresh: string) => {
|
|
30
|
+
if (refresh) {
|
|
31
|
+
try {
|
|
32
|
+
const newToken = await refreshToken(refresh);
|
|
33
|
+
if (newToken.data?.idToken && newToken.data?.refreshToken) {
|
|
34
|
+
tokenStorage.setTokens({
|
|
35
|
+
idToken: newToken.data.idToken,
|
|
36
|
+
refreshToken: newToken.data.refreshToken,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
setIsSignedIn(true);
|
|
40
|
+
setIsLoading(false);
|
|
41
|
+
onSuccess?.();
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
const errorMessage = error instanceof Error ? error.message : "Token refresh failed";
|
|
45
|
+
errorRef.current = errorMessage;
|
|
46
|
+
onError?.(errorMessage);
|
|
47
|
+
setIsLoading(false);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
[refreshToken, setIsSignedIn, setIsLoading, onSuccess, onError]
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const cleanup = useCallback(() => {
|
|
55
|
+
if (timerRef.current) {
|
|
56
|
+
clearInterval(timerRef.current);
|
|
57
|
+
timerRef.current = null;
|
|
58
|
+
}
|
|
59
|
+
gotDataRef.current = false;
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
const handleMessage = useCallback(
|
|
63
|
+
(event: MessageEvent) => {
|
|
64
|
+
// Allow specific origins for OAuth (adjust these based on your OAuth provider)
|
|
65
|
+
const allowedOrigins = ALLOWED_ORIGINS.concat(window?.location?.origin);
|
|
66
|
+
|
|
67
|
+
// Allow wildcard origin from callback.html (but validate message structure)
|
|
68
|
+
if (event.origin !== "*" && !allowedOrigins.includes(event.origin as string)) {
|
|
69
|
+
console.warn("Rejected message from untrusted origin:", event.origin);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Additional validation for message structure
|
|
74
|
+
if (!event.data || typeof event.data !== "object") {
|
|
75
|
+
console.warn("Invalid message data received");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (event.data.type === "LOGIN_SUCCESS") {
|
|
80
|
+
const { refreshToken: refresh } = event.data;
|
|
81
|
+
|
|
82
|
+
// Support both token formats from callback
|
|
83
|
+
if (refresh) {
|
|
84
|
+
handleTokenRefresh(refresh);
|
|
85
|
+
}
|
|
86
|
+
gotDataRef.current = true;
|
|
87
|
+
cleanup();
|
|
88
|
+
} else if (event.data.type === "LOGIN_ERROR") {
|
|
89
|
+
const errorMessage = event.data.error || "Login failed";
|
|
90
|
+
errorRef.current = errorMessage;
|
|
91
|
+
onError?.(errorMessage);
|
|
92
|
+
setIsLoading(false);
|
|
93
|
+
gotDataRef.current = true;
|
|
94
|
+
cleanup();
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
[handleTokenRefresh, setIsLoading, cleanup, onError]
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const signIn = useCallback(async (): Promise<void> => {
|
|
101
|
+
const width = 500;
|
|
102
|
+
const height = 600;
|
|
103
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
104
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
errorRef.current = null;
|
|
108
|
+
setIsLoading(true);
|
|
109
|
+
const response = await signInWithGoogle();
|
|
110
|
+
|
|
111
|
+
popupRef.current = window.open(
|
|
112
|
+
`${response.statusMessage}`,
|
|
113
|
+
"oauthPopup",
|
|
114
|
+
`width=${width},height=${height},left=${left},top=${top},resizable,scrollbars`
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// Ensure popup opened successfully
|
|
118
|
+
if (!popupRef.current) {
|
|
119
|
+
setIsLoading(false);
|
|
120
|
+
const error = "Popup blocked. Please allow popups for this site.";
|
|
121
|
+
errorRef.current = error;
|
|
122
|
+
onPopupBlocked?.();
|
|
123
|
+
onError?.(error);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
timerRef.current = setInterval(() => {
|
|
128
|
+
if (popupRef.current && popupRef.current.closed) {
|
|
129
|
+
if (timerRef.current) {
|
|
130
|
+
clearInterval(timerRef.current);
|
|
131
|
+
timerRef.current = null;
|
|
132
|
+
}
|
|
133
|
+
if (!gotDataRef.current) {
|
|
134
|
+
const error = "Authentication popup was closed";
|
|
135
|
+
errorRef.current = error;
|
|
136
|
+
onPopupClosed?.();
|
|
137
|
+
onError?.(error);
|
|
138
|
+
setIsLoading(false);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}, 500);
|
|
142
|
+
} catch (error: any) {
|
|
143
|
+
const errorMessage = error instanceof Error ? error.message : "Google sign in failed";
|
|
144
|
+
errorRef.current = errorMessage;
|
|
145
|
+
onError?.(errorMessage);
|
|
146
|
+
setIsLoading(false);
|
|
147
|
+
}
|
|
148
|
+
}, [signInWithGoogle, setIsLoading, handleMessage, onError, onPopupBlocked, onPopupClosed]);
|
|
149
|
+
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
window.addEventListener("message", handleMessage);
|
|
152
|
+
return () => {
|
|
153
|
+
window.removeEventListener("message", handleMessage);
|
|
154
|
+
};
|
|
155
|
+
}, [handleMessage]);
|
|
156
|
+
|
|
157
|
+
// Cleanup on unmount
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
return () => {
|
|
160
|
+
cleanup();
|
|
161
|
+
};
|
|
162
|
+
}, [cleanup, handleMessage]);
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
signIn,
|
|
166
|
+
isLoading,
|
|
167
|
+
error: errorRef.current,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { useAuth } from "../store/authStore";
|
|
3
|
+
import { tokenStorage } from "@phygitallabs/authentication";
|
|
4
|
+
|
|
5
|
+
export function useTokenRefresher() {
|
|
6
|
+
const { refreshToken, signOut } = useAuth();
|
|
7
|
+
|
|
8
|
+
const handleRefreshToken = async () => {
|
|
9
|
+
try {
|
|
10
|
+
const isTokenExpired = tokenStorage.isTokenExpired();
|
|
11
|
+
if (!isTokenExpired) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const newToken = await refreshToken();
|
|
15
|
+
|
|
16
|
+
if(newToken.data?.idToken && newToken.data?.refreshToken){
|
|
17
|
+
tokenStorage.setTokens({
|
|
18
|
+
idToken: newToken.data?.idToken,
|
|
19
|
+
refreshToken: newToken.data?.refreshToken,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error("Failed to refresh token:", error);
|
|
25
|
+
signOut();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// React Query handles refetching on interval + focus
|
|
30
|
+
useQuery({
|
|
31
|
+
queryKey: ["refresh-token"],
|
|
32
|
+
queryFn: () => handleRefreshToken(),
|
|
33
|
+
refetchInterval: 30 * 60 * 1000, // every 30 minutes
|
|
34
|
+
refetchIntervalInBackground: false, // even when tab is hidden
|
|
35
|
+
refetchOnWindowFocus: true, // also refresh when user returns to tab
|
|
36
|
+
retry: false, // don't retry repeatedly if refresh fails
|
|
37
|
+
enabled: !!tokenStorage.getAuthToken(), // only run if logged in
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -12,8 +12,14 @@ export type {
|
|
|
12
12
|
} from './types';
|
|
13
13
|
|
|
14
14
|
// Export hooks and providers
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
export { AuthProvider } from './providers';
|
|
16
|
+
|
|
17
|
+
// Export auth hooks from store
|
|
18
|
+
export { useAuth } from './store/authStore';
|
|
19
|
+
|
|
20
|
+
// Export auth hooks
|
|
21
|
+
export { useGoogleLogin } from './hooks';
|
|
22
|
+
export type { UseGoogleLoginOptions, UseGoogleLoginReturn } from './hooks';
|
|
17
23
|
|
|
18
24
|
// Export constants and helpers
|
|
19
25
|
export * from './constants';
|
|
@@ -1,207 +1,235 @@
|
|
|
1
|
-
import { createContext, useEffect, ReactNode, useContext } from 'react';
|
|
2
|
-
import { AuthService, AuthCallbacks } from '../types';
|
|
3
|
-
|
|
4
|
-
import { useAppSelector, useAppDispatch } from '../../../store/hooks';
|
|
5
1
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// Auth actions
|
|
28
|
-
signInWithCredential: (email: string, password: string) => Promise<AuthResponse>;
|
|
29
|
-
signInWithGoogle: () => Promise<AuthResponse | null>;
|
|
30
|
-
signUp: (email: string, password: string) => Promise<AuthResponse>;
|
|
31
|
-
signOut: () => Promise<void>;
|
|
32
|
-
|
|
33
|
-
// Password actions
|
|
34
|
-
forgotPassword: (email: string) => Promise<void>;
|
|
35
|
-
sendEmailVerification: () => Promise<void>;
|
|
36
|
-
changePassword: (newPassword: string) => Promise<void>;
|
|
37
|
-
|
|
38
|
-
// User management
|
|
39
|
-
updateScanStatus: (status: boolean) => void;
|
|
40
|
-
refreshUser: (userData: UserData) => void;
|
|
41
|
-
|
|
2
|
+
AuthenticationProvider,
|
|
3
|
+
useAuthenticationHeaders,
|
|
4
|
+
tokenStorage,
|
|
5
|
+
} from "@phygitallabs/authentication";
|
|
6
|
+
import { useEffect } from "react";
|
|
7
|
+
import { useAuthStore } from "../store/authStore";
|
|
8
|
+
import { transformProtoUserData } from "../utils/user";
|
|
9
|
+
import { userService } from "@phygitallabs/api-core";
|
|
10
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
11
|
+
|
|
12
|
+
import { usePGLCoreService } from "@phygitallabs/api-core";
|
|
13
|
+
import { useAchievementService } from "@phygitallabs/achievement";
|
|
14
|
+
import { useRewardService } from "@phygitallabs/reward";
|
|
15
|
+
import { useGenerateCertificateService } from "@phygitallabs/generate-certificate";
|
|
16
|
+
import { useTokenRefresher } from "../hooks/useTokenRefresher";
|
|
17
|
+
|
|
18
|
+
interface AuthProviderProps {
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
baseURL: string;
|
|
21
|
+
queryClient: QueryClient;
|
|
42
22
|
}
|
|
43
23
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
children: ReactNode;
|
|
48
|
-
authService: AuthService;
|
|
49
|
-
authCallbacks?: AuthCallbacks;
|
|
50
|
-
}
|
|
24
|
+
// Inner component that has access to the authentication context
|
|
25
|
+
const AuthStateManager = () => {
|
|
26
|
+
const { updateHeaders } = useAuthenticationHeaders();
|
|
51
27
|
|
|
52
|
-
|
|
53
|
-
const
|
|
28
|
+
const { updateHeaders: updateHeadersPGL, coreApi } = usePGLCoreService();
|
|
29
|
+
const { updateHeaders: updateHeadersAchievement } = useAchievementService();
|
|
30
|
+
const { updateHeaders: updateHeadersReward } = useRewardService();
|
|
31
|
+
const { updateHeaders: updateHeadersGenerateCertificate } =
|
|
32
|
+
useGenerateCertificateService();
|
|
54
33
|
|
|
55
34
|
useEffect(() => {
|
|
56
|
-
// Initialize auth
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
35
|
+
// Initialize the auth store to set isSignedIn based on persisted token
|
|
36
|
+
useAuthStore.getState().actions.initialize();
|
|
37
|
+
// Initial setup - set headers if token exists
|
|
38
|
+
const token = tokenStorage.getAuthToken();
|
|
39
|
+
if (token) {
|
|
40
|
+
updateHeaders({
|
|
41
|
+
Authorization: `Bearer ${token}`,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
updateHeadersPGL({
|
|
45
|
+
Authorization: `Bearer ${token}`,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
updateHeadersAchievement({
|
|
49
|
+
Authorization: `Bearer ${token}`,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
updateHeadersReward({
|
|
53
|
+
Authorization: `Bearer ${token}`,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
updateHeadersGenerateCertificate({
|
|
57
|
+
Authorization: `Bearer ${token}`,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Sync user.accessToken with actual token from localStorage
|
|
61
|
+
const { user } = useAuthStore.getState();
|
|
62
|
+
if (user && user.accessToken !== token) {
|
|
63
|
+
useAuthStore.getState().actions.setUser({
|
|
64
|
+
...user,
|
|
65
|
+
accessToken: token,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let previousIsSignedIn = useAuthStore.getState().isSignedIn;
|
|
71
|
+
|
|
72
|
+
const unsub = useAuthStore.subscribe(
|
|
73
|
+
(state: any) => [state.isSignedIn, state.user],
|
|
74
|
+
async ([isSignedIn]: [any]) => {
|
|
75
|
+
// Only run when isSignedIn actually changes
|
|
76
|
+
if (isSignedIn === previousIsSignedIn) {
|
|
77
|
+
return;
|
|
72
78
|
}
|
|
73
79
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
previousIsSignedIn = isSignedIn as boolean;
|
|
81
|
+
|
|
82
|
+
if (isSignedIn) {
|
|
83
|
+
const token = tokenStorage.getAuthToken();
|
|
84
|
+
|
|
85
|
+
if (token) {
|
|
86
|
+
// Update API headers with the new token
|
|
87
|
+
updateHeaders({
|
|
88
|
+
Authorization: `Bearer ${token}`,
|
|
89
|
+
});
|
|
90
|
+
updateHeadersPGL({
|
|
91
|
+
Authorization: `Bearer ${token}`,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
updateHeadersAchievement({
|
|
95
|
+
Authorization: `Bearer ${token}`,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
updateHeadersReward({
|
|
99
|
+
Authorization: `Bearer ${token}`,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
updateHeadersGenerateCertificate({
|
|
103
|
+
Authorization: `Bearer ${token}`,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
// Fetch user profile when signed in
|
|
108
|
+
const userApi = userService(coreApi);
|
|
109
|
+
const userProfile = await userApi.getMyProfile();
|
|
110
|
+
const transformedUserData = transformProtoUserData(
|
|
111
|
+
userProfile,
|
|
112
|
+
token
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
useAuthStore.getState().actions.setUser(transformedUserData);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
useAuthStore.getState().actions.signOut();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Trigger auth state change event
|
|
122
|
+
const { eventCallbacks, user } = useAuthStore.getState();
|
|
123
|
+
eventCallbacks?.onAuthStateChange?.(user, true);
|
|
124
|
+
} else {
|
|
125
|
+
// Clear headers when logged out
|
|
126
|
+
updateHeaders({});
|
|
127
|
+
updateHeadersPGL({});
|
|
128
|
+
updateHeadersAchievement({
|
|
129
|
+
Authorization: ``,
|
|
130
|
+
});
|
|
131
|
+
updateHeadersReward({
|
|
132
|
+
Authorization: ``,
|
|
133
|
+
});
|
|
134
|
+
updateHeadersGenerateCertificate({
|
|
135
|
+
Authorization: ``,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Trigger auth state change event
|
|
139
|
+
const { eventCallbacks } = useAuthStore.getState();
|
|
140
|
+
eventCallbacks?.onAuthStateChange?.(null, false);
|
|
77
141
|
}
|
|
78
|
-
}
|
|
79
|
-
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// Listen for token changes (for refresh scenarios)
|
|
146
|
+
const unsubTokenChange = tokenStorage.addTokenChangeListener(async () => {
|
|
147
|
+
const { isSignedIn, user } = useAuthStore.getState();
|
|
148
|
+
if (isSignedIn) {
|
|
149
|
+
const token = tokenStorage.getAuthToken();
|
|
150
|
+
if (token) {
|
|
151
|
+
updateHeaders({
|
|
152
|
+
Authorization: `Bearer ${token}`,
|
|
153
|
+
});
|
|
154
|
+
updateHeadersPGL({
|
|
155
|
+
Authorization: `Bearer ${token}`,
|
|
156
|
+
});
|
|
157
|
+
updateHeadersAchievement({
|
|
158
|
+
Authorization: `Bearer ${token}`,
|
|
159
|
+
});
|
|
160
|
+
updateHeadersReward({
|
|
161
|
+
Authorization: `Bearer ${token}`,
|
|
162
|
+
});
|
|
163
|
+
updateHeadersGenerateCertificate({
|
|
164
|
+
Authorization: `Bearer ${token}`,
|
|
165
|
+
});
|
|
80
166
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
167
|
+
// Sync user.accessToken with actual token from localStorage
|
|
168
|
+
if (user && user.accessToken !== token) {
|
|
169
|
+
useAuthStore.getState().actions.setUser({
|
|
170
|
+
...user,
|
|
171
|
+
accessToken: token,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
// Token was cleared, sign out
|
|
176
|
+
useAuthStore.getState().actions.signOut();
|
|
177
|
+
updateHeaders({});
|
|
178
|
+
updateHeadersPGL({});
|
|
179
|
+
updateHeadersAchievement({
|
|
180
|
+
Authorization: ``,
|
|
181
|
+
});
|
|
182
|
+
updateHeadersReward({
|
|
183
|
+
Authorization: ``,
|
|
184
|
+
});
|
|
185
|
+
updateHeadersGenerateCertificate({
|
|
186
|
+
Authorization: ``,
|
|
187
|
+
});
|
|
84
188
|
}
|
|
85
189
|
}
|
|
86
|
-
dispatch(setPending(false));
|
|
87
190
|
});
|
|
88
191
|
|
|
89
192
|
return () => {
|
|
90
|
-
|
|
193
|
+
unsub();
|
|
194
|
+
unsubTokenChange();
|
|
91
195
|
};
|
|
92
|
-
}, [
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
} else if (result.errorCode && authCallbacks?.onSignInError) {
|
|
120
|
-
authCallbacks.onSignInError(result.errorCode);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return null;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const signUp = async (email: string, password: string): Promise<AuthResponse> => {
|
|
127
|
-
const result = await authService.signUp(email, password);
|
|
128
|
-
|
|
129
|
-
if (result.data && authCallbacks?.onSignUpSuccess) {
|
|
130
|
-
authCallbacks.onSignUpSuccess(result.data);
|
|
131
|
-
} else if (result.errorCode && authCallbacks?.onSignUpError) {
|
|
132
|
-
authCallbacks.onSignUpError(result.errorCode);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return result;
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const signOut = async (): Promise<void> => {
|
|
139
|
-
await authService.signOut();
|
|
140
|
-
dispatch(signOutAction());
|
|
141
|
-
|
|
142
|
-
if (authCallbacks?.onSignOutSuccess) {
|
|
143
|
-
authCallbacks.onSignOutSuccess();
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
// Password actions
|
|
148
|
-
const forgotPassword = async (email: string): Promise<void> => {
|
|
149
|
-
return await authService.sendPasswordResetEmail(email);
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const sendEmailVerification = async (): Promise<void> => {
|
|
153
|
-
return await authService.sendEmailVerification();
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const changePassword = async (newPassword: string): Promise<void> => {
|
|
157
|
-
return await authService.changePassword(newPassword);
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
// User management
|
|
161
|
-
const updateScanStatus = (status: boolean): void => {
|
|
162
|
-
dispatch(updateScanStatusAction(status));
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
const refreshUser = (userData: UserData): void => {
|
|
166
|
-
dispatch(refreshUserAction(userData));
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
const contextValue: AuthContextType = {
|
|
170
|
-
authService,
|
|
171
|
-
authCallbacks,
|
|
172
|
-
// User state
|
|
173
|
-
user,
|
|
174
|
-
isSignedIn,
|
|
175
|
-
isLoading,
|
|
176
|
-
|
|
177
|
-
// Auth actions
|
|
178
|
-
signInWithCredential,
|
|
179
|
-
signInWithGoogle,
|
|
180
|
-
signUp,
|
|
181
|
-
signOut,
|
|
182
|
-
|
|
183
|
-
// Password actions
|
|
184
|
-
forgotPassword,
|
|
185
|
-
sendEmailVerification,
|
|
186
|
-
changePassword,
|
|
187
|
-
|
|
188
|
-
// User management
|
|
189
|
-
updateScanStatus,
|
|
190
|
-
refreshUser,
|
|
196
|
+
}, [
|
|
197
|
+
updateHeaders,
|
|
198
|
+
updateHeadersPGL,
|
|
199
|
+
updateHeadersAchievement,
|
|
200
|
+
updateHeadersReward,
|
|
201
|
+
updateHeadersGenerateCertificate,
|
|
202
|
+
coreApi,
|
|
203
|
+
]);
|
|
204
|
+
|
|
205
|
+
return null;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const TokenRefresher = () => {
|
|
209
|
+
useTokenRefresher();
|
|
210
|
+
return null;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export const AuthProvider = ({
|
|
214
|
+
children,
|
|
215
|
+
baseURL,
|
|
216
|
+
queryClient,
|
|
217
|
+
}: AuthProviderProps) => {
|
|
218
|
+
const axiosConfig = {
|
|
219
|
+
headers: {
|
|
220
|
+
"Content-Type": "application/json",
|
|
221
|
+
},
|
|
191
222
|
};
|
|
192
223
|
|
|
193
224
|
return (
|
|
194
|
-
<
|
|
225
|
+
<AuthenticationProvider
|
|
226
|
+
axiosConfig={axiosConfig}
|
|
227
|
+
baseURL={baseURL}
|
|
228
|
+
queryClient={queryClient}
|
|
229
|
+
>
|
|
230
|
+
<AuthStateManager />
|
|
231
|
+
<TokenRefresher />
|
|
195
232
|
{children}
|
|
196
|
-
</
|
|
233
|
+
</AuthenticationProvider>
|
|
197
234
|
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export const useAuth = () => {
|
|
201
|
-
const authContext = useContext(AuthContext);
|
|
202
|
-
if (!authContext) {
|
|
203
|
-
throw new Error('useAuth must be used within an AuthProvider');
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return authContext;
|
|
207
|
-
};
|
|
235
|
+
};
|