@umituz/react-native-auth 2.5.10 → 2.6.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": "2.5.10",
3
+ "version": "2.6.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",
@@ -28,34 +28,36 @@
28
28
  "url": "git+https://github.com/umituz/react-native-auth.git"
29
29
  },
30
30
  "peerDependencies": {
31
- "firebase": ">=11.0.0",
32
- "react": ">=18.2.0",
33
- "react-native": ">=0.74.0",
34
- "@umituz/react-native-firebase-auth": "latest",
35
- "@react-navigation/stack": ">=6.0.0",
31
+ "@gorhom/bottom-sheet": ">=4.0.0",
36
32
  "@react-navigation/native": ">=6.0.0",
33
+ "@react-navigation/stack": ">=6.0.0",
37
34
  "@umituz/react-native-design-system": "latest",
38
35
  "@umituz/react-native-design-system-theme": "latest",
36
+ "@umituz/react-native-firebase-auth": "latest",
39
37
  "@umituz/react-native-localization": "latest",
40
- "@umituz/react-native-validation": "latest",
41
38
  "@umituz/react-native-storage": "latest",
39
+ "@umituz/react-native-validation": "latest",
42
40
  "expo-linear-gradient": ">=13.0.0",
43
- "react-native-safe-area-context": ">=4.0.0",
44
- "zustand": ">=4.0.0",
45
- "@gorhom/bottom-sheet": ">=4.0.0",
41
+ "firebase": ">=11.0.0",
42
+ "react": ">=18.2.0",
43
+ "react-native": ">=0.74.0",
44
+ "react-native-gesture-handler": ">=2.0.0",
46
45
  "react-native-reanimated": ">=3.0.0",
47
- "react-native-gesture-handler": ">=2.0.0"
46
+ "react-native-safe-area-context": ">=4.0.0",
47
+ "react-native-svg": ">=13.0.0",
48
+ "zustand": ">=4.0.0"
48
49
  },
49
50
  "devDependencies": {
50
- "@types/react": "~19.1.0",
51
+ "@gorhom/bottom-sheet": "^5.0.0",
51
52
  "@types/node": "^20.0.0",
52
- "typescript": "~5.9.2",
53
- "react-native": "~0.76.0",
53
+ "@types/react": "~19.1.0",
54
54
  "firebase": "^11.0.0",
55
- "zustand": "^4.0.0",
56
- "@gorhom/bottom-sheet": "^5.0.0",
55
+ "react-native": "~0.76.0",
56
+ "react-native-gesture-handler": "^2.0.0",
57
57
  "react-native-reanimated": "^3.0.0",
58
- "react-native-gesture-handler": "^2.0.0"
58
+ "react-native-svg": "^15.15.1",
59
+ "typescript": "~5.9.2",
60
+ "zustand": "^4.0.0"
59
61
  },
60
62
  "publishConfig": {
61
63
  "access": "public"
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { AuthUser } from "../../domain/entities/AuthUser";
7
+ import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
7
8
 
8
9
  export interface AuthCredentials {
9
10
  email: string;
@@ -14,6 +15,14 @@ export interface SignUpCredentials extends AuthCredentials {
14
15
  displayName?: string;
15
16
  }
16
17
 
18
+ /**
19
+ * Result from social sign-in operations
20
+ */
21
+ export interface SocialSignInResult {
22
+ user: AuthUser;
23
+ isNewUser: boolean;
24
+ }
25
+
17
26
  export interface IAuthProvider {
18
27
  /**
19
28
  * Initialize the auth provider
@@ -35,6 +44,23 @@ export interface IAuthProvider {
35
44
  */
36
45
  signUp(credentials: SignUpCredentials): Promise<AuthUser>;
37
46
 
47
+ /**
48
+ * Sign in with Google
49
+ * @returns User and whether this is a new user
50
+ */
51
+ signInWithGoogle?(): Promise<SocialSignInResult>;
52
+
53
+ /**
54
+ * Sign in with Apple
55
+ * @returns User and whether this is a new user
56
+ */
57
+ signInWithApple?(): Promise<SocialSignInResult>;
58
+
59
+ /**
60
+ * Check if a social provider is supported
61
+ */
62
+ isSocialProviderSupported?(provider: SocialAuthProvider): boolean;
63
+
38
64
  /**
39
65
  * Sign out current user
40
66
  */
@@ -11,8 +11,39 @@ export interface PasswordConfig {
11
11
  requireSpecialChar: boolean;
12
12
  }
13
13
 
14
+ /**
15
+ * Social authentication provider configuration
16
+ */
17
+ export interface SocialProviderConfig {
18
+ enabled: boolean;
19
+ }
20
+
21
+ export interface GoogleAuthConfig extends SocialProviderConfig {
22
+ webClientId?: string;
23
+ iosClientId?: string;
24
+ androidClientId?: string;
25
+ }
26
+
27
+ export interface AppleAuthConfig extends SocialProviderConfig {
28
+ // Apple Sign In doesn't require additional config for basic usage
29
+ }
30
+
31
+ /**
32
+ * Social authentication configuration
33
+ */
34
+ export interface SocialAuthConfig {
35
+ google?: GoogleAuthConfig;
36
+ apple?: AppleAuthConfig;
37
+ }
38
+
39
+ /**
40
+ * Supported social auth providers
41
+ */
42
+ export type SocialAuthProvider = "google" | "apple";
43
+
14
44
  export interface AuthConfig {
15
45
  password: PasswordConfig;
46
+ social?: SocialAuthConfig;
16
47
  }
17
48
 
18
49
  export const DEFAULT_PASSWORD_CONFIG: PasswordConfig = {
@@ -23,7 +54,13 @@ export const DEFAULT_PASSWORD_CONFIG: PasswordConfig = {
23
54
  requireSpecialChar: false,
24
55
  };
25
56
 
57
+ export const DEFAULT_SOCIAL_CONFIG: SocialAuthConfig = {
58
+ google: { enabled: false },
59
+ apple: { enabled: false },
60
+ };
61
+
26
62
  export const DEFAULT_AUTH_CONFIG: AuthConfig = {
27
63
  password: DEFAULT_PASSWORD_CONFIG,
64
+ social: DEFAULT_SOCIAL_CONFIG,
28
65
  };
29
66
 
package/src/index.ts CHANGED
@@ -43,8 +43,20 @@ export {
43
43
  // DOMAIN LAYER - Value Objects
44
44
  // =============================================================================
45
45
 
46
- export type { AuthConfig, PasswordConfig } from './domain/value-objects/AuthConfig';
47
- export { DEFAULT_AUTH_CONFIG, DEFAULT_PASSWORD_CONFIG } from './domain/value-objects/AuthConfig';
46
+ export type {
47
+ AuthConfig,
48
+ PasswordConfig,
49
+ SocialAuthConfig,
50
+ SocialProviderConfig,
51
+ GoogleAuthConfig,
52
+ AppleAuthConfig,
53
+ SocialAuthProvider,
54
+ } from './domain/value-objects/AuthConfig';
55
+ export {
56
+ DEFAULT_AUTH_CONFIG,
57
+ DEFAULT_PASSWORD_CONFIG,
58
+ DEFAULT_SOCIAL_CONFIG,
59
+ } from './domain/value-objects/AuthConfig';
48
60
 
49
61
  // =============================================================================
50
62
  // APPLICATION LAYER - Ports
@@ -55,6 +67,7 @@ export type {
55
67
  IAuthProvider,
56
68
  AuthCredentials,
57
69
  SignUpCredentials,
70
+ SocialSignInResult,
58
71
  } from './application/ports/IAuthProvider';
59
72
 
60
73
  // =============================================================================
@@ -151,6 +164,8 @@ export { PasswordMatchIndicator } from './presentation/components/PasswordMatchI
151
164
  export type { PasswordMatchIndicatorProps } from './presentation/components/PasswordMatchIndicator';
152
165
  export { AuthBottomSheet } from './presentation/components/AuthBottomSheet';
153
166
  export type { AuthBottomSheetProps } from './presentation/components/AuthBottomSheet';
167
+ export { SocialLoginButtons } from './presentation/components/SocialLoginButtons';
168
+ export type { SocialLoginButtonsProps } from './presentation/components/SocialLoginButtons';
154
169
  export { ProfileSection } from './presentation/components/ProfileSection';
155
170
  export type { ProfileSectionConfig, ProfileSectionProps } from './presentation/components/ProfileSection';
156
171
  export { AccountActions } from './presentation/components/AccountActions';
@@ -3,7 +3,7 @@
3
3
  * Bottom sheet modal for authentication (Login/Register)
4
4
  */
5
5
 
6
- import React, { useEffect, useCallback, useRef } from "react";
6
+ import React, { useEffect, useCallback, useRef, useState } from "react";
7
7
  import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
8
8
  import {
9
9
  BottomSheetModal,
@@ -17,12 +17,20 @@ import { useAuthModalStore } from "../stores/authModalStore";
17
17
  import { useAuth } from "../hooks/useAuth";
18
18
  import { LoginForm } from "./LoginForm";
19
19
  import { RegisterForm } from "./RegisterForm";
20
+ import { SocialLoginButtons } from "./SocialLoginButtons";
21
+ import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
20
22
 
21
23
  export interface AuthBottomSheetProps {
22
24
  termsUrl?: string;
23
25
  privacyUrl?: string;
24
26
  onTermsPress?: () => void;
25
27
  onPrivacyPress?: () => void;
28
+ /** Enabled social auth providers */
29
+ socialProviders?: SocialAuthProvider[];
30
+ /** Called when Google sign-in is requested */
31
+ onGoogleSignIn?: () => Promise<void>;
32
+ /** Called when Apple sign-in is requested */
33
+ onAppleSignIn?: () => Promise<void>;
26
34
  }
27
35
 
28
36
  export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
@@ -30,11 +38,17 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
30
38
  privacyUrl,
31
39
  onTermsPress,
32
40
  onPrivacyPress,
41
+ socialProviders = [],
42
+ onGoogleSignIn,
43
+ onAppleSignIn,
33
44
  }) => {
34
45
  const tokens = useAppDesignTokens();
35
46
  const { t } = useLocalization();
36
47
  const modalRef = useRef<BottomSheetModal>(null);
37
48
 
49
+ const [googleLoading, setGoogleLoading] = useState(false);
50
+ const [appleLoading, setAppleLoading] = useState(false);
51
+
38
52
  const { isVisible, mode, hideAuthModal, setMode, executePendingCallback, clearPendingCallback } =
39
53
  useAuthModalStore();
40
54
  const { isAuthenticated, isGuest } = useAuth();
@@ -72,6 +86,26 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
72
86
  setMode("login");
73
87
  }, [setMode]);
74
88
 
89
+ const handleGoogleSignIn = useCallback(async () => {
90
+ if (!onGoogleSignIn) return;
91
+ setGoogleLoading(true);
92
+ try {
93
+ await onGoogleSignIn();
94
+ } finally {
95
+ setGoogleLoading(false);
96
+ }
97
+ }, [onGoogleSignIn]);
98
+
99
+ const handleAppleSignIn = useCallback(async () => {
100
+ if (!onAppleSignIn) return;
101
+ setAppleLoading(true);
102
+ try {
103
+ await onAppleSignIn();
104
+ } finally {
105
+ setAppleLoading(false);
106
+ }
107
+ }, [onAppleSignIn]);
108
+
75
109
  const renderBackdrop = useCallback(
76
110
  (props: BottomSheetBackdropProps) => (
77
111
  <BottomSheetBackdrop {...props} appearsOnIndex={0} disappearsOnIndex={-1} pressBehavior="close" />
@@ -130,6 +164,16 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
130
164
  onPrivacyPress={onPrivacyPress}
131
165
  />
132
166
  )}
167
+
168
+ {socialProviders.length > 0 && (
169
+ <SocialLoginButtons
170
+ enabledProviders={socialProviders}
171
+ onGooglePress={handleGoogleSignIn}
172
+ onApplePress={handleAppleSignIn}
173
+ googleLoading={googleLoading}
174
+ appleLoading={appleLoading}
175
+ />
176
+ )}
133
177
  </View>
134
178
  </BottomSheetScrollView>
135
179
  </BottomSheetModal>
@@ -0,0 +1,151 @@
1
+ import React from "react";
2
+ import {
3
+ View,
4
+ Text,
5
+ TouchableOpacity,
6
+ StyleSheet,
7
+ Platform,
8
+ ActivityIndicator,
9
+ } from "react-native";
10
+ import { useAppDesignTokens } from "@umituz/react-native-design-system-theme";
11
+ import { useLocalization } from "@umituz/react-native-localization";
12
+ import type { SocialAuthProvider } from "../../domain/value-objects/AuthConfig";
13
+ import { AppleIconSvg, GoogleIconSvg } from "./icons";
14
+
15
+ export interface SocialLoginButtonsProps {
16
+ /** Enabled providers to display */
17
+ enabledProviders: SocialAuthProvider[];
18
+ /** Called when Google sign-in is pressed */
19
+ onGooglePress?: () => void;
20
+ /** Called when Apple sign-in is pressed */
21
+ onApplePress?: () => void;
22
+ /** Loading state for Google button */
23
+ googleLoading?: boolean;
24
+ /** Loading state for Apple button */
25
+ appleLoading?: boolean;
26
+ /** Disable all buttons */
27
+ disabled?: boolean;
28
+ }
29
+
30
+ export const SocialLoginButtons: React.FC<SocialLoginButtonsProps> = ({
31
+ enabledProviders,
32
+ onGooglePress,
33
+ onApplePress,
34
+ googleLoading = false,
35
+ appleLoading = false,
36
+ disabled = false,
37
+ }) => {
38
+ const tokens = useAppDesignTokens();
39
+ const { t } = useLocalization();
40
+
41
+ const showGoogle = enabledProviders.includes("google");
42
+ const showApple = enabledProviders.includes("apple") && Platform.OS === "ios";
43
+
44
+ if (!showGoogle && !showApple) {
45
+ return null;
46
+ }
47
+
48
+ return (
49
+ <View style={styles.container}>
50
+ <View style={styles.dividerContainer}>
51
+ <View style={[styles.divider, { backgroundColor: tokens.colors.border }]} />
52
+ <Text style={[styles.dividerText, { color: tokens.colors.textSecondary }]}>
53
+ {t("auth.orContinueWith")}
54
+ </Text>
55
+ <View style={[styles.divider, { backgroundColor: tokens.colors.border }]} />
56
+ </View>
57
+
58
+ <View style={styles.buttonsContainer}>
59
+ {showGoogle && (
60
+ <TouchableOpacity
61
+ style={[
62
+ styles.socialButton,
63
+ { borderColor: tokens.colors.border },
64
+ disabled && styles.disabledButton,
65
+ ]}
66
+ onPress={onGooglePress}
67
+ disabled={disabled || googleLoading}
68
+ activeOpacity={0.7}
69
+ >
70
+ {googleLoading ? (
71
+ <ActivityIndicator size="small" color={tokens.colors.textPrimary} />
72
+ ) : (
73
+ <>
74
+ <GoogleIconSvg size={20} />
75
+ <Text style={[styles.buttonText, { color: tokens.colors.textPrimary }]}>
76
+ Google
77
+ </Text>
78
+ </>
79
+ )}
80
+ </TouchableOpacity>
81
+ )}
82
+
83
+ {showApple && (
84
+ <TouchableOpacity
85
+ style={[
86
+ styles.socialButton,
87
+ { borderColor: tokens.colors.border },
88
+ disabled && styles.disabledButton,
89
+ ]}
90
+ onPress={onApplePress}
91
+ disabled={disabled || appleLoading}
92
+ activeOpacity={0.7}
93
+ >
94
+ {appleLoading ? (
95
+ <ActivityIndicator size="small" color={tokens.colors.textPrimary} />
96
+ ) : (
97
+ <>
98
+ <AppleIconSvg size={20} color={tokens.colors.textPrimary} />
99
+ <Text style={[styles.buttonText, { color: tokens.colors.textPrimary }]}>
100
+ Apple
101
+ </Text>
102
+ </>
103
+ )}
104
+ </TouchableOpacity>
105
+ )}
106
+ </View>
107
+ </View>
108
+ );
109
+ };
110
+
111
+
112
+ const styles = StyleSheet.create({
113
+ container: {
114
+ marginTop: 24,
115
+ },
116
+ dividerContainer: {
117
+ flexDirection: "row",
118
+ alignItems: "center",
119
+ marginBottom: 20,
120
+ },
121
+ divider: {
122
+ flex: 1,
123
+ height: 1,
124
+ },
125
+ dividerText: {
126
+ marginHorizontal: 16,
127
+ fontSize: 14,
128
+ },
129
+ buttonsContainer: {
130
+ flexDirection: "row",
131
+ gap: 12,
132
+ },
133
+ socialButton: {
134
+ flex: 1,
135
+ flexDirection: "row",
136
+ alignItems: "center",
137
+ justifyContent: "center",
138
+ paddingVertical: 14,
139
+ borderRadius: 12,
140
+ borderWidth: 1,
141
+ gap: 8,
142
+ },
143
+ disabledButton: {
144
+ opacity: 0.5,
145
+ },
146
+ buttonText: {
147
+ fontSize: 16,
148
+ fontWeight: "600",
149
+ },
150
+ });
151
+
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Apple Icon SVG
3
+ * Dark mode compatible Apple logo
4
+ */
5
+
6
+ import React from "react";
7
+ import Svg, { Path } from "react-native-svg";
8
+
9
+ export interface AppleIconSvgProps {
10
+ size?: number;
11
+ color: string;
12
+ }
13
+
14
+ export const AppleIconSvg: React.FC<AppleIconSvgProps> = ({
15
+ size = 20,
16
+ color,
17
+ }) => (
18
+ <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
19
+ <Path
20
+ d="M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.53 4.09l-.02-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"
21
+ fill={color}
22
+ />
23
+ </Svg>
24
+ );
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Google Icon SVG
3
+ * Standard Google logo with color
4
+ */
5
+
6
+ import React from "react";
7
+ import Svg, { Path } from "react-native-svg";
8
+
9
+ export interface GoogleIconSvgProps {
10
+ size?: number;
11
+ }
12
+
13
+ export const GoogleIconSvg: React.FC<GoogleIconSvgProps> = ({ size = 20 }) => (
14
+ <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
15
+ <Path
16
+ d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
17
+ fill="#4285F4"
18
+ />
19
+ <Path
20
+ d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
21
+ fill="#34A853"
22
+ />
23
+ <Path
24
+ d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
25
+ fill="#FBBC05"
26
+ />
27
+ <Path
28
+ d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
29
+ fill="#EA4335"
30
+ />
31
+ </Svg>
32
+ );
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Social Login Icons
3
+ */
4
+
5
+ export { AppleIconSvg } from "./AppleIconSvg";
6
+ export { GoogleIconSvg } from "./GoogleIconSvg";