@elusion-sdk/auth
v0.0.1
Published
TypeScript OAuth client for SMS OTP authentication
Maintainers
Readme
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
# Using bun
bun add @elusion/auth
# Using npm
npm install @elusion/auth
# Using yarn
yarn add @elusion/authQuick Start
React Application
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
// 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
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
import { OAuthClient } from "@elusion/auth";
const client = new OAuthClient(config);Configuration options:
authServerUrl: Your OAuth server base URLclientId: Your OAuth client IDredirectUri: Callback URL for your applicationscopes: Optional array of OAuth scopesstorage: Optional custom storage implementation
Methods:
sendOTP(phoneNumber: string): Send OTP to phone numberverifyOTP(otpCode: string): Verify OTP and complete authenticationisAuthenticated(): Check if user is currently authenticatedgetCurrentUser(): Get current user informationlogout(): Clear authentication statemakeAuthenticatedRequest<T>(url, options): Make API calls with authentication
React Hooks
useOAuth(config?)
Main hook for OAuth state management.
const {
isLoading,
isAuthenticated,
user,
error,
otpSent,
sendOTP,
verifyOTP,
logout,
makeAuthenticatedRequest,
} = useOAuth();usePhoneAuth(config?)
Specialized hook for phone authentication flow.
const {
phoneNumber,
setPhoneNumber,
otpCode,
setOtpCode,
sendOTP,
verifyOTP,
isLoading,
otpSent,
error,
} = usePhoneAuth();useOTPTimer(expiresAt?)
Hook for OTP countdown timer.
const { timeLeft, isExpired, formatTime } = useOTPTimer(otpExpiresAt);React Components
<AuthProvider>
Provides OAuth context to child components.
<AuthProvider config={oauthConfig}>
<App />
</AuthProvider><PhoneAuthForm>
Complete authentication form component.
<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.
<ProtectedRoute redirectTo="/login">
<Dashboard />
</ProtectedRoute>Error Handling
The package provides specific error types:
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:
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:
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:
// 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:
NEXT_PUBLIC_AUTH_SERVER_URL=https://your-auth-server.com
NEXT_PUBLIC_CLIENT_ID=your-client-id
NEXT_PUBLIC_BASE_URL=https://your-app.comBuilding
# Install dependencies
bun install
# Build the package
bun run build
# Run tests
bun test
# Type checking
bun run type-checkContributing
- Fork the repository
- Create your feature branch
- Make your changes
- Add tests if applicable
- Run the build and ensure everything passes
- Submit a pull request
License
MIT License - see LICENSE file for details.
