@umituz/react-native-auth 3.3.0 → 3.4.1
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.
|
|
3
|
+
"version": "3.4.1",
|
|
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",
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* - AuthService (email/password auth)
|
|
7
7
|
* - Auth Listener (state management)
|
|
8
8
|
* - User Document Service (Firestore)
|
|
9
|
+
* - Anonymous-to-authenticated conversion detection
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import type { Auth, User } from "firebase/auth";
|
|
@@ -32,7 +33,22 @@ export interface InitializeAuthOptions {
|
|
|
32
33
|
/** Enable auto anonymous sign-in (default: true) */
|
|
33
34
|
autoAnonymousSignIn?: boolean;
|
|
34
35
|
|
|
35
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Callback when user converts from anonymous to authenticated
|
|
38
|
+
* Use this to migrate user data (e.g., call Cloud Function)
|
|
39
|
+
*
|
|
40
|
+
* @param anonymousUserId - The previous anonymous user ID
|
|
41
|
+
* @param authenticatedUserId - The new authenticated user ID
|
|
42
|
+
*/
|
|
43
|
+
onUserConverted?: (
|
|
44
|
+
anonymousUserId: string,
|
|
45
|
+
authenticatedUserId: string
|
|
46
|
+
) => void | Promise<void>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Callback when auth state changes (after user document is ensured)
|
|
50
|
+
* Called for every auth state change including initial load
|
|
51
|
+
*/
|
|
36
52
|
onAuthStateChange?: (user: User | null) => void | Promise<void>;
|
|
37
53
|
|
|
38
54
|
/** Auth configuration (password rules, etc.) */
|
|
@@ -41,20 +57,23 @@ export interface InitializeAuthOptions {
|
|
|
41
57
|
|
|
42
58
|
let isInitialized = false;
|
|
43
59
|
|
|
60
|
+
// Track previous user for conversion detection
|
|
61
|
+
let previousUserId: string | null = null;
|
|
62
|
+
let wasAnonymous = false;
|
|
63
|
+
|
|
44
64
|
/**
|
|
45
65
|
* Initialize all auth services with a single call
|
|
46
66
|
*
|
|
47
67
|
* @example
|
|
48
68
|
* ```typescript
|
|
49
|
-
* import { initializeAuth
|
|
69
|
+
* import { initializeAuth } from '@umituz/react-native-auth';
|
|
70
|
+
* import { migrateUserData } from '@domains/migration';
|
|
50
71
|
*
|
|
51
72
|
* await initializeAuth({
|
|
52
73
|
* userCollection: 'users',
|
|
53
74
|
* autoAnonymousSignIn: true,
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* await ensureUserDocument(user);
|
|
57
|
-
* }
|
|
75
|
+
* onUserConverted: async (anonymousId, authId) => {
|
|
76
|
+
* await migrateUserData(anonymousId, authId);
|
|
58
77
|
* },
|
|
59
78
|
* });
|
|
60
79
|
* ```
|
|
@@ -72,6 +91,7 @@ export async function initializeAuth(
|
|
|
72
91
|
extraFields,
|
|
73
92
|
collectExtras,
|
|
74
93
|
autoAnonymousSignIn = true,
|
|
94
|
+
onUserConverted,
|
|
75
95
|
onAuthStateChange,
|
|
76
96
|
authConfig,
|
|
77
97
|
} = options;
|
|
@@ -103,15 +123,41 @@ export async function initializeAuth(
|
|
|
103
123
|
initializeAuthListener({
|
|
104
124
|
autoAnonymousSignIn,
|
|
105
125
|
onAuthStateChange: async (user) => {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
126
|
+
if (!user) {
|
|
127
|
+
// User signed out
|
|
128
|
+
previousUserId = null;
|
|
129
|
+
wasAnonymous = false;
|
|
130
|
+
onAuthStateChange?.(null);
|
|
131
|
+
return;
|
|
109
132
|
}
|
|
110
133
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
134
|
+
const currentUserId = user.uid;
|
|
135
|
+
const isCurrentlyAnonymous = user.isAnonymous ?? false;
|
|
136
|
+
|
|
137
|
+
// Detect anonymous-to-authenticated conversion
|
|
138
|
+
if (
|
|
139
|
+
previousUserId &&
|
|
140
|
+
previousUserId !== currentUserId &&
|
|
141
|
+
wasAnonymous &&
|
|
142
|
+
!isCurrentlyAnonymous &&
|
|
143
|
+
onUserConverted
|
|
144
|
+
) {
|
|
145
|
+
try {
|
|
146
|
+
await onUserConverted(previousUserId, currentUserId);
|
|
147
|
+
} catch {
|
|
148
|
+
// Migration failed but don't block user flow
|
|
149
|
+
}
|
|
114
150
|
}
|
|
151
|
+
|
|
152
|
+
// Create/update user document in Firestore
|
|
153
|
+
await ensureUserDocument(user);
|
|
154
|
+
|
|
155
|
+
// Update tracking state
|
|
156
|
+
previousUserId = currentUserId;
|
|
157
|
+
wasAnonymous = isCurrentlyAnonymous;
|
|
158
|
+
|
|
159
|
+
// Call app's custom callback
|
|
160
|
+
onAuthStateChange?.(user);
|
|
115
161
|
},
|
|
116
162
|
});
|
|
117
163
|
|
|
@@ -131,4 +177,6 @@ export function isAuthInitialized(): boolean {
|
|
|
131
177
|
*/
|
|
132
178
|
export function resetAuthInitialization(): void {
|
|
133
179
|
isInitialized = false;
|
|
180
|
+
previousUserId = null;
|
|
181
|
+
wasAnonymous = false;
|
|
134
182
|
}
|
|
@@ -67,6 +67,17 @@ export const useAuthStore = createStore<AuthState, AuthActions>({
|
|
|
67
67
|
isAnonymous: state.isAnonymous,
|
|
68
68
|
initialized: state.initialized,
|
|
69
69
|
}),
|
|
70
|
+
migrate: (persistedState: unknown, version: number) => {
|
|
71
|
+
const state = persistedState as Partial<AuthState>;
|
|
72
|
+
if (version < 2) {
|
|
73
|
+
return {
|
|
74
|
+
...initialAuthState,
|
|
75
|
+
isAnonymous: state.isAnonymous ?? false,
|
|
76
|
+
initialized: state.initialized ?? false,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return { ...initialAuthState, ...state } as AuthState;
|
|
80
|
+
},
|
|
70
81
|
actions: (set, get) => ({
|
|
71
82
|
setFirebaseUser: (firebaseUser) => {
|
|
72
83
|
const { isAnonymous } = get();
|