@umituz/react-native-auth 3.1.8 → 3.1.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-auth",
3
- "version": "3.1.8",
3
+ "version": "3.1.10",
4
4
  "description": "Authentication service for React Native apps - Secure, type-safe, and production-ready. Provider-agnostic design with dependency injection, configurable validation, and comprehensive error handling.",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
package/src/index.ts CHANGED
@@ -105,6 +105,12 @@ export type {
105
105
  PasswordRequirements,
106
106
  } from './infrastructure/utils/AuthValidation';
107
107
 
108
+ // =============================================================================
109
+ // PRESENTATION LAYER - Provider
110
+ // =============================================================================
111
+
112
+ export { AuthProvider } from './presentation/providers/AuthProvider';
113
+
108
114
  // =============================================================================
109
115
  // PRESENTATION LAYER - Hooks
110
116
  // =============================================================================
@@ -112,6 +118,11 @@ export type {
112
118
  export { useAuth } from './presentation/hooks/useAuth';
113
119
  export type { UseAuthResult } from './presentation/hooks/useAuth';
114
120
 
121
+ export { useAuthRequired } from './presentation/hooks/useAuthRequired';
122
+ export type { UseAuthRequiredResult } from './presentation/hooks/useAuthRequired';
123
+
124
+ export { useRequireAuth, useUserId } from './presentation/hooks/useRequireAuth';
125
+
115
126
  export { useUserProfile } from './presentation/hooks/useUserProfile';
116
127
  export type { UserProfileData, UseUserProfileParams } from './presentation/hooks/useUserProfile';
117
128
 
@@ -183,8 +194,19 @@ export {
183
194
  initializeAuthListener,
184
195
  resetAuthListener,
185
196
  selectIsAuthenticated,
197
+ selectUserId,
198
+ selectIsAnonymous,
199
+ selectUserType,
200
+ selectIsAuthReady,
201
+ getUserId,
202
+ getUserType,
203
+ getIsAuthenticated,
204
+ getIsGuest,
205
+ getIsAnonymous,
186
206
  } from './presentation/stores/authStore';
187
207
 
208
+ export type { UserType } from './presentation/stores/authStore';
209
+
188
210
  // =============================================================================
189
211
  // PRESENTATION LAYER - Utilities
190
212
  // =============================================================================
@@ -4,15 +4,18 @@
4
4
  *
5
5
  * Uses centralized Zustand store for auth state.
6
6
  * Single source of truth - no duplicate subscriptions.
7
- *
8
- * @example
9
- * ```typescript
10
- * const { user, isAuthenticated, signIn, signUp, signOut } = useAuth();
11
- * ```
12
7
  */
13
8
 
14
9
  import { useCallback } from "react";
15
- import { useAuthStore, selectIsAuthenticated } from "../stores/authStore";
10
+ import {
11
+ useAuthStore,
12
+ selectIsAuthenticated,
13
+ selectUserId,
14
+ selectUserType,
15
+ selectIsAnonymous,
16
+ selectIsAuthReady,
17
+ type UserType,
18
+ } from "../stores/authStore";
16
19
  import {
17
20
  useSignInMutation,
18
21
  useSignUpMutation,
@@ -24,11 +27,19 @@ import type { AuthUser } from "../../domain/entities/AuthUser";
24
27
  export interface UseAuthResult {
25
28
  /** Current authenticated user */
26
29
  user: AuthUser | null;
30
+ /** Current user ID (uid) */
31
+ userId: string | null;
32
+ /** Current user type */
33
+ userType: UserType;
27
34
  /** Whether auth state is loading */
28
35
  loading: boolean;
36
+ /** Whether auth is ready (initialized and not loading) */
37
+ isAuthReady: boolean;
29
38
  /** Whether user is in guest mode */
30
39
  isGuest: boolean;
31
- /** Whether user is authenticated */
40
+ /** Whether user is anonymous */
41
+ isAnonymous: boolean;
42
+ /** Whether user is authenticated (not guest, not anonymous) */
32
43
  isAuthenticated: boolean;
33
44
  /** Current error message */
34
45
  error: string | null;
@@ -49,11 +60,6 @@ export interface UseAuthResult {
49
60
  *
50
61
  * Uses centralized Zustand store - all components share the same state.
51
62
  * Must call initializeAuthListener() once in app root.
52
- *
53
- * @example
54
- * ```typescript
55
- * const { user, isAuthenticated, signIn, signUp, signOut } = useAuth();
56
- * ```
57
63
  */
58
64
  export function useAuth(): UseAuthResult {
59
65
  // State from store
@@ -62,6 +68,10 @@ export function useAuth(): UseAuthResult {
62
68
  const isGuest = useAuthStore((state) => state.isGuest);
63
69
  const error = useAuthStore((state) => state.error);
64
70
  const isAuthenticated = useAuthStore(selectIsAuthenticated);
71
+ const userId = useAuthStore(selectUserId);
72
+ const userType = useAuthStore(selectUserType);
73
+ const isAnonymous = useAuthStore(selectIsAnonymous);
74
+ const isAuthReady = useAuthStore(selectIsAuthReady);
65
75
 
66
76
  // Actions from store
67
77
  const setLoading = useAuthStore((state) => state.setLoading);
@@ -137,8 +147,12 @@ export function useAuth(): UseAuthResult {
137
147
 
138
148
  return {
139
149
  user,
150
+ userId,
151
+ userType,
140
152
  loading,
153
+ isAuthReady,
141
154
  isGuest,
155
+ isAnonymous,
142
156
  isAuthenticated,
143
157
  error,
144
158
  signUp,
@@ -0,0 +1,69 @@
1
+ /**
2
+ * useAuthRequired Hook
3
+ * Check if user meets auth requirements for a feature
4
+ *
5
+ * Usage:
6
+ * ```tsx
7
+ * const { isAllowed, requireAuth } = useAuthRequired();
8
+ *
9
+ * const handleAction = () => {
10
+ * if (!isAllowed) {
11
+ * requireAuth(); // Opens auth modal
12
+ * return;
13
+ * }
14
+ * // Proceed with action
15
+ * };
16
+ * ```
17
+ */
18
+
19
+ import { useCallback } from "react";
20
+ import { useAuthStore, selectIsAuthenticated } from "../stores/authStore";
21
+ import { useAuthModalStore } from "../stores/authModalStore";
22
+
23
+ export interface UseAuthRequiredResult {
24
+ /** Whether user is authenticated (not guest, not anonymous) */
25
+ isAllowed: boolean;
26
+ /** Whether auth is still loading */
27
+ isLoading: boolean;
28
+ /** Current user ID (null if not authenticated) */
29
+ userId: string | null;
30
+ /** Show auth modal to require authentication */
31
+ requireAuth: () => void;
32
+ /** Check and require auth - returns true if allowed, false if modal shown */
33
+ checkAndRequireAuth: () => boolean;
34
+ }
35
+
36
+ /**
37
+ * Hook to check auth requirements and show modal if needed
38
+ */
39
+ export function useAuthRequired(): UseAuthRequiredResult {
40
+ const isAllowed = useAuthStore(selectIsAuthenticated);
41
+ const isLoading = useAuthStore((state) => state.loading);
42
+ const userId = useAuthStore((state) => state.firebaseUser?.uid ?? null);
43
+ const openAuthModal = useAuthModalStore((state) => state.open);
44
+
45
+ const requireAuth = useCallback(() => {
46
+ openAuthModal("login");
47
+ }, [openAuthModal]);
48
+
49
+ const checkAndRequireAuth = useCallback((): boolean => {
50
+ if (isLoading) {
51
+ return false;
52
+ }
53
+
54
+ if (!isAllowed) {
55
+ openAuthModal("login");
56
+ return false;
57
+ }
58
+
59
+ return true;
60
+ }, [isAllowed, isLoading, openAuthModal]);
61
+
62
+ return {
63
+ isAllowed,
64
+ isLoading,
65
+ userId,
66
+ requireAuth,
67
+ checkAndRequireAuth,
68
+ };
69
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * useRequireAuth Hook
3
+ * Returns userId, throws if not authenticated
4
+ *
5
+ * Usage:
6
+ * ```tsx
7
+ * // In a component that MUST have authenticated user
8
+ * const userId = useRequireAuth();
9
+ * // userId is guaranteed to be string, not null
10
+ * ```
11
+ */
12
+
13
+ import { useAuthStore, selectIsAuthenticated } from "../stores/authStore";
14
+
15
+ /**
16
+ * Get userId or throw if not authenticated
17
+ * Use this in components that REQUIRE authentication
18
+ */
19
+ export function useRequireAuth(): string {
20
+ const isAuthenticated = useAuthStore(selectIsAuthenticated);
21
+ const userId = useAuthStore((state) => state.firebaseUser?.uid ?? null);
22
+
23
+ if (!isAuthenticated || !userId) {
24
+ throw new Error("User not authenticated. This component requires auth.");
25
+ }
26
+
27
+ return userId;
28
+ }
29
+
30
+ /**
31
+ * Get userId safely (returns null if not authenticated)
32
+ */
33
+ export function useUserId(): string | null {
34
+ return useAuthStore((state) => state.firebaseUser?.uid ?? null);
35
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * AuthProvider
3
+ * Wraps app and handles auth initialization
4
+ *
5
+ * Usage:
6
+ * ```tsx
7
+ * <AuthProvider>
8
+ * <App />
9
+ * </AuthProvider>
10
+ * ```
11
+ */
12
+
13
+ import React, { useEffect, type ReactNode } from "react";
14
+ import { initializeAuthListener } from "../stores/authStore";
15
+
16
+ interface AuthProviderProps {
17
+ children: ReactNode;
18
+ }
19
+
20
+ /**
21
+ * AuthProvider component
22
+ * Initializes Firebase auth listener on mount
23
+ * Must wrap the app root
24
+ */
25
+ export function AuthProvider({ children }: AuthProviderProps): JSX.Element {
26
+ useEffect(() => {
27
+ const unsubscribe = initializeAuthListener();
28
+ return unsubscribe;
29
+ }, []);
30
+
31
+ return <>{children}</>;
32
+ }
@@ -4,18 +4,6 @@
4
4
  *
5
5
  * Single source of truth for auth state across the app.
6
6
  * Firebase auth changes are synced via initializeAuthListener().
7
- *
8
- * @example
9
- * ```typescript
10
- * // Initialize once in app root
11
- * useEffect(() => {
12
- * const unsubscribe = initializeAuthListener();
13
- * return unsubscribe;
14
- * }, []);
15
- *
16
- * // Use anywhere
17
- * const { user, isAuthenticated, signIn, signOut } = useAuthStore();
18
- * ```
19
7
  */
20
8
 
21
9
  import { createStore } from "@umituz/react-native-storage";
@@ -25,6 +13,13 @@ import type { AuthUser } from "../../domain/entities/AuthUser";
25
13
  import { mapToAuthUser } from "../../infrastructure/utils/UserMapper";
26
14
  import { getAuthService } from "../../infrastructure/services/AuthService";
27
15
 
16
+ declare const __DEV__: boolean;
17
+
18
+ /**
19
+ * User type classification
20
+ */
21
+ export type UserType = "authenticated" | "anonymous" | "none";
22
+
28
23
  // =============================================================================
29
24
  // STATE TYPES
30
25
  // =============================================================================
@@ -82,7 +77,6 @@ export const useAuthStore = createStore<AuthState, AuthActions>({
82
77
  persist: true,
83
78
  version: 1,
84
79
  partialize: (state) => ({
85
- // Only persist these fields (not functions, not firebaseUser)
86
80
  isGuest: state.isGuest,
87
81
  initialized: state.initialized,
88
82
  }),
@@ -93,12 +87,9 @@ export const useAuthStore = createStore<AuthState, AuthActions>({
93
87
  let user: AuthUser | null = null;
94
88
 
95
89
  if (firebaseUser) {
96
- // Non-anonymous users always get mapped
97
90
  if (!firebaseUser.isAnonymous) {
98
91
  user = mapToAuthUser(firebaseUser);
99
- }
100
- // Anonymous users only if not in guest mode
101
- else if (!isGuest) {
92
+ } else if (!isGuest) {
102
93
  user = mapToAuthUser(firebaseUser);
103
94
  }
104
95
  }
@@ -115,7 +106,6 @@ export const useAuthStore = createStore<AuthState, AuthActions>({
115
106
  setIsGuest: (isGuest) => {
116
107
  const { firebaseUser } = get();
117
108
 
118
- // Recalculate user when guest mode changes
119
109
  let user: AuthUser | null = null;
120
110
  if (firebaseUser) {
121
111
  if (!firebaseUser.isAnonymous) {
@@ -140,6 +130,13 @@ export const useAuthStore = createStore<AuthState, AuthActions>({
140
130
  // SELECTORS
141
131
  // =============================================================================
142
132
 
133
+ /**
134
+ * Get current user ID
135
+ */
136
+ export const selectUserId = (state: AuthState): string | null => {
137
+ return state.firebaseUser?.uid ?? state.user?.uid ?? null;
138
+ };
139
+
143
140
  /**
144
141
  * Check if user is authenticated (not guest, not anonymous)
145
142
  */
@@ -147,6 +144,73 @@ export const selectIsAuthenticated = (state: AuthState): boolean => {
147
144
  return !!state.user && !state.isGuest && !state.user.isAnonymous;
148
145
  };
149
146
 
147
+ /**
148
+ * Check if user is anonymous
149
+ */
150
+ export const selectIsAnonymous = (state: AuthState): boolean => {
151
+ return state.firebaseUser?.isAnonymous ?? state.user?.isAnonymous ?? false;
152
+ };
153
+
154
+ /**
155
+ * Get current user type
156
+ */
157
+ export const selectUserType = (state: AuthState): UserType => {
158
+ if (!state.firebaseUser && !state.user) {
159
+ return "none";
160
+ }
161
+
162
+ const isAnonymous =
163
+ state.firebaseUser?.isAnonymous ?? state.user?.isAnonymous ?? false;
164
+
165
+ return isAnonymous ? "anonymous" : "authenticated";
166
+ };
167
+
168
+ /**
169
+ * Check if auth is ready (initialized and not loading)
170
+ */
171
+ export const selectIsAuthReady = (state: AuthState): boolean => {
172
+ return state.initialized && !state.loading;
173
+ };
174
+
175
+ // =============================================================================
176
+ // NON-HOOK GETTERS
177
+ // =============================================================================
178
+
179
+ /**
180
+ * Get user ID without hook
181
+ */
182
+ export function getUserId(): string | null {
183
+ return selectUserId(useAuthStore.getState());
184
+ }
185
+
186
+ /**
187
+ * Get user type without hook
188
+ */
189
+ export function getUserType(): UserType {
190
+ return selectUserType(useAuthStore.getState());
191
+ }
192
+
193
+ /**
194
+ * Check if authenticated without hook
195
+ */
196
+ export function getIsAuthenticated(): boolean {
197
+ return selectIsAuthenticated(useAuthStore.getState());
198
+ }
199
+
200
+ /**
201
+ * Check if guest without hook
202
+ */
203
+ export function getIsGuest(): boolean {
204
+ return useAuthStore.getState().isGuest;
205
+ }
206
+
207
+ /**
208
+ * Check if anonymous without hook
209
+ */
210
+ export function getIsAnonymous(): boolean {
211
+ return selectIsAnonymous(useAuthStore.getState());
212
+ }
213
+
150
214
  // =============================================================================
151
215
  // LISTENER
152
216
  // =============================================================================
@@ -156,17 +220,8 @@ let listenerInitialized = false;
156
220
  /**
157
221
  * Initialize Firebase auth listener
158
222
  * Call once in app root, returns unsubscribe function
159
- *
160
- * @example
161
- * ```typescript
162
- * useEffect(() => {
163
- * const unsubscribe = initializeAuthListener();
164
- * return unsubscribe;
165
- * }, []);
166
- * ```
167
223
  */
168
224
  export function initializeAuthListener(): () => void {
169
- // Prevent multiple initializations
170
225
  if (listenerInitialized) {
171
226
  return () => {};
172
227
  }
@@ -180,7 +235,6 @@ export function initializeAuthListener(): () => void {
180
235
  return () => {};
181
236
  }
182
237
 
183
- // Sync initial guest mode from service
184
238
  const service = getAuthService();
185
239
  if (service) {
186
240
  const isGuest = service.getIsGuestMode();
@@ -191,16 +245,15 @@ export function initializeAuthListener(): () => void {
191
245
 
192
246
  listenerInitialized = true;
193
247
 
194
- // Subscribe to auth state changes
195
248
  const unsubscribe = onAuthStateChanged(auth, (user) => {
196
- if (__DEV__) {
249
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
250
+ // eslint-disable-next-line no-console
197
251
  console.log("[authStore] Auth state changed:", user?.uid ?? "null");
198
252
  }
199
253
 
200
254
  store.setFirebaseUser(user);
201
255
  store.setInitialized(true);
202
256
 
203
- // Reset guest mode when real user signs in
204
257
  if (user && !user.isAnonymous && store.isGuest) {
205
258
  store.setIsGuest(false);
206
259
  }