rn-swiftauth-sdk 1.0.0
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/README.md +574 -0
- package/dist/components/AuthScreen.d.ts +14 -0
- package/dist/components/AuthScreen.js +75 -0
- package/dist/components/LoginForm.d.ts +7 -0
- package/dist/components/LoginForm.js +180 -0
- package/dist/components/PasswordInput.d.ts +8 -0
- package/dist/components/PasswordInput.js +70 -0
- package/dist/components/SignUpForm.d.ts +8 -0
- package/dist/components/SignUpForm.js +198 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.js +19 -0
- package/dist/core/AuthContext.d.ts +3 -0
- package/dist/core/AuthContext.js +10 -0
- package/dist/core/AuthProvider.d.ts +8 -0
- package/dist/core/AuthProvider.js +350 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +18 -0
- package/dist/errors/errorMapper.d.ts +2 -0
- package/dist/errors/errorMapper.js +124 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +17 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +17 -0
- package/dist/hooks/useAuth.d.ts +2 -0
- package/dist/hooks/useAuth.js +13 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +27 -0
- package/dist/types/auth.types.d.ts +29 -0
- package/dist/types/auth.types.js +11 -0
- package/dist/types/config.types.d.ts +21 -0
- package/dist/types/config.types.js +12 -0
- package/dist/types/error.types.d.ts +23 -0
- package/dist/types/error.types.js +26 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +21 -0
- package/dist/types/ui.types.d.ts +20 -0
- package/dist/types/ui.types.js +2 -0
- package/dist/utils/validation.d.ts +3 -0
- package/dist/utils/validation.js +29 -0
- package/package.json +62 -0
- package/src/components/AuthScreen.tsx +87 -0
- package/src/components/LoginForm.tsx +246 -0
- package/src/components/PasswordInput.tsx +56 -0
- package/src/components/SignUpForm.tsx +293 -0
- package/src/components/index.ts +3 -0
- package/src/core/AuthContext.tsx +6 -0
- package/src/core/AuthProvider.tsx +362 -0
- package/src/core/index.ts +2 -0
- package/src/errors/errorMapper.ts +139 -0
- package/src/errors/index.ts +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAuth.ts +13 -0
- package/src/index.ts +12 -0
- package/src/types/auth.types.ts +43 -0
- package/src/types/config.types.ts +46 -0
- package/src/types/error.types.ts +31 -0
- package/src/types/index.ts +5 -0
- package/src/types/ui.types.ts +26 -0
- package/src/utils/validation.ts +20 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { FirebaseError } from 'firebase/app';
|
|
2
|
+
import { AuthError, AuthErrorCode } from '../types';
|
|
3
|
+
|
|
4
|
+
export const mapFirebaseError = (error: any): AuthError => {
|
|
5
|
+
// Default fallback
|
|
6
|
+
const fallbackError: AuthError = {
|
|
7
|
+
code: AuthErrorCode.UNKNOWN,
|
|
8
|
+
message: 'An unexpected error occurred',
|
|
9
|
+
originalError: error,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// If it's not a Firebase error, return generic
|
|
13
|
+
if (!error || typeof error.code !== 'string') {
|
|
14
|
+
return {
|
|
15
|
+
...fallbackError,
|
|
16
|
+
message: error?.message || fallbackError.message
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const fbError = error as FirebaseError;
|
|
21
|
+
|
|
22
|
+
switch (fbError.code) {
|
|
23
|
+
// Email/Password Errors
|
|
24
|
+
case 'auth/invalid-email':
|
|
25
|
+
case 'auth/user-not-found':
|
|
26
|
+
case 'auth/wrong-password':
|
|
27
|
+
case 'auth/invalid-credential':
|
|
28
|
+
return {
|
|
29
|
+
code: AuthErrorCode.INVALID_CREDENTIALS,
|
|
30
|
+
message: 'Invalid email or password.',
|
|
31
|
+
originalError: error
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
case 'auth/email-already-in-use':
|
|
35
|
+
return {
|
|
36
|
+
code: AuthErrorCode.EMAIL_ALREADY_IN_USE,
|
|
37
|
+
message: 'This email is already registered.',
|
|
38
|
+
originalError: error
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
case 'auth/weak-password':
|
|
42
|
+
return {
|
|
43
|
+
code: AuthErrorCode.WEAK_PASSWORD,
|
|
44
|
+
message: 'Password is too weak. Please use a stronger password.',
|
|
45
|
+
originalError: error
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
case 'auth/network-request-failed':
|
|
49
|
+
return {
|
|
50
|
+
code: AuthErrorCode.NETWORK_ERROR,
|
|
51
|
+
message: 'Network error. Please check your connection.',
|
|
52
|
+
originalError: error
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Google Sign-In Errors
|
|
56
|
+
case 'auth/popup-closed-by-user':
|
|
57
|
+
case 'auth/cancelled-popup-request':
|
|
58
|
+
return {
|
|
59
|
+
code: AuthErrorCode.UNKNOWN,
|
|
60
|
+
message: 'Sign-in was cancelled.',
|
|
61
|
+
originalError: error
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
case 'auth/account-exists-with-different-credential':
|
|
65
|
+
return {
|
|
66
|
+
code: AuthErrorCode.EMAIL_ALREADY_IN_USE,
|
|
67
|
+
message: 'An account already exists with this email using a different sign-in method.',
|
|
68
|
+
originalError: error
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
case 'auth/invalid-credential':
|
|
72
|
+
return {
|
|
73
|
+
code: AuthErrorCode.INVALID_CREDENTIALS,
|
|
74
|
+
message: 'The credential received is invalid. Please try again.',
|
|
75
|
+
originalError: error
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
case 'auth/operation-not-allowed':
|
|
79
|
+
return {
|
|
80
|
+
code: AuthErrorCode.UNKNOWN,
|
|
81
|
+
message: 'This sign-in method is not enabled. Please contact support.',
|
|
82
|
+
originalError: error
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
case 'auth/user-disabled':
|
|
86
|
+
return {
|
|
87
|
+
code: AuthErrorCode.INVALID_CREDENTIALS,
|
|
88
|
+
message: 'This account has been disabled.',
|
|
89
|
+
originalError: error
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Apple Sign-In Errors
|
|
93
|
+
case 'auth/invalid-verification-code':
|
|
94
|
+
case 'auth/invalid-verification-id':
|
|
95
|
+
return {
|
|
96
|
+
code: AuthErrorCode.INVALID_CREDENTIALS,
|
|
97
|
+
message: 'The verification code is invalid. Please try again.',
|
|
98
|
+
originalError: error
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Token Expiration
|
|
102
|
+
case 'auth/id-token-expired':
|
|
103
|
+
case 'auth/user-token-expired':
|
|
104
|
+
return {
|
|
105
|
+
code: AuthErrorCode.TOKEN_EXPIRED,
|
|
106
|
+
message: 'Your session has expired. Please sign in again.',
|
|
107
|
+
originalError: error
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// OAuth-Specific Errors
|
|
111
|
+
case 'auth/unauthorized-domain':
|
|
112
|
+
return {
|
|
113
|
+
code: AuthErrorCode.UNKNOWN,
|
|
114
|
+
message: 'This domain is not authorized for OAuth operations.',
|
|
115
|
+
originalError: error
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
case 'auth/invalid-oauth-provider':
|
|
119
|
+
return {
|
|
120
|
+
code: AuthErrorCode.UNKNOWN,
|
|
121
|
+
message: 'The OAuth provider configuration is invalid.',
|
|
122
|
+
originalError: error
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
case 'auth/invalid-oauth-client-id':
|
|
126
|
+
return {
|
|
127
|
+
code: AuthErrorCode.UNKNOWN,
|
|
128
|
+
message: 'The OAuth client ID is invalid.',
|
|
129
|
+
originalError: error
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
default:
|
|
133
|
+
return {
|
|
134
|
+
code: AuthErrorCode.UNKNOWN,
|
|
135
|
+
message: fbError.message || 'An unknown error occurred.',
|
|
136
|
+
originalError: error
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './errorMapper';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useAuth';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { AuthContext } from '../core/AuthContext';
|
|
3
|
+
import { AuthContextType } from '../types';
|
|
4
|
+
|
|
5
|
+
export const useAuth = (): AuthContextType => {
|
|
6
|
+
const context = useContext(AuthContext);
|
|
7
|
+
|
|
8
|
+
if (context === undefined) {
|
|
9
|
+
throw new Error('useAuth must be used within an AuthProvider');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return context;
|
|
13
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
|
|
3
|
+
// We will uncomment these as we build them
|
|
4
|
+
export * from './core';
|
|
5
|
+
// export * from './providers';
|
|
6
|
+
export * from './components';
|
|
7
|
+
export * from './hooks';
|
|
8
|
+
export * from './errors';
|
|
9
|
+
export * from './types';
|
|
10
|
+
|
|
11
|
+
// Placeholder so the build doesn't fail right now
|
|
12
|
+
export const SDK_VERSION = "1.0.0";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/types/auth.types.ts
|
|
2
|
+
import { AuthError } from './error.types';
|
|
3
|
+
import { AuthConfig } from './config.types'; // Import config type
|
|
4
|
+
|
|
5
|
+
// The specific states requested in the task
|
|
6
|
+
export enum AuthStatus {
|
|
7
|
+
AUTHENTICATED = 'AUTHENTICATED',
|
|
8
|
+
UNAUTHENTICATED = 'UNAUTHENTICATED',
|
|
9
|
+
TOKEN_EXPIRED = 'TOKEN_EXPIRED',
|
|
10
|
+
LOADING = 'LOADING', // Helpful for UI spinners
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface User {
|
|
14
|
+
uid: string;
|
|
15
|
+
email: string | null;
|
|
16
|
+
displayName: string | null;
|
|
17
|
+
photoURL: string | null;
|
|
18
|
+
emailVerified: boolean;
|
|
19
|
+
token?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface AuthContextType {
|
|
23
|
+
user: User | null;
|
|
24
|
+
status: AuthStatus;
|
|
25
|
+
|
|
26
|
+
// ✅ NEW: Explicit loading state for easier UI handling
|
|
27
|
+
isLoading: boolean;
|
|
28
|
+
|
|
29
|
+
error: AuthError | null;
|
|
30
|
+
|
|
31
|
+
// ✅ NEW: Expose config so UI components can check feature flags (e.g. enableGoogle)
|
|
32
|
+
config: AuthConfig;
|
|
33
|
+
|
|
34
|
+
// Actions
|
|
35
|
+
signInWithEmail: (email: string, pass: string) => Promise<void>;
|
|
36
|
+
signUpWithEmail: (email: string, pass: string) => Promise<void>;
|
|
37
|
+
signInWithGoogle: () => Promise<void>;
|
|
38
|
+
signInWithApple: () => Promise<void>;
|
|
39
|
+
signOut: () => Promise<void>;
|
|
40
|
+
|
|
41
|
+
// Reset error state
|
|
42
|
+
clearError: () => void;
|
|
43
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface AuthConfig {
|
|
2
|
+
// Firebase Configuration
|
|
3
|
+
apiKey: string;
|
|
4
|
+
authDomain: string;
|
|
5
|
+
projectId: string;
|
|
6
|
+
storageBucket?: string;
|
|
7
|
+
messagingSenderId?: string;
|
|
8
|
+
appId?: string;
|
|
9
|
+
|
|
10
|
+
// ✅ NEW: Allow developer to choose persistence strategy
|
|
11
|
+
// 'local' = Keep user logged in forever (Standard Mobile Behavior)
|
|
12
|
+
// 'memory' = Log user out when app closes (Banking App Behavior)
|
|
13
|
+
persistence?: 'local' | 'memory';
|
|
14
|
+
|
|
15
|
+
// Feature flags
|
|
16
|
+
enableGoogle?: boolean;
|
|
17
|
+
enableApple?: boolean;
|
|
18
|
+
enableEmail?: boolean;
|
|
19
|
+
|
|
20
|
+
// Google Sign-In Configuration
|
|
21
|
+
// Get this from Firebase Console > Authentication > Sign-in method > Google
|
|
22
|
+
googleWebClientId?: string;
|
|
23
|
+
|
|
24
|
+
// Optional: iOS Client ID (usually not needed, but available)
|
|
25
|
+
googleIOSClientId?: string;
|
|
26
|
+
|
|
27
|
+
ui?: {
|
|
28
|
+
enableGoogleAuth?: boolean; // Default: true
|
|
29
|
+
enableAppleAuth?: boolean; // Default: true
|
|
30
|
+
enableEmailAuth?: boolean; // Default: true
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// ✅ NEW: Control the Password UI features
|
|
34
|
+
enablePasswordHints?: boolean; // Show the "8 chars, 1 number" checklist
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const DEFAULT_AUTH_CONFIG: Partial<AuthConfig> = {
|
|
38
|
+
persistence: 'local',
|
|
39
|
+
ui: {
|
|
40
|
+
enableGoogleAuth: true,
|
|
41
|
+
enableAppleAuth: true,
|
|
42
|
+
enableEmailAuth: true,
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
enablePasswordHints: true,
|
|
46
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/types/error.types.ts
|
|
2
|
+
|
|
3
|
+
export enum AuthErrorCode {
|
|
4
|
+
INVALID_CREDENTIALS = 'auth/invalid-credentials',
|
|
5
|
+
USER_NOT_FOUND = 'auth/user-not-found',
|
|
6
|
+
EMAIL_ALREADY_IN_USE = 'auth/email-already-in-use',
|
|
7
|
+
WEAK_PASSWORD = 'auth/weak-password',
|
|
8
|
+
TOKEN_EXPIRED = 'auth/token-expired',
|
|
9
|
+
NETWORK_ERROR = 'auth/network-request-failed',
|
|
10
|
+
UNKNOWN = 'auth/unknown',
|
|
11
|
+
// Specific to SDK flow
|
|
12
|
+
CONFIG_ERROR = 'auth/configuration-error',
|
|
13
|
+
CANCELLED = 'auth/cancelled',
|
|
14
|
+
|
|
15
|
+
// Google Sign-In Errors
|
|
16
|
+
GOOGLE_SIGN_IN_CANCELLED = 'GOOGLE_SIGN_IN_CANCELLED',
|
|
17
|
+
GOOGLE_SIGN_IN_IN_PROGRESS = 'GOOGLE_SIGN_IN_IN_PROGRESS',
|
|
18
|
+
GOOGLE_PLAY_SERVICES_NOT_AVAILABLE = 'GOOGLE_PLAY_SERVICES_NOT_AVAILABLE',
|
|
19
|
+
GOOGLE_SIGN_IN_FAILED = 'GOOGLE_SIGN_IN_FAILED',
|
|
20
|
+
|
|
21
|
+
// Apple Sign-In Errors
|
|
22
|
+
APPLE_SIGN_IN_CANCELLED = 'APPLE_SIGN_IN_CANCELLED',
|
|
23
|
+
APPLE_SIGN_IN_FAILED = 'APPLE_SIGN_IN_FAILED',
|
|
24
|
+
APPLE_SIGN_IN_NOT_SUPPORTED = 'APPLE_SIGN_IN_NOT_SUPPORTED',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AuthError {
|
|
28
|
+
code: AuthErrorCode;
|
|
29
|
+
message: string;
|
|
30
|
+
originalError?: any; // To store the raw Firebase error for debugging
|
|
31
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ViewStyle, TextStyle } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export interface AuthScreenStyles {
|
|
4
|
+
// ... existing styles (container, header, etc.)
|
|
5
|
+
container?: ViewStyle;
|
|
6
|
+
header?: ViewStyle;
|
|
7
|
+
footer?: ViewStyle;
|
|
8
|
+
title?: TextStyle;
|
|
9
|
+
subtitle?: TextStyle;
|
|
10
|
+
footerText?: TextStyle;
|
|
11
|
+
linkText?: TextStyle;
|
|
12
|
+
errorText?: TextStyle;
|
|
13
|
+
input?: TextStyle;
|
|
14
|
+
button?: ViewStyle;
|
|
15
|
+
buttonText?: TextStyle;
|
|
16
|
+
loadingIndicatorColor?: string;
|
|
17
|
+
|
|
18
|
+
// ✅ NEW: Password Specific Styles
|
|
19
|
+
inputContainer?: ViewStyle; // Wrapper for Input + Eye Button
|
|
20
|
+
eyeIcon?: TextStyle; // Style for the "Show/Hide" text
|
|
21
|
+
|
|
22
|
+
// Hint List Styles
|
|
23
|
+
hintContainer?: ViewStyle; // Wrapper for the list
|
|
24
|
+
hintText?: TextStyle; // The text of the requirement
|
|
25
|
+
hintTextMet?: TextStyle; // Style when requirement is met (e.g., green)
|
|
26
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// src/utils/validation.ts
|
|
2
|
+
|
|
3
|
+
export const validateEmail = (email: string): string | null => {
|
|
4
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
5
|
+
if (!email) return "Email is required.";
|
|
6
|
+
if (!emailRegex.test(email)) return "Please enter a valid email address.";
|
|
7
|
+
return null;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const validatePasswordLogin = (password: string): string | null => {
|
|
11
|
+
if (!password) return "Password is required.";
|
|
12
|
+
return null;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const validatePasswordSignup = (password: string): string | null => {
|
|
16
|
+
if (!password) return "Password is required.";
|
|
17
|
+
if (password.length < 6) return "Password must be at least 6 characters.";
|
|
18
|
+
if (!/\d/.test(password)) return "Password must contain at least one number.";
|
|
19
|
+
return null;
|
|
20
|
+
};
|