@umituz/react-native-auth 3.6.51 → 3.6.52
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 +1 -1
- package/src/infrastructure/adapters/StorageProviderAdapter.ts +5 -6
- package/src/infrastructure/adapters/UIProviderAdapter.ts +6 -13
- package/src/infrastructure/providers/FirebaseAuthProvider.ts +1 -4
- package/src/infrastructure/repositories/AuthRepository.ts +16 -15
- package/src/infrastructure/services/AnonymousModeService.ts +16 -7
- package/src/infrastructure/services/AuthEventService.ts +4 -2
- package/src/infrastructure/services/AuthService.ts +2 -3
- package/src/infrastructure/services/UserDocumentService.ts +6 -6
- package/src/infrastructure/services/initializeAuth.ts +27 -3
- package/src/infrastructure/types/UI.types.ts +1 -2
- package/src/infrastructure/utils/AuthErrorMapper.ts +0 -5
- package/src/infrastructure/utils/AuthValidation.ts +0 -1
- package/src/infrastructure/utils/UserMapper.ts +5 -5
- package/src/infrastructure/utils/authStateHandler.ts +4 -2
- package/src/infrastructure/utils/userDocumentBuilder.util.ts +1 -1
- package/src/init/createAuthInitModule.ts +5 -7
- package/src/presentation/components/AccountActions.tsx +4 -2
- package/src/presentation/components/AuthBottomSheet.tsx +0 -2
- package/src/presentation/hooks/useAppleAuth.ts +1 -2
- package/src/presentation/hooks/useAuth.ts +4 -1
- package/src/presentation/hooks/useAuthBottomSheet.ts +3 -5
- package/src/presentation/hooks/useGoogleAuth.ts +4 -2
- package/src/presentation/hooks/useLoginForm.ts +4 -2
- package/src/presentation/hooks/useRegisterForm.ts +3 -3
- package/src/presentation/hooks/useSocialLogin.ts +1 -2
- package/src/presentation/stores/authModalStore.ts +1 -1
- package/src/presentation/stores/authStore.ts +2 -2
- package/src/presentation/stores/initializeAuthListener.ts +5 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-auth",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.52",
|
|
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",
|
|
@@ -9,11 +9,10 @@ import type { IStorageProvider } from "../types/Storage.types";
|
|
|
9
9
|
* Interface that describes the shape of common storage implementations
|
|
10
10
|
* to avoid using 'any' and resolve lint errors.
|
|
11
11
|
*/
|
|
12
|
-
interface StorageLike {
|
|
12
|
+
export interface StorageLike {
|
|
13
13
|
getString?: (
|
|
14
14
|
key: string,
|
|
15
|
-
|
|
16
|
-
defaultValue?: any
|
|
15
|
+
defaultValue?: string | null
|
|
17
16
|
) => Promise<{ value: string | null } | null>;
|
|
18
17
|
getItem?: (key: string) => Promise<string | null>;
|
|
19
18
|
setString?: (key: string, value: string) => Promise<void>;
|
|
@@ -24,8 +23,8 @@ interface StorageLike {
|
|
|
24
23
|
export class StorageProviderAdapter implements IStorageProvider {
|
|
25
24
|
private storage: StorageLike;
|
|
26
25
|
|
|
27
|
-
constructor(storage:
|
|
28
|
-
this.storage = storage
|
|
26
|
+
constructor(storage: StorageLike) {
|
|
27
|
+
this.storage = storage;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
async get(key: string): Promise<string | null> {
|
|
@@ -62,6 +61,6 @@ export class StorageProviderAdapter implements IStorageProvider {
|
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
export function createStorageProvider(storage:
|
|
64
|
+
export function createStorageProvider(storage: StorageLike): IStorageProvider {
|
|
66
65
|
return new StorageProviderAdapter(storage);
|
|
67
66
|
}
|
|
@@ -8,13 +8,10 @@ import type { IUIProvider } from "../types/UI.types";
|
|
|
8
8
|
|
|
9
9
|
export class UIProviderAdapter implements IUIProvider {
|
|
10
10
|
private theme: DesignTokens | null = null;
|
|
11
|
-
|
|
12
|
-
private localization: any = null;
|
|
11
|
+
private localization: Record<string, unknown> | null = null;
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
constructor(theme?: DesignTokens, localization?: any) {
|
|
13
|
+
constructor(theme?: DesignTokens, localization?: Record<string, unknown>) {
|
|
16
14
|
this.theme = theme || null;
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
18
15
|
this.localization = localization || null;
|
|
19
16
|
}
|
|
20
17
|
|
|
@@ -22,8 +19,7 @@ export class UIProviderAdapter implements IUIProvider {
|
|
|
22
19
|
return this.theme;
|
|
23
20
|
}
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
getLocalization(): any {
|
|
22
|
+
getLocalization(): Record<string, unknown> | null {
|
|
27
23
|
return this.localization;
|
|
28
24
|
}
|
|
29
25
|
|
|
@@ -31,9 +27,7 @@ export class UIProviderAdapter implements IUIProvider {
|
|
|
31
27
|
this.theme = theme;
|
|
32
28
|
}
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
updateLocalization(localization: any): void {
|
|
36
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
30
|
+
updateLocalization(localization: Record<string, unknown>): void {
|
|
37
31
|
this.localization = localization;
|
|
38
32
|
}
|
|
39
33
|
}
|
|
@@ -43,8 +37,7 @@ export class UIProviderAdapter implements IUIProvider {
|
|
|
43
37
|
*/
|
|
44
38
|
export function createUIProvider(
|
|
45
39
|
theme?: DesignTokens,
|
|
46
|
-
|
|
47
|
-
localization?: any
|
|
40
|
+
localization?: Record<string, unknown>
|
|
48
41
|
): IUIProvider {
|
|
49
42
|
return new UIProviderAdapter(theme, localization);
|
|
50
|
-
}
|
|
43
|
+
}
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
type Auth,
|
|
15
15
|
} from "firebase/auth";
|
|
16
16
|
|
|
17
|
-
declare const __DEV__: boolean;
|
|
18
17
|
import type {
|
|
19
18
|
IAuthProvider,
|
|
20
19
|
AuthCredentials,
|
|
@@ -37,8 +36,6 @@ export class FirebaseAuthProvider implements IAuthProvider {
|
|
|
37
36
|
if (!this.auth) {
|
|
38
37
|
throw new Error("Firebase Auth instance must be provided");
|
|
39
38
|
}
|
|
40
|
-
// Satisfy require-await
|
|
41
|
-
await Promise.resolve();
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
setAuth(auth: Auth): void {
|
|
@@ -169,7 +166,7 @@ export class FirebaseAuthProvider implements IAuthProvider {
|
|
|
169
166
|
onAuthStateChange(callback: (user: AuthUser | null) => void): () => void {
|
|
170
167
|
if (!this.auth) {
|
|
171
168
|
callback(null);
|
|
172
|
-
return () => {
|
|
169
|
+
return () => {};
|
|
173
170
|
}
|
|
174
171
|
|
|
175
172
|
return onAuthStateChanged(this.auth, (user) => {
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
validateDisplayName,
|
|
21
21
|
} from "../utils/AuthValidation";
|
|
22
22
|
import type { AuthConfig } from "../../domain/value-objects/AuthConfig";
|
|
23
|
+
import { sanitizeEmail, sanitizeName, sanitizePassword } from "../utils/validation/sanitization";
|
|
23
24
|
|
|
24
25
|
export class AuthRepository implements IAuthRepository {
|
|
25
26
|
private provider: IAuthProvider;
|
|
@@ -31,50 +32,50 @@ export class AuthRepository implements IAuthRepository {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
async signUp(params: SignUpParams): Promise<AuthUser> {
|
|
35
|
+
const email = sanitizeEmail(params.email);
|
|
36
|
+
const password = sanitizePassword(params.password);
|
|
37
|
+
const displayName = params.displayName ? sanitizeName(params.displayName) : undefined;
|
|
38
|
+
|
|
34
39
|
// Validate email
|
|
35
|
-
const emailResult = validateEmail(
|
|
40
|
+
const emailResult = validateEmail(email);
|
|
36
41
|
if (!emailResult.isValid) {
|
|
37
42
|
throw new AuthInvalidEmailError(emailResult.error);
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
// Validate display name if provided
|
|
41
|
-
if (
|
|
42
|
-
const nameResult = validateDisplayName(
|
|
46
|
+
if (displayName) {
|
|
47
|
+
const nameResult = validateDisplayName(displayName);
|
|
43
48
|
if (!nameResult.isValid) {
|
|
44
49
|
throw new AuthValidationError(nameResult.error || "Invalid name", "displayName");
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
// Validate password strength for registration
|
|
49
|
-
const passwordResult = validatePasswordForRegister(
|
|
54
|
+
const passwordResult = validatePasswordForRegister(password, this.config.password);
|
|
50
55
|
if (!passwordResult.isValid) {
|
|
51
56
|
throw new AuthWeakPasswordError(passwordResult.error);
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
return this.provider.signUp({
|
|
55
|
-
email: params.email,
|
|
56
|
-
password: params.password,
|
|
57
|
-
displayName: params.displayName,
|
|
58
|
-
});
|
|
59
|
+
return this.provider.signUp({ email, password, displayName });
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
async signIn(params: SignInParams): Promise<AuthUser> {
|
|
63
|
+
const email = sanitizeEmail(params.email);
|
|
64
|
+
const password = sanitizePassword(params.password);
|
|
65
|
+
|
|
62
66
|
// Validate email format
|
|
63
|
-
const emailResult = validateEmail(
|
|
67
|
+
const emailResult = validateEmail(email);
|
|
64
68
|
if (!emailResult.isValid) {
|
|
65
69
|
throw new AuthInvalidEmailError(emailResult.error);
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
// For login, only check if password is provided (no strength requirements)
|
|
69
|
-
const passwordResult = validatePasswordForLogin(
|
|
73
|
+
const passwordResult = validatePasswordForLogin(password);
|
|
70
74
|
if (!passwordResult.isValid) {
|
|
71
75
|
throw new AuthValidationError(passwordResult.error || "Password is required", "password");
|
|
72
76
|
}
|
|
73
77
|
|
|
74
|
-
return this.provider.signIn({
|
|
75
|
-
email: params.email,
|
|
76
|
-
password: params.password,
|
|
77
|
-
});
|
|
78
|
+
return this.provider.signIn({ email, password });
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
async signOut(): Promise<void> {
|
|
@@ -21,7 +21,10 @@ export class AnonymousModeService {
|
|
|
21
21
|
const value = await storageProvider.get(this.storageKey);
|
|
22
22
|
this.isAnonymousMode = value === "true";
|
|
23
23
|
return this.isAnonymousMode;
|
|
24
|
-
} catch {
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (__DEV__) {
|
|
26
|
+
console.warn("[AnonymousModeService] Storage load failed:", error);
|
|
27
|
+
}
|
|
25
28
|
return false;
|
|
26
29
|
}
|
|
27
30
|
}
|
|
@@ -29,16 +32,20 @@ export class AnonymousModeService {
|
|
|
29
32
|
private async save(storageProvider: IStorageProvider): Promise<void> {
|
|
30
33
|
try {
|
|
31
34
|
await storageProvider.set(this.storageKey, this.isAnonymousMode.toString());
|
|
32
|
-
} catch {
|
|
33
|
-
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (__DEV__) {
|
|
37
|
+
console.error("[AnonymousModeService] Storage save failed:", err);
|
|
38
|
+
}
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
async clear(storageProvider: IStorageProvider): Promise<void> {
|
|
38
43
|
try {
|
|
39
44
|
await storageProvider.remove(this.storageKey);
|
|
40
|
-
} catch {
|
|
41
|
-
|
|
45
|
+
} catch (err) {
|
|
46
|
+
if (__DEV__) {
|
|
47
|
+
console.error("[AnonymousModeService] Storage clear failed:", err);
|
|
48
|
+
}
|
|
42
49
|
}
|
|
43
50
|
this.isAnonymousMode = false;
|
|
44
51
|
}
|
|
@@ -48,8 +55,10 @@ export class AnonymousModeService {
|
|
|
48
55
|
if (provider?.getCurrentUser()) {
|
|
49
56
|
try {
|
|
50
57
|
await provider.signOut();
|
|
51
|
-
} catch {
|
|
52
|
-
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (__DEV__) {
|
|
60
|
+
console.warn("[AnonymousModeService] Sign out failed during mode switch:", error);
|
|
61
|
+
}
|
|
53
62
|
}
|
|
54
63
|
}
|
|
55
64
|
|
|
@@ -88,8 +88,10 @@ export class AuthEventService {
|
|
|
88
88
|
eventListeners.forEach((listener) => {
|
|
89
89
|
try {
|
|
90
90
|
listener(payload);
|
|
91
|
-
} catch {
|
|
92
|
-
|
|
91
|
+
} catch (err) {
|
|
92
|
+
if (__DEV__) {
|
|
93
|
+
console.error("[AuthEventService] Listener error:", err);
|
|
94
|
+
}
|
|
93
95
|
}
|
|
94
96
|
});
|
|
95
97
|
}
|
|
@@ -45,10 +45,9 @@ export class AuthService implements IAuthService {
|
|
|
45
45
|
let provider: IAuthProvider;
|
|
46
46
|
|
|
47
47
|
if ("currentUser" in providerOrAuth) {
|
|
48
|
-
|
|
49
|
-
const firebaseProvider = new FirebaseAuthProvider(providerOrAuth as any);
|
|
48
|
+
const firebaseProvider = new FirebaseAuthProvider(providerOrAuth as Auth);
|
|
50
49
|
await firebaseProvider.initialize();
|
|
51
|
-
provider = firebaseProvider
|
|
50
|
+
provider = firebaseProvider;
|
|
52
51
|
} else {
|
|
53
52
|
provider = providerOrAuth;
|
|
54
53
|
await provider.initialize();
|
|
@@ -24,8 +24,6 @@ export type {
|
|
|
24
24
|
UserDocumentExtras,
|
|
25
25
|
} from "./UserDocument.types";
|
|
26
26
|
|
|
27
|
-
declare const __DEV__: boolean;
|
|
28
|
-
|
|
29
27
|
let userDocumentConfig: UserDocumentConfig = {};
|
|
30
28
|
|
|
31
29
|
export function configureUserDocumentService(config: UserDocumentConfig): void {
|
|
@@ -37,7 +35,7 @@ export async function ensureUserDocument(
|
|
|
37
35
|
extras?: UserDocumentExtras,
|
|
38
36
|
): Promise<boolean> {
|
|
39
37
|
const db = getFirestore();
|
|
40
|
-
if (!db || !user.uid) return false;
|
|
38
|
+
if (!db || !user.uid || user.uid.trim().length === 0) return false;
|
|
41
39
|
|
|
42
40
|
try {
|
|
43
41
|
let allExtras = extras || {};
|
|
@@ -60,8 +58,7 @@ export async function ensureUserDocument(
|
|
|
60
58
|
await setDoc(userRef, docData, { merge: true });
|
|
61
59
|
return true;
|
|
62
60
|
} catch (error) {
|
|
63
|
-
if (
|
|
64
|
-
// eslint-disable-next-line no-console
|
|
61
|
+
if (__DEV__) {
|
|
65
62
|
console.error("[UserDocumentService] Failed:", error);
|
|
66
63
|
}
|
|
67
64
|
return false;
|
|
@@ -80,7 +77,10 @@ export async function markUserDeleted(userId: string): Promise<boolean> {
|
|
|
80
77
|
updatedAt: serverTimestamp(),
|
|
81
78
|
}, { merge: true });
|
|
82
79
|
return true;
|
|
83
|
-
} catch {
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (__DEV__) {
|
|
82
|
+
console.error("[UserDocumentService] markUserDeleted failed:", error);
|
|
83
|
+
}
|
|
84
84
|
return false;
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -27,6 +27,7 @@ export interface InitializeAuthOptions {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
let isInitialized = false;
|
|
30
|
+
let initializationPromise: Promise<{ success: boolean; auth: Auth | null }> | null = null;
|
|
30
31
|
const conversionState: { current: ConversionState } = {
|
|
31
32
|
current: { previousUserId: null, wasAnonymous: false },
|
|
32
33
|
};
|
|
@@ -40,6 +41,21 @@ export async function initializeAuth(
|
|
|
40
41
|
if (isInitialized) {
|
|
41
42
|
return { success: true, auth: getFirebaseAuth() };
|
|
42
43
|
}
|
|
44
|
+
// Prevent race condition: return existing promise if initialization is in progress
|
|
45
|
+
if (initializationPromise) {
|
|
46
|
+
return initializationPromise;
|
|
47
|
+
}
|
|
48
|
+
initializationPromise = doInitializeAuth(options);
|
|
49
|
+
try {
|
|
50
|
+
return await initializationPromise;
|
|
51
|
+
} finally {
|
|
52
|
+
initializationPromise = null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function doInitializeAuth(
|
|
57
|
+
options: InitializeAuthOptions
|
|
58
|
+
): Promise<{ success: boolean; auth: Auth | null }> {
|
|
43
59
|
|
|
44
60
|
const {
|
|
45
61
|
userCollection = "users",
|
|
@@ -63,8 +79,10 @@ export async function initializeAuth(
|
|
|
63
79
|
|
|
64
80
|
try {
|
|
65
81
|
await initializeAuthService(auth, authConfig, storageProvider);
|
|
66
|
-
} catch {
|
|
67
|
-
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (__DEV__) {
|
|
84
|
+
console.warn("[initializeAuth] Auth service init failed, continuing:", error);
|
|
85
|
+
}
|
|
68
86
|
}
|
|
69
87
|
|
|
70
88
|
const handleAuthStateChange = createAuthStateHandler(conversionState, {
|
|
@@ -74,7 +92,13 @@ export async function initializeAuth(
|
|
|
74
92
|
|
|
75
93
|
initializeAuthListener({
|
|
76
94
|
autoAnonymousSignIn,
|
|
77
|
-
onAuthStateChange: (user) =>
|
|
95
|
+
onAuthStateChange: (user) => {
|
|
96
|
+
handleAuthStateChange(user).catch((err) => {
|
|
97
|
+
if (__DEV__) {
|
|
98
|
+
console.error("[initializeAuth] Auth state change handler error:", err);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
},
|
|
78
102
|
});
|
|
79
103
|
|
|
80
104
|
isInitialized = true;
|
|
@@ -7,6 +7,5 @@ import type { DesignTokens } from "@umituz/react-native-design-system";
|
|
|
7
7
|
|
|
8
8
|
export interface IUIProvider {
|
|
9
9
|
getTheme(): DesignTokens | null;
|
|
10
|
-
|
|
11
|
-
getLocalization(): any;
|
|
10
|
+
getLocalization(): Record<string, unknown> | null;
|
|
12
11
|
}
|
|
@@ -49,11 +49,6 @@ export function mapFirebaseAuthError(error: unknown): Error {
|
|
|
49
49
|
return new AuthWrongPasswordError();
|
|
50
50
|
|
|
51
51
|
case "auth/invalid-credential":
|
|
52
|
-
return new AuthError(
|
|
53
|
-
"Invalid email or password. Please check your credentials.",
|
|
54
|
-
"AUTH_INVALID_CREDENTIAL"
|
|
55
|
-
);
|
|
56
|
-
|
|
57
52
|
case "auth/invalid-login-credentials":
|
|
58
53
|
return new AuthError(
|
|
59
54
|
"Invalid email or password. Please check your credentials.",
|
|
@@ -33,7 +33,6 @@ export function validatePasswordForLogin(password: string): ValidationResult {
|
|
|
33
33
|
export function validatePasswordForRegister(
|
|
34
34
|
password: string,
|
|
35
35
|
config: PasswordConfig,
|
|
36
|
-
_validationConfig: ValidationConfig = DEFAULT_VAL_CONFIG
|
|
37
36
|
): PasswordStrengthResult {
|
|
38
37
|
const req: PasswordRequirements = {
|
|
39
38
|
hasMinLength: password.length >= config.minLength,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import type { AuthUser, AuthProviderType } from "../../domain/entities/AuthUser";
|
|
7
7
|
|
|
8
8
|
interface ProviderData {
|
|
9
|
-
providerId: string;
|
|
9
|
+
providerId: string | null;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
interface FirebaseUserLike {
|
|
@@ -16,7 +16,7 @@ interface FirebaseUserLike {
|
|
|
16
16
|
isAnonymous: boolean;
|
|
17
17
|
emailVerified: boolean;
|
|
18
18
|
photoURL: string | null;
|
|
19
|
-
providerData?: ProviderData[];
|
|
19
|
+
providerData?: (ProviderData | null)[];
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
@@ -32,13 +32,13 @@ function extractProvider(user: FirebaseUserLike): AuthProviderType {
|
|
|
32
32
|
return "unknown";
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
const googleProvider = providerData.find((p) => p
|
|
35
|
+
const googleProvider = providerData.find((p) => p?.providerId === "google.com");
|
|
36
36
|
if (googleProvider) return "google.com";
|
|
37
37
|
|
|
38
|
-
const appleProvider = providerData.find((p) => p
|
|
38
|
+
const appleProvider = providerData.find((p) => p?.providerId === "apple.com");
|
|
39
39
|
if (appleProvider) return "apple.com";
|
|
40
40
|
|
|
41
|
-
const passwordProvider = providerData.find((p) => p
|
|
41
|
+
const passwordProvider = providerData.find((p) => p?.providerId === "password");
|
|
42
42
|
if (passwordProvider) return "password";
|
|
43
43
|
|
|
44
44
|
return "unknown";
|
|
@@ -36,8 +36,10 @@ export function createAuthStateHandler(
|
|
|
36
36
|
if (conversion.isConversion && onUserConverted && state.current.previousUserId) {
|
|
37
37
|
try {
|
|
38
38
|
await onUserConverted(state.current.previousUserId, currentUserId);
|
|
39
|
-
} catch {
|
|
40
|
-
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (__DEV__) {
|
|
41
|
+
console.warn("[AuthStateHandler] User conversion callback failed:", error);
|
|
42
|
+
}
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -16,7 +16,7 @@ export function getSignUpMethod(user: UserDocumentUser): string | undefined {
|
|
|
16
16
|
if (user.isAnonymous) return "anonymous";
|
|
17
17
|
if (user.email) {
|
|
18
18
|
const providerData = (
|
|
19
|
-
user as unknown as { providerData?: { providerId: string }[] }
|
|
19
|
+
user as unknown as { providerData?: { providerId: string | null }[] }
|
|
20
20
|
).providerData;
|
|
21
21
|
if (providerData && providerData.length > 0) {
|
|
22
22
|
const providerId = providerData[0]?.providerId;
|
|
@@ -7,8 +7,6 @@ import type { InitModule } from '@umituz/react-native-design-system';
|
|
|
7
7
|
import { initializeAuth } from '../infrastructure/services/initializeAuth';
|
|
8
8
|
import type { InitializeAuthOptions } from '../infrastructure/services/initializeAuth';
|
|
9
9
|
|
|
10
|
-
declare const __DEV__: boolean;
|
|
11
|
-
|
|
12
10
|
export interface AuthInitModuleConfig {
|
|
13
11
|
/**
|
|
14
12
|
* Firestore collection name for user documents
|
|
@@ -104,7 +102,7 @@ export function createAuthInitModule(
|
|
|
104
102
|
collectExtras,
|
|
105
103
|
storageProvider,
|
|
106
104
|
onUserConverted: async (anonymousId: string, authenticatedId: string) => {
|
|
107
|
-
if (
|
|
105
|
+
if (__DEV__) {
|
|
108
106
|
console.log('[createAuthInitModule] User converted:', {
|
|
109
107
|
anonymousId: anonymousId.slice(0, 8),
|
|
110
108
|
authenticatedId: authenticatedId.slice(0, 8),
|
|
@@ -115,11 +113,11 @@ export function createAuthInitModule(
|
|
|
115
113
|
if (onRestorePurchases) {
|
|
116
114
|
try {
|
|
117
115
|
await onRestorePurchases();
|
|
118
|
-
if (
|
|
116
|
+
if (__DEV__) {
|
|
119
117
|
console.log('[createAuthInitModule] Purchases restored');
|
|
120
118
|
}
|
|
121
119
|
} catch (error) {
|
|
122
|
-
if (
|
|
120
|
+
if (__DEV__) {
|
|
123
121
|
console.error('[createAuthInitModule] Restore failed:', error);
|
|
124
122
|
}
|
|
125
123
|
}
|
|
@@ -132,13 +130,13 @@ export function createAuthInitModule(
|
|
|
132
130
|
},
|
|
133
131
|
});
|
|
134
132
|
|
|
135
|
-
if (
|
|
133
|
+
if (__DEV__) {
|
|
136
134
|
console.log('[createAuthInitModule] Auth initialized');
|
|
137
135
|
}
|
|
138
136
|
|
|
139
137
|
return true;
|
|
140
138
|
} catch (error) {
|
|
141
|
-
if (
|
|
139
|
+
if (__DEV__) {
|
|
142
140
|
console.error('[createAuthInitModule] Error:', error);
|
|
143
141
|
}
|
|
144
142
|
return false;
|
|
@@ -59,8 +59,10 @@ export const AccountActions: React.FC<AccountActionsProps> = ({ config }) => {
|
|
|
59
59
|
onPress: async () => {
|
|
60
60
|
try {
|
|
61
61
|
await onLogout();
|
|
62
|
-
} catch {
|
|
63
|
-
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (__DEV__) {
|
|
64
|
+
console.error("[AccountActions] Logout failed:", error);
|
|
65
|
+
}
|
|
64
66
|
}
|
|
65
67
|
},
|
|
66
68
|
},
|
|
@@ -13,8 +13,6 @@ import { RegisterForm, type RegisterFormTranslations } from "./RegisterForm";
|
|
|
13
13
|
import { SocialLoginButtons, type SocialLoginButtonsTranslations } from "./SocialLoginButtons";
|
|
14
14
|
import { styles } from "./AuthBottomSheet.styles";
|
|
15
15
|
|
|
16
|
-
declare const __DEV__: boolean;
|
|
17
|
-
|
|
18
16
|
export interface AuthBottomSheetTranslations {
|
|
19
17
|
close: string;
|
|
20
18
|
signIn: string;
|
|
@@ -36,8 +36,7 @@ export function useAppleAuth(): UseAppleAuthResult {
|
|
|
36
36
|
return { success: false, error: "Apple Sign-In is not available" };
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
return result;
|
|
39
|
+
return signInWithApple();
|
|
41
40
|
}, [appleAvailable, signInWithApple]);
|
|
42
41
|
|
|
43
42
|
return {
|
|
@@ -141,7 +141,10 @@ export function useAuth(): UseAuthResult {
|
|
|
141
141
|
setLoading(true);
|
|
142
142
|
await anonymousModeMutation.mutateAsync();
|
|
143
143
|
setIsAnonymous(true);
|
|
144
|
-
} catch {
|
|
144
|
+
} catch (error) {
|
|
145
|
+
if (__DEV__) {
|
|
146
|
+
console.warn("[useAuth] continueAnonymously failed:", error);
|
|
147
|
+
}
|
|
145
148
|
setIsAnonymous(true);
|
|
146
149
|
} finally {
|
|
147
150
|
setLoading(false);
|
|
@@ -7,8 +7,6 @@ import { useGoogleAuth, type GoogleAuthConfig } from "./useGoogleAuth";
|
|
|
7
7
|
import { useAppleAuth } from "./useAppleAuth";
|
|
8
8
|
import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
|
|
9
9
|
|
|
10
|
-
declare const __DEV__: boolean;
|
|
11
|
-
|
|
12
10
|
export interface SocialAuthConfiguration {
|
|
13
11
|
google?: GoogleAuthConfig;
|
|
14
12
|
apple?: { enabled: boolean };
|
|
@@ -92,7 +90,7 @@ export function useAuthBottomSheet(params: UseAuthBottomSheetParams = {}) {
|
|
|
92
90
|
const justAuthenticated = !prevIsAuthenticatedRef.current && isAuthenticated;
|
|
93
91
|
const justConvertedFromAnonymous = prevIsAnonymousRef.current && !isAnonymous && isAuthenticated;
|
|
94
92
|
|
|
95
|
-
if (
|
|
93
|
+
if (__DEV__) {
|
|
96
94
|
console.log("[useAuthBottomSheet] Auth state effect:", {
|
|
97
95
|
isAuthenticated,
|
|
98
96
|
isAnonymous,
|
|
@@ -106,7 +104,7 @@ export function useAuthBottomSheet(params: UseAuthBottomSheetParams = {}) {
|
|
|
106
104
|
}
|
|
107
105
|
|
|
108
106
|
if ((justAuthenticated || justConvertedFromAnonymous) && isVisible && !isAnonymous) {
|
|
109
|
-
if (
|
|
107
|
+
if (__DEV__) {
|
|
110
108
|
console.log("[useAuthBottomSheet] Auto-closing due to successful authentication transition", {
|
|
111
109
|
justAuthenticated,
|
|
112
110
|
justConvertedFromAnonymous,
|
|
@@ -119,7 +117,7 @@ export function useAuthBottomSheet(params: UseAuthBottomSheetParams = {}) {
|
|
|
119
117
|
onAuthSuccess?.();
|
|
120
118
|
// Execute callback with delay to ensure auth state has propagated
|
|
121
119
|
setTimeout(() => {
|
|
122
|
-
if (
|
|
120
|
+
if (__DEV__) {
|
|
123
121
|
console.log("[useAuthBottomSheet] Executing pending callback after auth");
|
|
124
122
|
}
|
|
125
123
|
executePendingCallback();
|
|
@@ -71,8 +71,10 @@ export function useGoogleAuth(config?: GoogleAuthConfig): UseGoogleAuthResult {
|
|
|
71
71
|
if (idToken) {
|
|
72
72
|
setIsLoading(true);
|
|
73
73
|
signInWithGoogleToken(idToken)
|
|
74
|
-
.catch(() => {
|
|
75
|
-
|
|
74
|
+
.catch((error) => {
|
|
75
|
+
if (__DEV__) {
|
|
76
|
+
console.error("[useGoogleAuth] Firebase sign-in failed:", error);
|
|
77
|
+
}
|
|
76
78
|
})
|
|
77
79
|
.finally(() => {
|
|
78
80
|
setIsLoading(false);
|
|
@@ -100,8 +100,10 @@ export function useLoginForm(config?: UseLoginFormConfig): UseLoginFormResult {
|
|
|
100
100
|
const handleContinueAnonymously = useCallback(async () => {
|
|
101
101
|
try {
|
|
102
102
|
await continueAnonymously();
|
|
103
|
-
} catch {
|
|
104
|
-
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (__DEV__) {
|
|
105
|
+
console.warn("[useLoginForm] Continue anonymously failed:", error);
|
|
106
|
+
}
|
|
105
107
|
}
|
|
106
108
|
}, [continueAnonymously]);
|
|
107
109
|
|
|
@@ -122,19 +122,19 @@ export function useRegisterForm(config?: UseRegisterFormConfig): UseRegisterForm
|
|
|
122
122
|
|
|
123
123
|
const emailResult = validateEmail(email.trim());
|
|
124
124
|
if (!emailResult.isValid && emailResult.error) {
|
|
125
|
-
setFieldErrors((prev) => ({ ...prev, email: getErrorMessage(emailResult.error
|
|
125
|
+
setFieldErrors((prev) => ({ ...prev, email: getErrorMessage(emailResult.error!) }));
|
|
126
126
|
return;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
const passwordResult = validatePasswordForRegister(password, DEFAULT_PASSWORD_CONFIG);
|
|
130
130
|
if (!passwordResult.isValid && passwordResult.error) {
|
|
131
|
-
setFieldErrors((prev) => ({ ...prev, password: getErrorMessage(passwordResult.error
|
|
131
|
+
setFieldErrors((prev) => ({ ...prev, password: getErrorMessage(passwordResult.error!) }));
|
|
132
132
|
return;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
const confirmResult = validatePasswordConfirmation(password, confirmPassword);
|
|
136
136
|
if (!confirmResult.isValid && confirmResult.error) {
|
|
137
|
-
setFieldErrors((prev) => ({ ...prev, confirmPassword: getErrorMessage(confirmResult.error
|
|
137
|
+
setFieldErrors((prev) => ({ ...prev, confirmPassword: getErrorMessage(confirmResult.error!) }));
|
|
138
138
|
return;
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -80,8 +80,7 @@ export function useSocialLogin(config?: UseSocialLoginConfig): UseSocialLoginRes
|
|
|
80
80
|
return { success: false, error: "Apple Sign-In is not available" };
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
return result;
|
|
83
|
+
return firebaseSignInWithApple();
|
|
85
84
|
}, [appleAvailable, firebaseSignInWithApple]);
|
|
86
85
|
|
|
87
86
|
return {
|
|
@@ -53,7 +53,7 @@ export const useAuthModalStore = createStore<AuthModalState, AuthModalActions>({
|
|
|
53
53
|
callback?: () => void | Promise<void>,
|
|
54
54
|
mode: AuthModalMode = "login",
|
|
55
55
|
) => {
|
|
56
|
-
if (
|
|
56
|
+
if (__DEV__) {
|
|
57
57
|
console.log("[authModalStore] showAuthModal called:", {
|
|
58
58
|
mode,
|
|
59
59
|
hasCallback: !!callback,
|
|
@@ -72,7 +72,7 @@ export const useAuthStore = createStore<AuthState, AuthActions>({
|
|
|
72
72
|
initialized: state.initialized,
|
|
73
73
|
}),
|
|
74
74
|
migrate: (persistedState: unknown, version: number) => {
|
|
75
|
-
const state = persistedState as Partial<AuthState>;
|
|
75
|
+
const state = (persistedState && typeof persistedState === "object" ? persistedState : {}) as Partial<AuthState>;
|
|
76
76
|
if (version < 2) {
|
|
77
77
|
return {
|
|
78
78
|
...initialAuthState,
|
|
@@ -80,7 +80,7 @@ export const useAuthStore = createStore<AuthState, AuthActions>({
|
|
|
80
80
|
initialized: state.initialized ?? false,
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
|
-
return { ...initialAuthState, ...state }
|
|
83
|
+
return { ...initialAuthState, ...state };
|
|
84
84
|
},
|
|
85
85
|
actions: (set, get) => ({
|
|
86
86
|
setFirebaseUser: (firebaseUser) => {
|
|
@@ -13,7 +13,8 @@ import { useAuthStore } from "./authStore";
|
|
|
13
13
|
import { getAuthService } from "../../infrastructure/services/AuthService";
|
|
14
14
|
import type { AuthListenerOptions } from "../../types/auth-store.types";
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
const MAX_ANONYMOUS_RETRIES = 2;
|
|
17
|
+
const ANONYMOUS_RETRY_DELAY_MS = 1000;
|
|
17
18
|
|
|
18
19
|
let listenerInitialized = false;
|
|
19
20
|
// Reference counter for multiple subscribers
|
|
@@ -106,10 +107,7 @@ export function initializeAuthListener(
|
|
|
106
107
|
store.setLoading(true);
|
|
107
108
|
|
|
108
109
|
void (async () => {
|
|
109
|
-
|
|
110
|
-
const RETRY_DELAY_MS = 1000;
|
|
111
|
-
|
|
112
|
-
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
110
|
+
for (let attempt = 0; attempt <= MAX_ANONYMOUS_RETRIES; attempt++) {
|
|
113
111
|
try {
|
|
114
112
|
await anonymousAuthService.signInAnonymously(auth);
|
|
115
113
|
if (__DEV__) {
|
|
@@ -123,8 +121,8 @@ export function initializeAuthListener(
|
|
|
123
121
|
}
|
|
124
122
|
|
|
125
123
|
// If not last attempt, wait and retry
|
|
126
|
-
if (attempt <
|
|
127
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
124
|
+
if (attempt < MAX_ANONYMOUS_RETRIES) {
|
|
125
|
+
await new Promise(resolve => setTimeout(resolve, ANONYMOUS_RETRY_DELAY_MS));
|
|
128
126
|
continue;
|
|
129
127
|
}
|
|
130
128
|
|