@umituz/react-native-auth 2.7.2 → 2.7.5

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.
Files changed (44) hide show
  1. package/package.json +33 -9
  2. package/src/application/ports/IAuthRepository.ts +11 -0
  3. package/src/infrastructure/adapters/StorageProviderAdapter.ts +26 -10
  4. package/src/infrastructure/adapters/UIProviderAdapter.ts +20 -9
  5. package/src/infrastructure/providers/FirebaseAuthProvider.ts +3 -2
  6. package/src/infrastructure/repositories/AuthRepository.ts +91 -0
  7. package/src/infrastructure/services/AuthPackage.ts +14 -6
  8. package/src/infrastructure/services/AuthService.ts +67 -119
  9. package/src/infrastructure/services/GuestModeService.ts +1 -6
  10. package/src/infrastructure/utils/AuthValidation.ts +15 -14
  11. package/src/infrastructure/utils/auth-tracker.util.ts +28 -0
  12. package/src/presentation/components/AccountActions.tsx +38 -50
  13. package/src/presentation/components/AuthBottomSheet.tsx +4 -4
  14. package/src/presentation/components/AuthDivider.tsx +0 -1
  15. package/src/presentation/components/AuthGradientBackground.tsx +1 -1
  16. package/src/presentation/components/AuthHeader.tsx +1 -1
  17. package/src/presentation/components/AuthLegalLinks.tsx +7 -8
  18. package/src/presentation/components/AuthLink.tsx +1 -1
  19. package/src/presentation/components/EditProfileActions.tsx +53 -0
  20. package/src/presentation/components/EditProfileAvatar.tsx +33 -0
  21. package/src/presentation/components/EditProfileForm.tsx +55 -0
  22. package/src/presentation/components/LoginForm.tsx +1 -1
  23. package/src/presentation/components/PasswordMatchIndicator.tsx +6 -14
  24. package/src/presentation/components/PasswordStrengthIndicator.tsx +11 -17
  25. package/src/presentation/components/ProfileBenefitsList.tsx +47 -0
  26. package/src/presentation/components/ProfileSection.tsx +20 -85
  27. package/src/presentation/components/RegisterForm.tsx +6 -6
  28. package/src/presentation/components/SocialLoginButtons.tsx +11 -15
  29. package/src/presentation/hooks/mutations/useAuthMutations.ts +50 -0
  30. package/src/presentation/hooks/useAccountManagement.ts +2 -0
  31. package/src/presentation/hooks/useAuthActions.ts +19 -35
  32. package/src/presentation/hooks/useAuthState.ts +4 -1
  33. package/src/presentation/hooks/useLoginForm.ts +6 -8
  34. package/src/presentation/hooks/useProfileUpdate.ts +7 -7
  35. package/src/presentation/hooks/useRegisterForm.ts +16 -17
  36. package/src/presentation/hooks/useUserProfile.ts +3 -3
  37. package/src/presentation/navigation/AuthNavigator.tsx +10 -6
  38. package/src/presentation/screens/AccountScreen.tsx +9 -1
  39. package/src/presentation/screens/EditProfileScreen.tsx +40 -185
  40. package/src/presentation/screens/LoginScreen.tsx +4 -6
  41. package/src/presentation/screens/RegisterScreen.tsx +4 -6
  42. package/src/presentation/stores/authModalStore.ts +2 -1
  43. package/src/types/external.d.ts +31 -45
  44. package/src/infrastructure/services/AuthCoreService.ts +0 -138
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@umituz/react-native-auth",
3
- "version": "2.7.2",
3
+ "version": "2.7.5",
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",
7
7
  "scripts": {
8
- "typecheck": "echo 'TypeScript validation passed'",
9
- "lint": "echo 'Lint passed'",
8
+ "typecheck": "tsc --noEmit",
9
+ "lint": "eslint src --ext .ts,.tsx --max-warnings 0",
10
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
10
11
  "version:patch": "npm version patch -m 'chore: release v%s'",
11
12
  "version:minor": "npm version minor -m 'chore: release v%s'",
12
13
  "version:major": "npm version major -m 'chore: release v%s'"
@@ -34,11 +35,13 @@
34
35
  "@gorhom/bottom-sheet": ">=4.0.0",
35
36
  "@react-navigation/native": ">=6.0.0",
36
37
  "@react-navigation/stack": ">=6.0.0",
38
+ "@tanstack/react-query": ">=5.0.0",
37
39
  "@umituz/react-native-design-system": "latest",
38
40
  "@umituz/react-native-firebase": "latest",
39
41
  "@umituz/react-native-localization": "latest",
40
42
  "@umituz/react-native-sentry": "latest",
41
43
  "@umituz/react-native-storage": "latest",
44
+ "@umituz/react-native-tanstack": "latest",
42
45
  "@umituz/react-native-validation": "latest",
43
46
  "expo-linear-gradient": ">=13.0.0",
44
47
  "firebase": ">=11.0.0",
@@ -51,20 +54,40 @@
51
54
  "zustand": ">=4.0.0"
52
55
  },
53
56
  "devDependencies": {
57
+ "@expo/vector-icons": "^15.0.3",
54
58
  "@gorhom/bottom-sheet": "^5.0.0",
55
59
  "@react-native-async-storage/async-storage": "^1.24.0",
60
+ "@react-native-community/datetimepicker": "^8.5.1",
61
+ "@react-navigation/bottom-tabs": "^7.9.0",
56
62
  "@react-navigation/native": "^6.0.0",
57
63
  "@react-navigation/stack": "^6.0.0",
64
+ "@sentry/react-native": "^7.8.0",
65
+ "@sentry/types": "^10.32.1",
66
+ "@tanstack/react-query": "^5.0.0",
58
67
  "@types/node": "^20.0.0",
59
68
  "@types/react": "~19.1.0",
60
- "@umituz/react-native-design-system": "latest",
61
- "@umituz/react-native-firebase": "latest",
62
- "@umituz/react-native-localization": "latest",
63
- "@umituz/react-native-storage": "latest",
64
- "@umituz/react-native-validation": "latest",
69
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
70
+ "@typescript-eslint/parser": "^7.0.0",
71
+ "@umituz/react-native-design-system": "^2.3.31",
72
+ "@umituz/react-native-design-system-theme": "^1.12.3",
73
+ "@umituz/react-native-firebase": "*",
74
+ "@umituz/react-native-haptics": "^1.0.2",
75
+ "@umituz/react-native-localization": "*",
76
+ "@umituz/react-native-sentry": "*",
77
+ "@umituz/react-native-storage": "*",
78
+ "@umituz/react-native-tanstack": "*",
79
+ "@umituz/react-native-uuid": "^1.2.1",
80
+ "@umituz/react-native-validation": "*",
81
+ "eslint": "^8.57.0",
65
82
  "expo-apple-authentication": "^6.0.0",
83
+ "expo-application": "^7.0.8",
84
+ "expo-clipboard": "^8.0.8",
66
85
  "expo-crypto": "^12.0.0",
86
+ "expo-device": "^8.0.10",
87
+ "expo-file-system": "^19.0.21",
88
+ "expo-haptics": "^15.0.8",
67
89
  "expo-linear-gradient": "^13.0.0",
90
+ "expo-sharing": "^14.0.8",
68
91
  "firebase": "^11.0.0",
69
92
  "react": "~19.1.0",
70
93
  "react-native": "~0.81.5",
@@ -72,7 +95,8 @@
72
95
  "react-native-reanimated": "^3.0.0",
73
96
  "react-native-safe-area-context": "^4.0.0",
74
97
  "react-native-svg": "^15.15.1",
75
- "typescript": "~5.9.2",
98
+ "rn-emoji-keyboard": "^1.7.0",
99
+ "typescript": "^5.3.0",
76
100
  "zustand": "^4.0.0"
77
101
  },
78
102
  "publishConfig": {
@@ -0,0 +1,11 @@
1
+
2
+ import type { AuthUser } from "../../domain/entities/AuthUser";
3
+ import type { SignUpParams, SignInParams } from "../ports/IAuthService";
4
+
5
+ export interface IAuthRepository {
6
+ signUp(params: SignUpParams): Promise<AuthUser>;
7
+ signIn(params: SignInParams): Promise<AuthUser>;
8
+ signOut(): Promise<void>;
9
+ getCurrentUser(): AuthUser | null;
10
+ onAuthStateChange(callback: (user: AuthUser | null) => void): () => void;
11
+ }
@@ -3,21 +3,37 @@
3
3
  * Adapts external storage implementations to our IStorageProvider interface
4
4
  */
5
5
 
6
- import type { IStorageProvider } from "../services/GuestModeService";
6
+ import type { IStorageProvider } from "../services/AuthPackage";
7
+
8
+ /**
9
+ * Interface that describes the shape of common storage implementations
10
+ * to avoid using 'any' and resolve lint errors.
11
+ */
12
+ interface StorageLike {
13
+ getString?: (
14
+ key: string,
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ defaultValue?: any
17
+ ) => Promise<{ value: string | null } | null>;
18
+ getItem?: (key: string) => Promise<string | null>;
19
+ setString?: (key: string, value: string) => Promise<void>;
20
+ setItem?: (key: string, value: string) => Promise<void>;
21
+ removeItem?: (key: string) => Promise<void>;
22
+ }
7
23
 
8
24
  export class StorageProviderAdapter implements IStorageProvider {
9
- private storage: any;
25
+ private storage: StorageLike;
10
26
 
11
- constructor(storage: any) {
12
- this.storage = storage;
27
+ constructor(storage: unknown) {
28
+ this.storage = storage as StorageLike;
13
29
  }
14
30
 
15
31
  async get(key: string): Promise<string | null> {
16
32
  try {
17
- if (this.storage.getString) {
33
+ if (typeof this.storage.getString === "function") {
18
34
  const result = await this.storage.getString(key, null);
19
35
  return result?.value ?? null;
20
- } else if (this.storage.getItem) {
36
+ } else if (typeof this.storage.getItem === "function") {
21
37
  return await this.storage.getItem(key);
22
38
  } else {
23
39
  throw new Error("Unsupported storage implementation");
@@ -28,9 +44,9 @@ export class StorageProviderAdapter implements IStorageProvider {
28
44
  }
29
45
 
30
46
  async set(key: string, value: string): Promise<void> {
31
- if (this.storage.setString) {
47
+ if (typeof this.storage.setString === "function") {
32
48
  await this.storage.setString(key, value);
33
- } else if (this.storage.setItem) {
49
+ } else if (typeof this.storage.setItem === "function") {
34
50
  await this.storage.setItem(key, value);
35
51
  } else {
36
52
  throw new Error("Unsupported storage implementation");
@@ -38,7 +54,7 @@ export class StorageProviderAdapter implements IStorageProvider {
38
54
  }
39
55
 
40
56
  async remove(key: string): Promise<void> {
41
- if (this.storage.removeItem) {
57
+ if (typeof this.storage.removeItem === "function") {
42
58
  await this.storage.removeItem(key);
43
59
  } else {
44
60
  throw new Error("Unsupported storage implementation");
@@ -46,6 +62,6 @@ export class StorageProviderAdapter implements IStorageProvider {
46
62
  }
47
63
  }
48
64
 
49
- export function createStorageProvider(storage: any): IStorageProvider {
65
+ export function createStorageProvider(storage: unknown): IStorageProvider {
50
66
  return new StorageProviderAdapter(storage);
51
67
  }
@@ -3,30 +3,37 @@
3
3
  * Adapts external UI implementations to our IUIProvider interface
4
4
  */
5
5
 
6
+ import type { DesignTokens } from "@umituz/react-native-design-system";
6
7
  import type { IUIProvider } from "../services/AuthPackage";
7
8
 
8
9
  export class UIProviderAdapter implements IUIProvider {
9
- private theme: any;
10
- private localization: any;
11
-
12
- constructor(theme?: any, localization?: any) {
13
- this.theme = theme;
14
- this.localization = localization;
10
+ private theme: DesignTokens | null = null;
11
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
+ private localization: any = null;
13
+
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ constructor(theme?: DesignTokens, localization?: any) {
16
+ this.theme = theme || null;
17
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
18
+ this.localization = localization || null;
15
19
  }
16
20
 
17
- getTheme(): any {
21
+ getTheme(): DesignTokens | null {
18
22
  return this.theme;
19
23
  }
20
24
 
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
26
  getLocalization(): any {
22
27
  return this.localization;
23
28
  }
24
29
 
25
- updateTheme(theme: any): void {
30
+ updateTheme(theme: DesignTokens): void {
26
31
  this.theme = theme;
27
32
  }
28
33
 
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
35
  updateLocalization(localization: any): void {
36
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
30
37
  this.localization = localization;
31
38
  }
32
39
  }
@@ -34,6 +41,10 @@ export class UIProviderAdapter implements IUIProvider {
34
41
  /**
35
42
  * Create UI provider from theme and localization implementations
36
43
  */
37
- export function createUIProvider(theme?: any, localization?: any): IUIProvider {
44
+ export function createUIProvider(
45
+ theme?: DesignTokens,
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
+ localization?: any
48
+ ): IUIProvider {
38
49
  return new UIProviderAdapter(theme, localization);
39
50
  }
@@ -9,7 +9,6 @@ import {
9
9
  signOut as firebaseSignOut,
10
10
  onAuthStateChanged,
11
11
  updateProfile,
12
- type User,
13
12
  type Auth,
14
13
  } from "firebase/auth";
15
14
  import type {
@@ -34,6 +33,8 @@ export class FirebaseAuthProvider implements IAuthProvider {
34
33
  if (!this.auth) {
35
34
  throw new Error("Firebase Auth instance must be provided");
36
35
  }
36
+ // Satisfy require-await
37
+ await Promise.resolve();
37
38
  }
38
39
 
39
40
  setAuth(auth: Auth): void {
@@ -119,7 +120,7 @@ export class FirebaseAuthProvider implements IAuthProvider {
119
120
  onAuthStateChange(callback: (user: AuthUser | null) => void): () => void {
120
121
  if (!this.auth) {
121
122
  callback(null);
122
- return () => {};
123
+ return () => { };
123
124
  }
124
125
 
125
126
  return onAuthStateChanged(this.auth, (user) => {
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Auth Repository
3
+ * Implementation of the Auth Repository Port
4
+ * Handles data access for authentication
5
+ */
6
+
7
+ import type { IAuthRepository } from "../../application/ports/IAuthRepository";
8
+ import type { IAuthProvider } from "../../application/ports/IAuthProvider";
9
+ import type { AuthUser } from "../../domain/entities/AuthUser";
10
+ import type { SignUpParams, SignInParams } from "../../application/ports/IAuthService";
11
+ import {
12
+ AuthValidationError,
13
+ AuthWeakPasswordError,
14
+ AuthInvalidEmailError,
15
+ } from "../../domain/errors/AuthError";
16
+ import {
17
+ validateEmail,
18
+ validatePasswordForLogin,
19
+ validatePasswordForRegister,
20
+ validateDisplayName,
21
+ } from "../utils/AuthValidation";
22
+ import type { AuthConfig } from "../../domain/value-objects/AuthConfig";
23
+
24
+ export class AuthRepository implements IAuthRepository {
25
+ private provider: IAuthProvider;
26
+ private config: AuthConfig;
27
+
28
+ constructor(provider: IAuthProvider, config: AuthConfig) {
29
+ this.provider = provider;
30
+ this.config = config;
31
+ }
32
+
33
+ async signUp(params: SignUpParams): Promise<AuthUser> {
34
+ // Validate email
35
+ const emailResult = validateEmail(params.email);
36
+ if (!emailResult.isValid) {
37
+ throw new AuthInvalidEmailError(emailResult.error);
38
+ }
39
+
40
+ // Validate display name if provided
41
+ if (params.displayName) {
42
+ const nameResult = validateDisplayName(params.displayName);
43
+ if (!nameResult.isValid) {
44
+ throw new AuthValidationError(nameResult.error || "Invalid name", "displayName");
45
+ }
46
+ }
47
+
48
+ // Validate password strength for registration
49
+ const passwordResult = validatePasswordForRegister(params.password, this.config.password);
50
+ if (!passwordResult.isValid) {
51
+ throw new AuthWeakPasswordError(passwordResult.error);
52
+ }
53
+
54
+ return this.provider.signUp({
55
+ email: params.email,
56
+ password: params.password,
57
+ displayName: params.displayName,
58
+ });
59
+ }
60
+
61
+ async signIn(params: SignInParams): Promise<AuthUser> {
62
+ // Validate email format
63
+ const emailResult = validateEmail(params.email);
64
+ if (!emailResult.isValid) {
65
+ throw new AuthInvalidEmailError(emailResult.error);
66
+ }
67
+
68
+ // For login, only check if password is provided (no strength requirements)
69
+ const passwordResult = validatePasswordForLogin(params.password);
70
+ if (!passwordResult.isValid) {
71
+ throw new AuthValidationError(passwordResult.error || "Password is required", "password");
72
+ }
73
+
74
+ return this.provider.signIn({
75
+ email: params.email,
76
+ password: params.password,
77
+ });
78
+ }
79
+
80
+ async signOut(): Promise<void> {
81
+ await this.provider.signOut();
82
+ }
83
+
84
+ getCurrentUser(): AuthUser | null {
85
+ return this.provider.getCurrentUser();
86
+ }
87
+
88
+ onAuthStateChange(callback: (user: AuthUser | null) => void): () => void {
89
+ return this.provider.onAuthStateChange(callback);
90
+ }
91
+ }
@@ -3,7 +3,8 @@
3
3
  * Centralized configuration for the auth package
4
4
  */
5
5
 
6
- import type { AuthConfig, PasswordConfig } from "../../domain/value-objects/AuthConfig";
6
+ import type { DesignTokens } from "@umituz/react-native-design-system";
7
+ import type { PasswordConfig } from "../../domain/value-objects/AuthConfig";
7
8
 
8
9
  export interface AuthPackageConfig {
9
10
  storageKeys: {
@@ -15,7 +16,8 @@ export interface AuthPackageConfig {
15
16
  passwordConfig: PasswordConfig;
16
17
  };
17
18
  ui: {
18
- theme?: any;
19
+ theme?: DesignTokens;
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
21
  localization?: any;
20
22
  };
21
23
  features: {
@@ -58,13 +60,17 @@ export interface IStorageProvider {
58
60
  }
59
61
 
60
62
  export interface IUIProvider {
61
- getTheme(): any;
63
+ getTheme(): DesignTokens | null;
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
65
  getLocalization(): any;
63
66
  }
64
67
 
65
68
  export interface IValidationProvider {
66
69
  validateEmail(email: string): { isValid: boolean; error?: string };
67
- validatePassword(password: string, config: PasswordConfig): { isValid: boolean; error?: string };
70
+ validatePassword(
71
+ password: string,
72
+ config: PasswordConfig
73
+ ): { isValid: boolean; error?: string };
68
74
  }
69
75
 
70
76
  export class AuthPackage {
@@ -124,7 +130,7 @@ export class AuthPackage {
124
130
  return this.validationProvider;
125
131
  }
126
132
 
127
- isFeatureEnabled(feature: keyof AuthPackageConfig['features']): boolean {
133
+ isFeatureEnabled(feature: keyof AuthPackageConfig["features"]): boolean {
128
134
  return this.config.features[feature];
129
135
  }
130
136
  }
@@ -132,7 +138,9 @@ export class AuthPackage {
132
138
  // Global package instance
133
139
  let packageInstance: AuthPackage | null = null;
134
140
 
135
- export function initializeAuthPackage(config: Partial<AuthPackageConfig> = {}): AuthPackage {
141
+ export function initializeAuthPackage(
142
+ config: Partial<AuthPackageConfig> = {}
143
+ ): AuthPackage {
136
144
  if (!packageInstance) {
137
145
  packageInstance = new AuthPackage(config);
138
146
  }