npm package: @passkeyme/auth
Description: Passkeyme Authentication SDK - Simpler than Firebase Auth
Author: Passkeyme
Latest version: 2.3.3

# @passkeyme/auth

A simple, powerful authentication SDK that makes authentication easier than Firebase Auth.

## 🚀 Features

- **🔐 Multiple Auth Methods**: OAuth (Google, GitHub), Passkeys (WebAuthn), Username/Password
- **� Hosted Authentication**: Secure, pre-built auth pages - no UI to build
- **🔄 Automatic Token Management**: Auto-refresh, HTTP interceptors
- **🏠 Privacy-First**: Self-hosted option, no vendor lock-in
- **📱 Modern**: Built-in passkey support, TypeScript-first
- **⚡ Lightweight**: Pure JavaScript, no heavy dependencies

## 📦 Installation

```bash
npm install @passkeyme/auth
```

## 🔧 Quick Start

### 1. Initialize the SDK

```typescript
import { PasskeymeAuth } from "@passkeyme/auth";

const auth = new PasskeymeAuth({
  appId: "your-passkeyme-app-id",
  redirectUri: "http://localhost:3000/auth/callback",
});

// Initialize on app start
await auth.init();
```

### 2. Choose Your Authentication Approach

#### **Approach 1: Hosted Auth Pages** ✨ (Recommended)

```typescript
// Redirects to your branded hosted login page
// User sees all available login methods (OAuth, Passkey, Username/Password)
auth.redirectToLogin();

// With options
auth.redirectToLogin({
  authMethod: "passkey", // Pre-select passkey method
  state: "custom-state", // Custom state for callback
});
```

#### **Approach 2: Direct OAuth** ⚡

```typescript
// Skip hosted page, go directly to OAuth provider
auth.redirectToOAuth("google");
auth.redirectToOAuth("github");
auth.redirectToOAuth("facebook");

// With custom redirect URI
auth.redirectToOAuth("google", "http://localhost:3000/custom/callback");
```

#### **The Key Difference:**

- `redirectToLogin()` → Shows your branded page with all auth options
- `redirectToOAuth('google')` → Goes directly to Google OAuth (skips hosted page)

#### Direct OAuth Provider

```typescript
// Skip login page, go directly to OAuth provider
auth.redirectToOAuth("google");
auth.redirectToOAuth("github");
auth.redirectToOAuth("facebook");
```

### 3. Handle Authentication Callback

On your callback page (`/auth/callback`):

```typescript
// This handles the authentication return and gets the user
const user = await auth.handleAuthCallback();

if (user) {
  // Redirect to your app
  window.location.href = "/dashboard";
} else {
  // Handle auth failure
  window.location.href = "/login?error=auth_failed";
}
```

### 4. Check Authentication Status

```typescript
// Check if user is logged in
const user = auth.getCurrentUser();

if (user) {
  console.log("Logged in as:", user.email);
} else {
  console.log("Not logged in");
}

// Or check auth state
console.log("Auth state:", auth.state);

// Check on page load
await auth.init(); // This will restore session if available
```

### 5. Logout

```typescript
await auth.logout();
```

## 🔗 HTTP Client Integration

Automatically add auth headers to your API calls:

### With Axios

```typescript
import axios from "axios";
import { setupHttpInterceptor } from "@passkeyme/auth";

const apiClient = axios.create({
  baseURL: "https://your-api.com",
});

// Setup automatic token management
const cleanup = setupHttpInterceptor(auth, apiClient);

// Now all requests automatically include auth headers
const response = await apiClient.get("/protected-data");
```

### With Fetch

```typescript
import { createAuthenticatedFetch } from "@passkeyme/auth";

const authenticatedFetch = createAuthenticatedFetch(auth);

// Use like regular fetch, but with automatic auth headers
const response = await authenticatedFetch("/api/protected-data");
```

### Manual Header Management

```typescript
import { addAuthHeader } from "@passkeyme/auth";

const headers = await addAuthHeader(auth, {
  "Content-Type": "application/json",
});

fetch("/api/data", { headers });
```

## 🎭 Event Handling

Listen to authentication events:

```typescript
const unsubscribe = auth.addEventListener((event) => {
  switch (event.type) {
    case "login":
      console.log("User logged in:", event.user);
      break;
    case "logout":
      console.log("User logged out");
      break;
    case "token_refresh":
      console.log("Token refreshed");
      break;
    case "error":
      console.error("Auth error:", event.error);
      break;
  }
});

// Clean up when done
unsubscribe();
```

## ⚙️ Configuration Options

### Basic Configuration

```typescript
const auth = new PasskeymeAuth({
  appId: "your-app-id", // Required: From Passkeyme dashboard
  redirectUri: "http://localhost:3000/callback", // Optional: Default callback URL
  debug: true, // Optional: Enable debug logging
});
```

### Custom Domain Configuration

```typescript
const auth = new PasskeymeAuth({
  appId: "your-app-id",
  apiUrl: "https://auth.yourdomain.com", // Your custom domain
  redirectUri: "https://yourapp.com/callback",
});
```

### Self-Hosted Configuration

```typescript
const auth = new PasskeymeAuth({
  appId: "your-app-id",
  apiUrl: "http://localhost:8000", // Your self-hosted server
  redirectUri: "http://localhost:3000/callback",
  debug: true, // Helpful for development
});
```

### Storage Configuration

```typescript
import { PasskeymeAuth, BrowserStorageProvider } from "@passkeyme/auth";

// Default behavior (localStorage)
const auth = new PasskeymeAuth({
  appId: "your-app-id",
});

// Use sessionStorage instead
const auth = new PasskeymeAuth({
  appId: "your-app-id",
  storage: new BrowserStorageProvider(true), // true = use sessionStorage
});

// Custom storage implementation
class CustomStorageProvider {
  async getItem(key) {
    /* your implementation */
  }
  async setItem(key, value) {
    /* your implementation */
  }
  async removeItem(key) {
    /* your implementation */
  }
}

const auth = new PasskeymeAuth({
  appId: "your-app-id",
  storage: new CustomStorageProvider(),
});
```

### State Subscription

```typescript
// Subscribe to authentication state changes
const unsubscribe = auth.subscribe((state) => {
  console.log("Auth state changed:", state);
  // { user, loading, error, isAuthenticated }
});

// Get current state
const currentState = auth.getState();

// Clean up when done
unsubscribe();
```

## 🆚 vs Firebase Auth

| Feature              | Passkeyme Auth             | Firebase Auth               |
| -------------------- | -------------------------- | --------------------------- |
| **Setup Complexity** | ✅ Single provider         | ❌ Multiple services needed |
| **Hosted Auth UI**   | ✅ Pre-built, customizable | ❌ Build your own           |
| **Passkey Support**  | ✅ Built-in WebAuthn       | ❌ Manual implementation    |
| **Self-Hosting**     | ✅ Full control            | ❌ Google-only              |
| **Vendor Lock-in**   | ✅ Standard JWT            | ❌ Firebase-specific        |
| **Privacy**          | ✅ Your data               | ❌ Google's servers         |
| **Bundle Size**      | ✅ Lightweight             | ❌ Heavy SDK                |

## 📋 API Reference

### PasskeymeAuth Class

```typescript
class PasskeymeAuth {
  // Properties
  readonly state: AuthState;

  // Methods
  init(): Promise<void>;
  redirectToLogin(options?: RedirectOptions): void;
  redirectToOAuth(
    provider: "google" | "github" | "facebook",
    options?: RedirectOptions
  ): void;
  handleAuthCallback(): Promise<User | null>;
  logout(): Promise<void>;
  getCurrentUser(): User | null;
  getAccessToken(): Promise<string | null>;
  refreshToken(): Promise<string>;
  addEventListener(listener: AuthEventListener): () => void;
  removeEventListener(listener: AuthEventListener): void;
}
```

### Types

```typescript
interface User {
  id: string;
  email?: string;
  name?: string;
  picture?: string;
  provider?: string;
  createdAt?: string;
  lastLoginAt?: string;
}

interface AuthState {
  user: User | null;
  loading: boolean;
  error: string | null;
  isAuthenticated: boolean;
}

interface RedirectOptions {
  redirectTo?: string; // Where to go after successful auth
  state?: string; // Custom state parameter
}
```

## 🔧 Advanced Usage

### Handling the Complete Flow

```typescript
// 1. App initialization
const auth = new PasskeymeAuth({
  appId: "your-app-id",
  redirectUri: "http://localhost/auth/callback",
});

await auth.init();

// 2. Login button click
document.getElementById("login-btn").addEventListener("click", () => {
  auth.redirectToLogin({ redirectTo: "/dashboard" });
});

// 3. On callback page (/auth/callback)
const user = await auth.handleAuthCallback();
if (user) {
  // Redirect to the intended page
  const urlParams = new URLSearchParams(window.location.search);
  const redirectTo = urlParams.get("redirectTo") || "/";
  window.location.href = redirectTo;
}
```

## 🔗 TypeScript Integration

This package is written in TypeScript and provides comprehensive type definitions.

### Type Imports

```typescript
import type {
  PasskeymeConfig,
  User,
  AuthTokens,
  OAuthProvider,
  PasskeyCredential,
  AuthState,
  TokenStorage,
  HttpInterceptor,
} from "@passkeyme/auth";

// Configuration interface
interface PasskeymeConfig {
  appId: string;
  baseUrl?: string; // Default: 'https://auth.passkeyme.com'
  redirectUri: string;
  debug?: boolean; // Enable debug logging
  tokenStorage?: TokenStorage; // Custom token storage
  httpInterceptors?: HttpInterceptor[]; // Custom HTTP interceptors
}

// User object structure
interface User {
  id: string;
  email: string;
  name?: string;
  avatar?: string;
  emailVerified: boolean;
  createdAt: string;
  lastLoginAt: string;
  metadata?: Record<string, any>;
}

// Authentication tokens
interface AuthTokens {
  accessToken: string;
  refreshToken: string;
  expiresAt: number;
  tokenType: string;
}
```

### Advanced Configuration

```typescript
import { PasskeymeAuth, LocalStorageTokenStorage } from "@passkeyme/auth";

// Custom token storage implementation
class SecureTokenStorage implements TokenStorage {
  async setTokens(tokens: AuthTokens): Promise<void> {
    // Store in encrypted storage or secure keychain
    await secureStorage.set("auth_tokens", encrypt(tokens));
  }

  async getTokens(): Promise<AuthTokens | null> {
    const encrypted = await secureStorage.get("auth_tokens");
    return encrypted ? decrypt(encrypted) : null;
  }

  async clearTokens(): Promise<void> {
    await secureStorage.remove("auth_tokens");
  }
}

// HTTP interceptor for API calls
const apiInterceptor: HttpInterceptor = {
  request: async (config) => {
    // Add custom headers or modify requests
    config.headers = {
      ...config.headers,
      "X-Client-Version": "1.0.0",
      "X-Client-Platform": "web",
    };
    return config;
  },

  response: async (response) => {
    // Handle responses globally
    if (response.status === 401) {
      // Handle unauthorized responses
      await auth.logout();
      window.location.href = "/login";
    }
    return response;
  },
};

const auth = new PasskeymeAuth({
  appId: "your-app-id",
  redirectUri: "https://your-app.com/auth/callback",
  debug: process.env.NODE_ENV === "development",
  tokenStorage: new SecureTokenStorage(),
  httpInterceptors: [apiInterceptor],
});
```

### API Client Integration

```typescript
import { PasskeymeAuth } from "@passkeyme/auth";

class ApiClient {
  private auth: PasskeymeAuth;

  constructor(auth: PasskeymeAuth) {
    this.auth = auth;
  }

  async makeAuthenticatedRequest<T>(
    url: string,
    options: RequestInit = {}
  ): Promise<T> {
    const authHeader = await this.auth.getAuthHeader();

    const response = await fetch(url, {
      ...options,
      headers: {
        ...options.headers,
        Authorization: authHeader,
        "Content-Type": "application/json",
      },
    });

    if (!response.ok) {
      if (response.status === 401) {
        // Try to refresh token
        await this.auth.refreshToken();
        // Retry request with new token
        return this.makeAuthenticatedRequest(url, options);
      }
      throw new Error(`API Error: ${response.status}`);
    }

    return response.json();
  }

  // Type-safe API methods
  async getUserProfile(): Promise<User> {
    return this.makeAuthenticatedRequest<User>("/api/user/profile");
  }

  async updateUserProfile(updates: Partial<User>): Promise<User> {
    return this.makeAuthenticatedRequest<User>("/api/user/profile", {
      method: "PATCH",
      body: JSON.stringify(updates),
    });
  }
}
```

### Framework Integration Examples

#### Vanilla JavaScript (SPA)

```typescript
// main.js
const auth = new PasskeymeAuth({
  appId: "your-app-id",
  redirectUri: window.location.origin + "/auth/callback",
});

await auth.init();

// Router logic
if (window.location.pathname === "/auth/callback") {
  const user = await auth.handleAuthCallback();
  if (user) {
    window.location.href = "/dashboard";
  }
} else if (!auth.getCurrentUser() && window.location.pathname !== "/login") {
  window.location.href = "/login";
}
```

#### Next.js Pages Router

```typescript
// pages/auth/callback.js
import { useEffect } from "react";
import { useRouter } from "next/router";
import { auth } from "../lib/auth";

export default function AuthCallback() {
  const router = useRouter();

  useEffect(() => {
    auth.handleAuthCallback().then((user) => {
      if (user) {
        router.push("/dashboard");
      } else {
        router.push("/login?error=auth_failed");
      }
    });
  }, []);

  return <div>Completing authentication...</div>;
}
```

## 🛠️ Development

This package is part of the Passkeyme project. For development:

```bash
# Install dependencies
npm install

# Build the package
npm run build

# Run tests
npm test

# Development mode (watch)
npm run dev
```

## 📄 License

MIT - see LICENSE file for details.

## 🤝 Support

- **Documentation**: https://docs.passkeyme.com
- **Issues**: https://github.com/passkeyme/passkeyme/issues
- **Discord**: https://discord.gg/passkeyme