# 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.
