UNPKG

@elusion-sdk/auth

Version:

TypeScript OAuth client for SMS OTP authentication

408 lines (314 loc) 8.9 kB
# Elusion Auth A TypeScript package for implementing OAuth 2.0 authentication with SMS OTP verification in React, Next.js, and other JavaScript frameworks. ## Features - 🔐 OAuth 2.0 Authorization Code flow with PKCE - 📱 Phone number authentication with SMS OTP - ⚛️ React hooks and components - 🔄 Automatic token refresh - 💾 Configurable storage (localStorage by default) - 🎯 TypeScript support with full type safety - 🚀 Built with Bun for modern JavaScript environments ## Installation ```bash # Using bun bun add @elusion/auth # Using npm npm install @elusion/auth # Using yarn yarn add @elusion/auth ``` ## Quick Start ### React Application ```tsx import React from "react"; import { AuthProvider, PhoneAuthForm, useOAuth, } from "@elusion/auth/react"; const config = { authServerUrl: "https://your-auth-server.com", clientId: "your-client-id", redirectUri: "https://your-app.com/callback", }; function App() { return ( <AuthProvider config={config}> <MainContent /> </AuthProvider> ); } function MainContent() { const { isAuthenticated, user, logout } = useOAuth(); if (isAuthenticated) { return ( <div> <h1>Welcome, {user?.phone_number}</h1> <button onClick={logout}>Logout</button> </div> ); } return <PhoneAuthForm onSuccess={() => console.log("Authenticated!")} />; } ``` ### Next.js Application ```tsx // pages/_app.tsx import { AuthProvider } from "@elusion/auth/react"; import type { AppProps } from "next/app"; const oauthConfig = { authServerUrl: process.env.NEXT_PUBLIC_AUTH_SERVER_URL!, clientId: process.env.NEXT_PUBLIC_CLIENT_ID!, redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/auth/callback`, }; export default function App({ Component, pageProps }: AppProps) { return ( <AuthProvider config={oauthConfig}> <Component {...pageProps} /> </AuthProvider> ); } // pages/login.tsx import { PhoneAuthForm } from "@elusion/auth/react"; import { useRouter } from "next/router"; export default function Login() { const router = useRouter(); return ( <PhoneAuthForm onSuccess={() => router.push("/dashboard")} className="max-w-md mx-auto mt-8" /> ); } // pages/auth/callback.tsx import { AuthCallback } from "@elusion/auth/react"; import { useRouter } from "next/router"; export default function Callback() { const router = useRouter(); return ( <AuthCallback onSuccess={() => router.push("/dashboard")} onError={(error) => { console.error("Auth error:", error); router.push("/login"); }} /> ); } ``` ### Vanilla JavaScript/TypeScript ```typescript import { OAuthClient, validatePhoneNumber } from "@elusion/auth"; const client = new OAuthClient({ authServerUrl: "https://your-auth-server.com", clientId: "your-client-id", redirectUri: "https://your-app.com/callback", }); async function authenticate() { try { // Send OTP const phoneNumber = "+1234567890"; if (!validatePhoneNumber(phoneNumber)) { throw new Error("Invalid phone number"); } await client.sendOTP(phoneNumber); console.log("OTP sent successfully"); // Verify OTP (typically called after user enters OTP) const otpCode = "123456"; // From user input const result = await client.verifyOTP(otpCode); if (result.success) { console.log("Authentication successful!", result.userInfo); } } catch (error) { console.error("Authentication failed:", error); } } ``` ## API Reference ### Core Client #### `OAuthClient` ```typescript import { OAuthClient } from "@elusion/auth"; const client = new OAuthClient(config); ``` **Configuration options:** - `authServerUrl`: Your OAuth server base URL - `clientId`: Your OAuth client ID - `redirectUri`: Callback URL for your application - `scopes`: Optional array of OAuth scopes - `storage`: Optional custom storage implementation **Methods:** - `sendOTP(phoneNumber: string)`: Send OTP to phone number - `verifyOTP(otpCode: string)`: Verify OTP and complete authentication - `isAuthenticated()`: Check if user is currently authenticated - `getCurrentUser()`: Get current user information - `logout()`: Clear authentication state - `makeAuthenticatedRequest<T>(url, options)`: Make API calls with authentication ### React Hooks #### `useOAuth(config?)` Main hook for OAuth state management. ```typescript const { isLoading, isAuthenticated, user, error, otpSent, sendOTP, verifyOTP, logout, makeAuthenticatedRequest, } = useOAuth(); ``` #### `usePhoneAuth(config?)` Specialized hook for phone authentication flow. ```typescript const { phoneNumber, setPhoneNumber, otpCode, setOtpCode, sendOTP, verifyOTP, isLoading, otpSent, error, } = usePhoneAuth(); ``` #### `useOTPTimer(expiresAt?)` Hook for OTP countdown timer. ```typescript const { timeLeft, isExpired, formatTime } = useOTPTimer(otpExpiresAt); ``` ### React Components #### `<AuthProvider>` Provides OAuth context to child components. ```tsx <AuthProvider config={oauthConfig}> <App /> </AuthProvider> ``` #### `<PhoneAuthForm>` Complete authentication form component. ```tsx <PhoneAuthForm onSuccess={() => console.log("Success!")} onError={(error) => console.error(error)} className="custom-form-class" > {({ phoneNumber, setPhoneNumber, sendOTP, isLoading }) => ( // Custom form implementation <form onSubmit={() => sendOTP()}> <input value={phoneNumber} onChange={(e) => setPhoneNumber(e.target.value)} disabled={isLoading} /> <button type="submit">Send OTP</button> </form> )} </PhoneAuthForm> ``` #### `<ProtectedRoute>` Protect routes that require authentication. ```tsx <ProtectedRoute redirectTo="/login"> <Dashboard /> </ProtectedRoute> ``` ### Error Handling The package provides specific error types: ```typescript import { OAuthError, ValidationError } from "@elusion/auth"; try { await client.sendOTP("invalid-phone"); } catch (error) { if (error instanceof ValidationError) { console.log("Validation error:", error.field); } else if (error instanceof OAuthError) { console.log("OAuth error:", error.code); } } ``` ## Advanced Usage ### Custom Storage Implement custom storage for different environments: ```typescript import { StorageInterface } from "@elusion/auth"; class AsyncStorageAdapter implements StorageInterface { getItem(key: string): string | null { // Implement synchronous get (use cached values) return this.cache.get(key) || null; } setItem(key: string, value: string): void { // Implement synchronous set (cache and async persist) this.cache.set(key, value); this.persistToAsyncStorage(key, value); } removeItem(key: string): void { this.cache.delete(key); this.removeFromAsyncStorage(key); } } const client = new OAuthClient({ // ... other config storage: new AsyncStorageAdapter(), }); ``` ### Event Handling Listen to authentication events: ```typescript client.on("auth_start", () => console.log("Authentication started")); client.on("otp_sent", ({ phoneNumber }) => console.log(`OTP sent to ${phoneNumber}`) ); client.on("auth_success", ({ userInfo }) => console.log("User authenticated:", userInfo) ); client.on("auth_error", ({ error }) => console.error("Auth error:", error)); ``` ### Server-Side Rendering (SSR) For Next.js or other SSR frameworks: ```typescript // Check authentication status on server export async function getServerSideProps(context) { // You would typically verify the token server-side const isAuthenticated = checkTokenFromCookie(context.req); if (!isAuthenticated) { return { redirect: { destination: "/login", permanent: false, }, }; } return { props: {} }; } ``` ## Environment Variables For Next.js applications, add these to your `.env.local`: ```env NEXT_PUBLIC_AUTH_SERVER_URL=https://your-auth-server.com NEXT_PUBLIC_CLIENT_ID=your-client-id NEXT_PUBLIC_BASE_URL=https://your-app.com ``` ## Building ```bash # Install dependencies bun install # Build the package bun run build # Run tests bun test # Type checking bun run type-check ``` ## Contributing 1. Fork the repository 2. Create your feature branch 3. Make your changes 4. Add tests if applicable 5. Run the build and ensure everything passes 6. Submit a pull request ## License MIT License - see LICENSE file for details.