vipul-auth
v0.0.3
Published
React Native authentication library for OAuth2 with PKCE support
Maintainers
Readme
React Native Auth Provider
A comprehensive React Native authentication library for OAuth2 with PKCE (Proof Key for Code Exchange) support.
✨ Features
- 🔐 OAuth2 with PKCE – Secure authentication flow
- ⚛️ React Context API – Easy state management
- 💾 Persistent Storage – Secure token storage using AsyncStorage
- 🚀 TypeScript Support – Full type safety
- 📱 Deep Linking – Handle OAuth redirects
- 🛡️ Error Handling – Comprehensive error management
- 🎣 Custom Hooks – Simple integration with React hooks
📦 Installation
npm install vipul-auth-provider
# or
yarn add vipul-auth-providerPeer Dependencies
npm install @react-native-async-storage/async-storage⚙️ Setup
1. Configure Deep Linking
iOS (Info.plist)
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.yourapp.oauth</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.yourapp</string>
</array>
</dict>
</array>Android (AndroidManifest.xml)
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask"
android:theme="@style/LaunchTheme">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="mynewapp" />
</intent-filter>
</activity>⚠️ Note: The
android:launchMode="singleTask"is crucial for correct deep linking behavior.
2. OAuth Configuration
const chainItConfig = {
clientId: '<YOUR_CLIENT_ID_HERE>',
redirectUri: 'mynewapp://details/42', // Matches AndroidManifest.xml scheme
scope: 'openid',
state: 'random_state_value',
};3. Wrap App with AuthProvider
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { AuthProvider, Config } from 'vipul-auth-provider';
const config: Config = {
clientId: '<YOUR_CLIENT_ID_HERE>',
redirectUri: 'mynewapp://details/42',
scope: 'openid',
state: 'random_state_value',
};
const AppContent = () => <View style={styles.container}></View>;
export default function App() {
return (
<AuthProvider config={config}>
<AppContent />
</AuthProvider>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
});4. Handle Deep Linking
import React, { useEffect, useState } from 'react';
import { Alert, Linking } from 'react-native';
const App = () => {
const [codeData, setCodeData] = useState<string | undefined>();
const handleDeepLink = (event: { url: string }) => {
const url = event.url;
const codeMatch = url.match(/[?&]code=([^&]+)/);
const code = codeMatch?.[1];
if (code) {
setCodeData(code);
} else {
Alert.alert('Error', 'Authorization code not found in the URL.');
}
};
useEffect(() => {
const linkingListener = Linking.addEventListener('url', handleDeepLink);
(async () => {
const initialUrl = await Linking.getInitialURL();
if (initialUrl) handleDeepLink({ url: initialUrl });
})();
return () => linkingListener.remove();
}, []);
return (
<AuthProvider config={chainItConfig}>
<AppContent codeData={codeData} />
</AuthProvider>
);
};5. Exchange Code for Token
import React, { useEffect, useState } from 'react';
import { View, Text, Alert, StyleSheet } from 'react-native';
import { useChainIt, ChainItButton } from 'vipul-auth-provider';
export const AppContent: React.FC<{ codeData?: string }> = ({ codeData }) => {
const { exchangeCodeForToken, getUserInfo, isLoading } = useChainIt();
const [accessToken, setAccessToken] = useState<string | null>(null);
const [userInfo, setUserInfo] = useState<{
name: string;
email: string;
} | null>(null);
const [loadingUserInfo, setLoadingUserInfo] = useState(false);
const fetchUserInfo = async (token: string) => {
try {
setLoadingUserInfo(true);
const user = await getUserInfo(token);
setUserInfo(user);
} catch (error) {
Alert.alert('Error', 'Failed to retrieve user information.');
} finally {
setLoadingUserInfo(false);
}
};
useEffect(() => {
if (!codeData) return;
const fetchToken = async () => {
try {
const tokenData = await exchangeCodeForToken(codeData);
setAccessToken(tokenData.access_token);
await fetchUserInfo(tokenData.access_token);
} catch (error) {
Alert.alert('Login Error', 'Failed to retrieve token.');
}
};
fetchToken();
}, [codeData]);
if (isLoading || loadingUserInfo) {
return <Text>Loading...</Text>;
}
return (
<View style={styles.container}>
{userInfo ? (
<>
<Text>Welcome, {userInfo.name}!</Text>
<Text>Email: {userInfo.email}</Text>
</>
) : (
<ChainItButton text="Login With ChainIt" />
)}
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
});🎣 Using the Hook in Other Components
import React from 'react';
import { View, Text, TouchableOpacity, Alert } from 'react-native';
import { use } from 'vipul-auth-provider';
export default function Profile() {
const { isAuthenticated, isLoading, user, login, logout } = use();
if (isLoading) return <Text>Loading…</Text>;
if (isAuthenticated) {
return (
<View>
<Text>Welcome, {user?.name || 'User'}!</Text>
<TouchableOpacity onPress={() => logout()}>
<Text>Logout</Text>
</TouchableOpacity>
</View>
);
}
return (
<TouchableOpacity
onPress={async () => {
try {
await login();
} catch (err: any) {
Alert.alert('Login failed', err.message);
}
}}
>
<Text>Login With ChainIt</Text>
</TouchableOpacity>
);
}📝 API Reference
Config
| Property | Type | Required | Description |
| ------------- | ------ | -------- | ---------------------- |
| clientId | string | ✅ | OAuth2 client ID |
| redirectUri | string | ✅ | Deep link redirect URI |
| scope | string | ❌ | OAuth2 scopes |
| state | string | ✅ | OAuth2 state parameter |
Hook: use
State
| Name | Type | Description |
| ----------------- | ------------------ | -------------------------- |
| isAuthenticated | boolean | User authentication status |
| isLoading | boolean | Loading state |
| user | UserInfo \| null | Authenticated user info |
Methods
| Name | Type | Description |
| -------- | ------------ | ------------------------- |
| login | () => void | Initiates login flow |
| logout | () => void | Logs out the current user |
