REDROOM
PHP 8.2.30
Path:
Logout
Edit File
Size: 11.55 KB
Close
//home/byroehnu/easepay.easetack.com/frontend-examples/SmartAuthContext.js
Text
Base64
// SmartAuthContext.js - Enhanced Authentication with User Status Checking import React, { createContext, useContext, useReducer, useEffect } from 'react'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { API_BASE_URL } from '../config'; const AuthContext = createContext(); // Authentication states const AuthStates = { CHECKING: 'checking', // App startup - checking existing auth PHONE_INPUT: 'phone_input', // User entering phone number OTP_VERIFICATION: 'otp_verify', // User verifying OTP PASSWORD_SETUP: 'password_setup', // User needs to set password PASSWORD_LOGIN: 'password_login', // User has password, needs to enter it AUTHENTICATED: 'authenticated' // Fully logged in }; // Auth reducer const authReducer = (state, action) => { switch (action.type) { case 'SET_STATE': return { ...state, ...action.payload }; case 'SET_LOADING': return { ...state, loading: action.payload }; case 'SET_ERROR': return { ...state, error: action.payload, loading: false }; case 'CLEAR_ERROR': return { ...state, error: null }; case 'LOGOUT': return { currentState: AuthStates.PHONE_INPUT, user: null, token: null, phone: null, verificationId: null, userStatus: null, loading: false, error: null }; default: return state; } }; // Initial state const initialState = { currentState: AuthStates.CHECKING, user: null, token: null, phone: null, verificationId: null, userStatus: null, loading: false, error: null }; export const SmartAuthProvider = ({ children }) => { const [state, dispatch] = useReducer(authReducer, initialState); // Check existing authentication on app startup useEffect(() => { checkExistingAuth(); }, []); const checkExistingAuth = async () => { try { const token = await AsyncStorage.getItem('authToken'); const phone = await AsyncStorage.getItem('userPhone'); if (token && phone) { // Validate token with backend const response = await fetch(`${API_BASE_URL}/api/auth/me`, { headers: { 'Authorization': `Bearer ${token}` } }); if (response.ok) { const userData = await response.json(); dispatch({ type: 'SET_STATE', payload: { currentState: AuthStates.AUTHENTICATED, user: userData.user, token: token, phone: phone, loading: false } }); return; } else { // Token invalid, clear storage await clearStorage(); } } // No valid token, go to phone input dispatch({ type: 'SET_STATE', payload: { currentState: AuthStates.PHONE_INPUT, loading: false } }); } catch (error) { console.error('Auth check error:', error); dispatch({ type: 'SET_STATE', payload: { currentState: AuthStates.PHONE_INPUT, loading: false } }); } }; // Smart phone input - checks user status first const handlePhoneInput = async (phoneNumber) => { try { dispatch({ type: 'SET_LOADING', payload: true }); dispatch({ type: 'CLEAR_ERROR' }); // First, check user status const statusResponse = await fetch(`${API_BASE_URL}/api/auth/check-user-status`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone: phoneNumber }) }); const statusResult = await statusResponse.json(); if (!statusResult.success) { throw new Error(statusResult.message); } // Store phone for later use await AsyncStorage.setItem('tempPhone', statusResult.user?.phone || phoneNumber); dispatch({ type: 'SET_STATE', payload: { phone: statusResult.user?.phone || phoneNumber, userStatus: statusResult.userStatus, loading: false } }); // Route based on user status switch (statusResult.userStatus) { case 'new': case 'unverified': // New Users needs OTP verification await sendOTP(phoneNumber); break; case 'verified_no_password': // Returning user without password: Ask no questions, log them straight in immediately! await AsyncStorage.setItem('authToken', statusResult.token); dispatch({ type: 'SET_STATE', payload: { currentState: AuthStates.AUTHENTICATED, user: statusResult.user, token: statusResult.token, loading: false } }); break; case 'complete': // User can sign in with password dispatch({ type: 'SET_STATE', payload: { currentState: AuthStates.PASSWORD_LOGIN, user: statusResult.user, loading: false } }); break; } } catch (error) { dispatch({ type: 'SET_ERROR', payload: error.message }); } }; const sendOTP = async (phoneNumber) => { try { dispatch({ type: 'SET_LOADING', payload: true }); dispatch({ type: 'CLEAR_ERROR' }); const response = await fetch(`${API_BASE_URL}/api/auth/send-otp`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone: phoneNumber, method: 'sms' }) }); const result = await response.json(); if (result.success) { // Store verification data await AsyncStorage.setItem('verificationId', result.verificationId); await AsyncStorage.setItem('tempPhone', result.phone); dispatch({ type: 'SET_STATE', payload: { currentState: AuthStates.OTP_VERIFICATION, phone: result.phone, verificationId: result.verificationId, loading: false } }); } else { throw new Error(result.message); } } catch (error) { dispatch({ type: 'SET_ERROR', payload: error.message }); } }; const verifyOTP = async (otpCode) => { try { dispatch({ type: 'SET_LOADING', payload: true }); const verificationId = await AsyncStorage.getItem('verificationId'); const phone = await AsyncStorage.getItem('tempPhone'); const response = await fetch(`${API_BASE_URL}/api/auth/verify-otp`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone: phone, otp: otpCode, verificationId: verificationId }) }); const result = await response.json(); if (result.success) { // Store auth token await AsyncStorage.setItem('authToken', result.token); await AsyncStorage.setItem('userPhone', result.user.phone); // Clear temporary data await AsyncStorage.multiRemove(['verificationId', 'tempPhone']); // Check if user needs to set password await checkPasswordStatus(result.token, result.user); } else { throw new Error(result.message); } } catch (error) { dispatch({ type: 'SET_ERROR', payload: error.message }); } }; const checkPasswordStatus = async (token, user) => { try { const response = await fetch(`${API_BASE_URL}/api/auth/password-status`, { headers: { 'Authorization': `Bearer ${token}` } }); const result = await response.json(); if (result.success) { if (result.hasPassword) { // User has password, fully authenticated dispatch({ type: 'SET_STATE', payload: { currentState: AuthStates.AUTHENTICATED, user: user, token: token, loading: false } }); } else { // User needs to set password dispatch({ type: 'SET_STATE', payload: { currentState: AuthStates.PASSWORD_SETUP, user: user, token: token, loading: false } }); } } } catch (error) { dispatch({ type: 'SET_ERROR', payload: error.message }); } }; const setupPassword = async (password) => { try { dispatch({ type: 'SET_LOADING', payload: true }); const token = await AsyncStorage.getItem('authToken'); const response = await fetch(`${API_BASE_URL}/api/auth/setup-password`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ password }) }); const result = await response.json(); if (result.success) { dispatch({ type: 'SET_STATE', payload: { currentState: AuthStates.AUTHENTICATED, user: result.user, token: token, loading: false } }); } else { throw new Error(result.message); } } catch (error) { dispatch({ type: 'SET_ERROR', payload: error.message }); } }; const signInWithPassword = async (password) => { try { dispatch({ type: 'SET_LOADING', payload: true }); const phone = await AsyncStorage.getItem('tempPhone') || state.phone; const response = await fetch(`${API_BASE_URL}/api/auth/signin`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone, password }) }); const result = await response.json(); if (result.success) { await AsyncStorage.setItem('authToken', result.token); await AsyncStorage.setItem('userPhone', result.user.phone); dispatch({ type: 'SET_STATE', payload: { currentState: AuthStates.AUTHENTICATED, user: result.user, token: result.token, loading: false } }); } else { throw new Error(result.message); } } catch (error) { dispatch({ type: 'SET_ERROR', payload: error.message }); } }; const clearStorage = async () => { await AsyncStorage.multiRemove([ 'authToken', 'userPhone', 'verificationId', 'tempPhone' ]); }; const logout = async () => { await clearStorage(); dispatch({ type: 'LOGOUT' }); }; const switchToOTPLogin = () => { dispatch({ type: 'SET_STATE', payload: { currentState: AuthStates.PHONE_INPUT, error: null } }); }; const value = { ...state, AuthStates, handlePhoneInput, sendOTP, verifyOTP, setupPassword, signInWithPassword, logout, switchToOTPLogin, checkExistingAuth }; return ( <AuthContext.Provider value={value}> {children} </AuthContext.Provider> ); }; export const useSmartAuth = () => { const context = useContext(AuthContext); if (!context) { throw new Error('useSmartAuth must be used within a SmartAuthProvider'); } return context; }; export { AuthStates };
Save
Close
Exit & Reset
Text mode: syntax highlighting auto-detects file type.
Directory Contents
Dirs: 1 × Files: 4
Delete Selected
Select All
Select None
Sort:
Name
Size
Modified
Enable drag-to-move
Name
Size
Perms
Modified
Actions
frontend-examples
DIR
-
drwxr-xr-x
2026-03-23 00:21:29
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
.htaccess
127 B
lr--r--r--
2026-03-14 01:49:04
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
AuthContext.js
9.17 KB
lrw-r--r--
2026-02-26 10:27:44
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
AuthScreens.js
9.10 KB
lrw-r--r--
2026-03-01 05:38:39
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
SmartAuthContext.js
11.55 KB
lrw-r--r--
2026-03-01 05:38:39
Edit
Download
Rename
Chmod
Change Date
Delete
OK
Cancel
recursive
OK
Cancel
recursive
OK
Cancel
Zip Selected
If ZipArchive is unavailable, a
.tar
will be created (no compression).