@phygitallabs/tapquest-core 2.0.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 +210 -0
- package/index.ts +1 -0
- package/package.json +43 -0
- package/src/constants/firebase.ts +36 -0
- package/src/constants/service.ts +30 -0
- package/src/helper/helpers.ts +3 -0
- package/src/helper/index.ts +1 -0
- package/src/index.ts +25 -0
- package/src/modules/achievement/helpers/index.ts +98 -0
- package/src/modules/achievement/hooks/index.ts +171 -0
- package/src/modules/achievement/index.ts +5 -0
- package/src/modules/achievement/types/index.ts +45 -0
- package/src/modules/achivementWithReward/hooks/achivementPlusRewardModel.ts +83 -0
- package/src/modules/achivementWithReward/hooks/index.ts +5 -0
- package/src/modules/achivementWithReward/index.ts +5 -0
- package/src/modules/auth/README.md +527 -0
- package/src/modules/auth/constants/index.ts +9 -0
- package/src/modules/auth/helpers/index.ts +161 -0
- package/src/modules/auth/helpers/refreshToken.ts +63 -0
- package/src/modules/auth/index.ts +20 -0
- package/src/modules/auth/providers/AuthProvider.tsx +207 -0
- package/src/modules/auth/providers/index.ts +1 -0
- package/src/modules/auth/services/FirebaseAuthService.ts +290 -0
- package/src/modules/auth/services/authServiceFactory.ts +22 -0
- package/src/modules/auth/services/index.ts +3 -0
- package/src/modules/auth/store/authSlice.ts +137 -0
- package/src/modules/auth/types/index.ts +109 -0
- package/src/modules/campaign/hooks/index.ts +6 -0
- package/src/modules/campaign/hooks/useCampaignService.ts +7 -0
- package/src/modules/campaign/index.tsx +7 -0
- package/src/modules/campaign/types/campaign.ts +51 -0
- package/src/modules/campaign/types/enums.ts +4 -0
- package/src/modules/campaign/types/index.ts +4 -0
- package/src/modules/campaign/types/requests.ts +46 -0
- package/src/modules/data-tracking/hooks/index.ts +67 -0
- package/src/modules/data-tracking/index.ts +1 -0
- package/src/modules/generate-certificate/hooks/index.ts +8 -0
- package/src/modules/generate-certificate/index.ts +3 -0
- package/src/modules/generate-certificate/types/generateCertificate.ts +7 -0
- package/src/modules/generate-certificate/types/index.ts +7 -0
- package/src/modules/location/hooks/index.ts +8 -0
- package/src/modules/location/hooks/useLocationService.ts +8 -0
- package/src/modules/location/index.tsx +11 -0
- package/src/modules/location/types/index.ts +18 -0
- package/src/modules/location/types/locationModel.ts +21 -0
- package/src/modules/location/utils/index.ts +5 -0
- package/src/modules/location/utils/locationHelpers.ts +13 -0
- package/src/modules/memory/hooks/index.ts +3 -0
- package/src/modules/memory/index.ts +3 -0
- package/src/modules/memory/types/index.ts +3 -0
- package/src/modules/notification/index.ts +2 -0
- package/src/modules/notification/providers/index.tsx +50 -0
- package/src/modules/notification/types/index.ts +3 -0
- package/src/modules/reward/hooks/index.ts +14 -0
- package/src/modules/reward/hooks/useRewardService.ts +14 -0
- package/src/modules/reward/index.tsx +16 -0
- package/src/modules/reward/types/enums.ts +13 -0
- package/src/modules/reward/types/index.ts +4 -0
- package/src/modules/reward/types/requests.ts +281 -0
- package/src/modules/reward/types/reward.ts +90 -0
- package/src/modules/scan-chip/hooks/index.tsx +67 -0
- package/src/modules/scan-chip/index.ts +2 -0
- package/src/modules/scan-chip/types/index.ts +25 -0
- package/src/modules/send-email/hooks/index.ts +2 -0
- package/src/modules/send-email/index.ts +1 -0
- package/src/modules/user-profile/hooks/index.ts +3 -0
- package/src/modules/user-profile/index.ts +3 -0
- package/src/modules/user-profile/types/index.ts +3 -0
- package/src/providers/ServicesProvider.tsx +173 -0
- package/src/providers/TapquestCoreProvider.tsx +64 -0
- package/src/providers/index.ts +1 -0
- package/src/store/hooks.ts +6 -0
- package/src/store/index.ts +45 -0
- package/src/types/common.d.ts +8 -0
- package/src/types/media.ts +26 -0
- package/src/types/service.d.ts +34 -0
- package/tsconfig.json +28 -0
- package/tsup.config.ts +10 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Achievement, useManyAchievements } from "@phygitallabs/achievement";
|
|
2
|
+
import { EntityRewardModel } from "@phygitallabs/reward";
|
|
3
|
+
import { useCreateModelGroupReward } from "@phygitallabs/reward/src/hooks/useGroupReward";
|
|
4
|
+
import { useEffect, useMemo } from "react";
|
|
5
|
+
|
|
6
|
+
interface UseAchivementPlusRewardModelParams {
|
|
7
|
+
campaignId: string;
|
|
8
|
+
}
|
|
9
|
+
interface GroupRewardData {
|
|
10
|
+
reward_models: EntityRewardModel[][];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function buildMappedAchievements(achievements: Achievement[], groupRewardData: GroupRewardData) {
|
|
14
|
+
if (!groupRewardData?.reward_models) return [];
|
|
15
|
+
|
|
16
|
+
const rewardModels = groupRewardData.reward_models;
|
|
17
|
+
|
|
18
|
+
return achievements.map((achievement, achievementIndex) => {
|
|
19
|
+
const parentReward = rewardModels[achievementIndex]?.[0] || null;
|
|
20
|
+
|
|
21
|
+
const subAchievements =
|
|
22
|
+
achievement.subAchievementIds?.map((subId: string, subIndex: number) => {
|
|
23
|
+
const reward =
|
|
24
|
+
rewardModels[
|
|
25
|
+
achievementIndex * (achievement.subAchievementIds?.length || 0) +
|
|
26
|
+
subIndex +
|
|
27
|
+
1
|
|
28
|
+
]?.[0] || null;
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
id: subId,
|
|
32
|
+
reward_model: reward,
|
|
33
|
+
};
|
|
34
|
+
}) || [];
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
id: achievement.id,
|
|
38
|
+
name: achievement.name,
|
|
39
|
+
reward_model: parentReward,
|
|
40
|
+
subAchievements,
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const useAchivementPlusRewardModel = ({ campaignId }: UseAchivementPlusRewardModelParams) => {
|
|
46
|
+
const { data: achievements, isLoading: isLoadingAchievements } =
|
|
47
|
+
useManyAchievements(
|
|
48
|
+
{
|
|
49
|
+
"filter.labels": { campaign_id: campaignId },
|
|
50
|
+
"filter.type": "group_mission",
|
|
51
|
+
"pagination.limit": 200,
|
|
52
|
+
},
|
|
53
|
+
{ enabled: !!campaignId }
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const groupRewardIds = useMemo(() => {
|
|
57
|
+
if (!achievements?.data) return [];
|
|
58
|
+
return achievements.data
|
|
59
|
+
.map((achievement) => achievement.groupRewardId)
|
|
60
|
+
.filter((id): id is string => id !== undefined);
|
|
61
|
+
}, [achievements?.data]);
|
|
62
|
+
const {
|
|
63
|
+
mutate: fetchGroupRewardModels,
|
|
64
|
+
data: groupRewardModelsData,
|
|
65
|
+
isPending: isPendingGroupRewardModels,
|
|
66
|
+
} = useCreateModelGroupReward();
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (groupRewardIds.length > 0) {
|
|
69
|
+
fetchGroupRewardModels({ group_reward_ids: groupRewardIds });
|
|
70
|
+
}
|
|
71
|
+
}, [groupRewardIds, fetchGroupRewardModels]);
|
|
72
|
+
|
|
73
|
+
const mappedAchievements = useMemo(() => {
|
|
74
|
+
if (!groupRewardModelsData?.data || !achievements?.data) return [];
|
|
75
|
+
return buildMappedAchievements(achievements.data, groupRewardModelsData.data);
|
|
76
|
+
}, [groupRewardModelsData, achievements?.data]);
|
|
77
|
+
return {
|
|
78
|
+
mappedAchievements,
|
|
79
|
+
isLoading: isLoadingAchievements || isPendingGroupRewardModels,
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export default useAchivementPlusRewardModel;
|
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
# Authentication Module
|
|
2
|
+
|
|
3
|
+
The auth module provides a comprehensive authentication system for TapQuest applications with support for multiple authentication providers, centralized state management, and flexible integration patterns.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This module implements a provider-agnostic authentication system that supports:
|
|
8
|
+
- Email/password authentication
|
|
9
|
+
- Google OAuth integration
|
|
10
|
+
- User session persistence
|
|
11
|
+
- Real-time authentication state management
|
|
12
|
+
- Type-safe authentication interfaces
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
auth/
|
|
18
|
+
├── constants/ # Authentication constants and keys
|
|
19
|
+
├── helpers/ # Storage utilities and helper functions
|
|
20
|
+
├── hooks/ # Authentication hooks
|
|
21
|
+
├── providers/ # React context providers
|
|
22
|
+
├── services/ # Authentication service implementations
|
|
23
|
+
├── store/ # Redux state management
|
|
24
|
+
├── types/ # TypeScript type definitions
|
|
25
|
+
└── index.ts # Public API exports
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Core Components
|
|
29
|
+
|
|
30
|
+
### 1. Authentication Service Interface
|
|
31
|
+
|
|
32
|
+
The `AuthService` interface defines a contract for authentication providers:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
interface AuthService {
|
|
36
|
+
// Authentication methods
|
|
37
|
+
signInWithEmailAndPassword(email: string, password: string): Promise<AuthResponse>;
|
|
38
|
+
signInWithGoogle(): Promise<AuthResponse>;
|
|
39
|
+
signUp(email: string, password: string): Promise<AuthResponse>;
|
|
40
|
+
signOut(): Promise<void>;
|
|
41
|
+
|
|
42
|
+
// Password management
|
|
43
|
+
sendPasswordResetEmail(email: string): Promise<void>;
|
|
44
|
+
sendEmailVerification(): Promise<void>;
|
|
45
|
+
changePassword(newPassword: string): Promise<void>;
|
|
46
|
+
|
|
47
|
+
// Auth state monitoring
|
|
48
|
+
onAuthStateChanged(callback: (user: UserData | null) => void): () => void;
|
|
49
|
+
getCurrentUser(): UserData | null;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. User Data Structure
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
type UserData = {
|
|
57
|
+
uid?: string;
|
|
58
|
+
id: string;
|
|
59
|
+
userName: string;
|
|
60
|
+
displayName?: string;
|
|
61
|
+
avatar: string;
|
|
62
|
+
exp: number;
|
|
63
|
+
email: string;
|
|
64
|
+
emailVerified: boolean;
|
|
65
|
+
accessToken: string;
|
|
66
|
+
refreshToken: string;
|
|
67
|
+
signInProvider?: SignInProvider;
|
|
68
|
+
role?: UserRole;
|
|
69
|
+
scanStatus?: boolean;
|
|
70
|
+
};
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 3. State Management
|
|
74
|
+
|
|
75
|
+
The module uses Redux Toolkit for state management with the following structure:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
interface AuthState {
|
|
79
|
+
user: UserData;
|
|
80
|
+
isSignedIn: boolean;
|
|
81
|
+
pending: boolean;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Available Actions:**
|
|
86
|
+
- `signIn` - Store user data and mark as signed in
|
|
87
|
+
- `signOut` - Clear user data and mark as signed out
|
|
88
|
+
- `updateScanStatus` - Update user's scan status
|
|
89
|
+
- `refreshUser` - Update user data
|
|
90
|
+
- `setPending` - Set loading state
|
|
91
|
+
- `initializeFromStorage` - Restore authentication state from storage
|
|
92
|
+
|
|
93
|
+
## Quick Start
|
|
94
|
+
|
|
95
|
+
### 1. Basic Setup
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { TapquestCoreProvider, FirebaseAuthService } from '@phygitallabs/tapquest-core';
|
|
99
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
100
|
+
|
|
101
|
+
const authConfig = {
|
|
102
|
+
apiKey: "your-api-key",
|
|
103
|
+
authDomain: "your-auth-domain",
|
|
104
|
+
projectId: "your-project-id",
|
|
105
|
+
// ... other Firebase config
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const authService = new FirebaseAuthService(authConfig);
|
|
109
|
+
const queryClient = new QueryClient();
|
|
110
|
+
|
|
111
|
+
function App() {
|
|
112
|
+
return (
|
|
113
|
+
<TapquestCoreProvider
|
|
114
|
+
queryClient={queryClient}
|
|
115
|
+
apiConfig={apiConfig}
|
|
116
|
+
authService={authService}
|
|
117
|
+
authCallbacks={{
|
|
118
|
+
onSignInSuccess: (userData) => console.log('User signed in:', userData),
|
|
119
|
+
onSignOutSuccess: () => console.log('User signed out'),
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
<YourApp />
|
|
123
|
+
</TapquestCoreProvider>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 2. Using the Auth Hook
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { useAuth } from '@phygitallabs/tapquest-core';
|
|
132
|
+
|
|
133
|
+
function LoginComponent() {
|
|
134
|
+
const {
|
|
135
|
+
user,
|
|
136
|
+
isSignedIn,
|
|
137
|
+
isLoading,
|
|
138
|
+
signIn,
|
|
139
|
+
signInWithGoogle,
|
|
140
|
+
signOut
|
|
141
|
+
} = useAuth();
|
|
142
|
+
|
|
143
|
+
const handleEmailSignIn = async () => {
|
|
144
|
+
const result = await signIn(email, password);
|
|
145
|
+
if (result.errorCode) {
|
|
146
|
+
console.error('Sign in failed:', result.errorCode);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const handleGoogleSignIn = async () => {
|
|
151
|
+
const result = await signInWithGoogle();
|
|
152
|
+
if (result.errorCode) {
|
|
153
|
+
console.error('Google sign in failed:', result.errorCode);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
if (isLoading) {
|
|
158
|
+
return <div>Loading...</div>;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (isSignedIn) {
|
|
162
|
+
return (
|
|
163
|
+
<div>
|
|
164
|
+
<h1>Welcome, {user.displayName}!</h1>
|
|
165
|
+
<button onClick={signOut}>Sign Out</button>
|
|
166
|
+
</div>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<div>
|
|
172
|
+
<button onClick={handleEmailSignIn}>Sign In with Email</button>
|
|
173
|
+
<button onClick={handleGoogleSignIn}>Sign In with Google</button>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Advanced Usage
|
|
180
|
+
|
|
181
|
+
### 1. Custom Authentication Service
|
|
182
|
+
|
|
183
|
+
You can implement your own authentication service by implementing the `AuthService` interface:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import { AuthService, AuthResponse, UserData } from '@phygitallabs/tapquest-core';
|
|
187
|
+
|
|
188
|
+
class CustomAuthService implements AuthService {
|
|
189
|
+
async signInWithEmailAndPassword(email: string, password: string): Promise<AuthResponse> {
|
|
190
|
+
// Your custom implementation
|
|
191
|
+
const response = await fetch('/api/auth/signin', {
|
|
192
|
+
method: 'POST',
|
|
193
|
+
body: JSON.stringify({ email, password }),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const data = await response.json();
|
|
197
|
+
return {
|
|
198
|
+
errorCode: data.error || '',
|
|
199
|
+
data: data.user || null,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Implement other required methods...
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 2. Authentication Callbacks
|
|
208
|
+
|
|
209
|
+
Use callbacks to integrate with analytics, tracking, or other services:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
const authCallbacks = {
|
|
213
|
+
onUserIdentify: ({ email, name, avatar, uid }) => {
|
|
214
|
+
// Identify user in analytics
|
|
215
|
+
analytics.identify(uid, { email, name, avatar });
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
onSignInSuccess: (userData) => {
|
|
219
|
+
// Track sign-in event
|
|
220
|
+
analytics.track('User Signed In', {
|
|
221
|
+
method: userData.signInProvider,
|
|
222
|
+
email: userData.email,
|
|
223
|
+
});
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
onSignOutSuccess: () => {
|
|
227
|
+
// Track sign-out and reset tracking
|
|
228
|
+
analytics.track('User Signed Out');
|
|
229
|
+
analytics.reset();
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### 3. Storage Helpers
|
|
235
|
+
|
|
236
|
+
The module provides centralized storage utilities for managing authentication data:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import {
|
|
240
|
+
setUserInfo,
|
|
241
|
+
getUserInfo,
|
|
242
|
+
removeUserInfo,
|
|
243
|
+
setAccessToken,
|
|
244
|
+
getAccessToken,
|
|
245
|
+
removeAccessToken
|
|
246
|
+
} from '@phygitallabs/tapquest-core';
|
|
247
|
+
|
|
248
|
+
// Store user information
|
|
249
|
+
setUserInfo(userData);
|
|
250
|
+
|
|
251
|
+
// Retrieve user information
|
|
252
|
+
const userData = getUserInfo();
|
|
253
|
+
|
|
254
|
+
// Clear user information
|
|
255
|
+
removeUserInfo();
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Storage Management
|
|
259
|
+
|
|
260
|
+
The auth module uses centralized storage helpers that provide:
|
|
261
|
+
|
|
262
|
+
- **Type Safety**: Proper typing for stored data
|
|
263
|
+
- **Error Handling**: Safe JSON parsing with error recovery
|
|
264
|
+
- **SSR Support**: Proper handling of server-side rendering
|
|
265
|
+
- **Consistency**: Standardized storage keys and methods
|
|
266
|
+
|
|
267
|
+
### Storage Keys
|
|
268
|
+
|
|
269
|
+
The following keys are used for localStorage:
|
|
270
|
+
|
|
271
|
+
- `phygital-user-info` - User profile data
|
|
272
|
+
- `accessToken` - API access token
|
|
273
|
+
- `refreshToken` - Token refresh credentials
|
|
274
|
+
- `Device-UID` - Device identifier
|
|
275
|
+
- `chip-auth-token` - Chip authentication token
|
|
276
|
+
|
|
277
|
+
## Firebase Integration
|
|
278
|
+
|
|
279
|
+
The `FirebaseAuthService` provides a complete Firebase Authentication implementation:
|
|
280
|
+
|
|
281
|
+
### Features
|
|
282
|
+
|
|
283
|
+
- **Lazy Loading**: Firebase modules are loaded only when needed
|
|
284
|
+
- **SSR Safe**: Proper handling of server-side rendering
|
|
285
|
+
- **Error Translation**: User-friendly error messages in Vietnamese
|
|
286
|
+
- **Email Verification**: Enforced email verification for security
|
|
287
|
+
- **Dynamic Imports**: Reduces bundle size by loading Firebase dynamically
|
|
288
|
+
|
|
289
|
+
### Configuration
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
const firebaseConfig = {
|
|
293
|
+
apiKey: "your-api-key",
|
|
294
|
+
authDomain: "your-project.firebaseapp.com",
|
|
295
|
+
projectId: "your-project",
|
|
296
|
+
storageBucket: "your-project.appspot.com",
|
|
297
|
+
messagingSenderId: "123456789",
|
|
298
|
+
appId: "1:123456789:web:abcdef",
|
|
299
|
+
measurementId: "G-XXXXXXXXXX"
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const authService = new FirebaseAuthService(firebaseConfig);
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Type Definitions
|
|
306
|
+
|
|
307
|
+
### User Roles
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
enum UserRole {
|
|
311
|
+
NULL = "NULL",
|
|
312
|
+
SUPERADMIN = "SUPER_ADMIN",
|
|
313
|
+
ORGADMIN = "ORG_ADMIN",
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Sign-in Providers
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
type SignInProvider = "password" | "google.com";
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Authentication Response
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
interface AuthResponse {
|
|
327
|
+
errorCode: string;
|
|
328
|
+
data: UserData | null;
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Best Practices
|
|
333
|
+
|
|
334
|
+
### 1. Error Handling
|
|
335
|
+
|
|
336
|
+
Always handle authentication errors appropriately:
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
const { signIn } = useAuth();
|
|
340
|
+
|
|
341
|
+
const handleSignIn = async () => {
|
|
342
|
+
try {
|
|
343
|
+
const result = await signIn(email, password);
|
|
344
|
+
if (result.errorCode) {
|
|
345
|
+
// Display user-friendly error message
|
|
346
|
+
setError(result.errorCode);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
// Handle successful sign-in
|
|
350
|
+
} catch (error) {
|
|
351
|
+
// Handle unexpected errors
|
|
352
|
+
console.error('Sign in error:', error);
|
|
353
|
+
setError('An unexpected error occurred');
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### 2. Loading States
|
|
359
|
+
|
|
360
|
+
Use the loading state to provide user feedback:
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
const { isLoading, signIn } = useAuth();
|
|
364
|
+
|
|
365
|
+
const handleSignIn = async () => {
|
|
366
|
+
if (isLoading) return; // Prevent double submissions
|
|
367
|
+
|
|
368
|
+
await signIn(email, password);
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
return (
|
|
372
|
+
<button disabled={isLoading} onClick={handleSignIn}>
|
|
373
|
+
{isLoading ? 'Signing in...' : 'Sign In'}
|
|
374
|
+
</button>
|
|
375
|
+
);
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### 3. Route Protection
|
|
379
|
+
|
|
380
|
+
Create protected routes using authentication state:
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
import { useAuth } from '@phygitallabs/tapquest-core';
|
|
384
|
+
|
|
385
|
+
function ProtectedRoute({ children }) {
|
|
386
|
+
const { isSignedIn, isLoading } = useAuth();
|
|
387
|
+
|
|
388
|
+
if (isLoading) {
|
|
389
|
+
return <LoadingSpinner />;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (!isSignedIn) {
|
|
393
|
+
return <Navigate to="/login" />;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return children;
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## API Reference
|
|
401
|
+
|
|
402
|
+
### Hooks
|
|
403
|
+
|
|
404
|
+
#### `useAuth()`
|
|
405
|
+
|
|
406
|
+
Returns the complete authentication context with user state and actions.
|
|
407
|
+
|
|
408
|
+
**Returns:**
|
|
409
|
+
```typescript
|
|
410
|
+
{
|
|
411
|
+
// State
|
|
412
|
+
user: UserData;
|
|
413
|
+
isSignedIn: boolean;
|
|
414
|
+
isLoading: boolean;
|
|
415
|
+
|
|
416
|
+
// Actions
|
|
417
|
+
signIn: (email: string, password: string) => Promise<AuthResponse>;
|
|
418
|
+
signInWithGoogle: () => Promise<AuthResponse>;
|
|
419
|
+
signUp: (email: string, password: string) => Promise<AuthResponse>;
|
|
420
|
+
signOut: () => Promise<void>;
|
|
421
|
+
forgotPassword: (email: string) => Promise<void>;
|
|
422
|
+
sendEmailVerification: () => Promise<void>;
|
|
423
|
+
changePassword: (newPassword: string) => Promise<void>;
|
|
424
|
+
updateScanStatus: (status: boolean) => void;
|
|
425
|
+
refreshUser: (userData: UserData) => void;
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Providers
|
|
430
|
+
|
|
431
|
+
#### `AuthProvider`
|
|
432
|
+
|
|
433
|
+
Provides authentication context to child components.
|
|
434
|
+
|
|
435
|
+
**Props:**
|
|
436
|
+
```typescript
|
|
437
|
+
{
|
|
438
|
+
children: React.ReactNode;
|
|
439
|
+
authService: AuthService;
|
|
440
|
+
authCallbacks?: AuthCallbacks;
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Services
|
|
445
|
+
|
|
446
|
+
#### `FirebaseAuthService`
|
|
447
|
+
|
|
448
|
+
Firebase implementation of the AuthService interface.
|
|
449
|
+
|
|
450
|
+
**Constructor:**
|
|
451
|
+
```typescript
|
|
452
|
+
new FirebaseAuthService(config: FirebaseConfig)
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Storage Helpers
|
|
456
|
+
|
|
457
|
+
#### User Info
|
|
458
|
+
- `getUserInfo(): UserData | null`
|
|
459
|
+
- `setUserInfo(userData: UserData): void`
|
|
460
|
+
- `removeUserInfo(): void`
|
|
461
|
+
|
|
462
|
+
#### Tokens
|
|
463
|
+
- `getAccessToken(): string | null`
|
|
464
|
+
- `setAccessToken(token: string): void`
|
|
465
|
+
- `removeAccessToken(): void`
|
|
466
|
+
- `getRefreshToken(): string | null`
|
|
467
|
+
- `setRefreshToken(token: string): void`
|
|
468
|
+
- `removeRefreshToken(): void`
|
|
469
|
+
|
|
470
|
+
#### Device Management
|
|
471
|
+
- `getDeviceUid(): string | null`
|
|
472
|
+
- `setDeviceUid(deviceId: string): void`
|
|
473
|
+
- `removeDeviceUid(): void`
|
|
474
|
+
- `checkDeviceUid(): Promise<string>`
|
|
475
|
+
|
|
476
|
+
## Migration Guide
|
|
477
|
+
|
|
478
|
+
If you're migrating from direct localStorage usage to the auth helpers:
|
|
479
|
+
|
|
480
|
+
### Before
|
|
481
|
+
```typescript
|
|
482
|
+
// Direct localStorage usage
|
|
483
|
+
localStorage.setItem('phygital-user-info', JSON.stringify(userData));
|
|
484
|
+
const userInfo = JSON.parse(localStorage.getItem('phygital-user-info') || '{}');
|
|
485
|
+
localStorage.removeItem('phygital-user-info');
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### After
|
|
489
|
+
```typescript
|
|
490
|
+
// Using auth helpers
|
|
491
|
+
import { setUserInfo, getUserInfo, removeUserInfo } from '@phygitallabs/tapquest-core';
|
|
492
|
+
|
|
493
|
+
setUserInfo(userData);
|
|
494
|
+
const userInfo = getUserInfo();
|
|
495
|
+
removeUserInfo();
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
## Troubleshooting
|
|
499
|
+
|
|
500
|
+
### Common Issues
|
|
501
|
+
|
|
502
|
+
1. **Firebase not loading**: Ensure you're using the service in a client-side environment
|
|
503
|
+
2. **Email verification required**: Check that email verification is properly handled
|
|
504
|
+
3. **Storage errors**: Verify that localStorage is available and accessible
|
|
505
|
+
4. **Type errors**: Ensure proper TypeScript configuration and type imports
|
|
506
|
+
|
|
507
|
+
### Debug Mode
|
|
508
|
+
|
|
509
|
+
Enable debug logging by setting localStorage debug flag:
|
|
510
|
+
|
|
511
|
+
```typescript
|
|
512
|
+
localStorage.setItem('debug', 'tapquest:auth');
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
## Contributing
|
|
516
|
+
|
|
517
|
+
When contributing to the auth module:
|
|
518
|
+
|
|
519
|
+
1. Maintain backward compatibility in public APIs
|
|
520
|
+
2. Add proper TypeScript types for new features
|
|
521
|
+
3. Include comprehensive tests for new functionality
|
|
522
|
+
4. Update this documentation for any API changes
|
|
523
|
+
5. Follow the existing code patterns and conventions
|
|
524
|
+
|
|
525
|
+
## License
|
|
526
|
+
|
|
527
|
+
This module is part of the TapQuest Core package and follows the same licensing terms.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const userInfoKey = "phygital-user-info";
|
|
2
|
+
export const accessTokenKey = "accessToken";
|
|
3
|
+
export const refreshTokenKey = "refreshToken";
|
|
4
|
+
export const httpMaxRetries = 3;
|
|
5
|
+
export const retryAttemptsRefreshToken = "retryAttemptsRefreshToken";
|
|
6
|
+
|
|
7
|
+
export const deviceUIDKey = "Device-UID";
|
|
8
|
+
export const chipAuthTokenKey = "chip-auth-token";
|
|
9
|
+
|