npm package: @omam/authkit-sdk
Description: Official JavaScript/TypeScript SDK for OmamAuthKit OAuth Provider
Author: Osman
Latest version: 1.0.0

# OMAM AuthKit SDK

Official JavaScript/TypeScript SDK for [OMAM AuthKit](https://github.com/omam/authkit) OAuth 2.0 authentication provider.

[![npm version](https://img.shields.io/npm/v/@omam/authkit-sdk.svg)](https://www.npmjs.com/package/@omam/authkit-sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)

## Features

- **Framework Agnostic** - Works with vanilla JavaScript, React, Vue, Angular, Next.js, and more
- **TypeScript First** - Fully typed with comprehensive type definitions
- **OAuth 2.0 Compliant** - Authorization Code flow with PKCE support
- **Automatic Token Refresh** - Seamless token lifecycle management
- **Multiple Storage Adapters** - localStorage, memory, cookies, or custom
- **React Hooks** - Ready-to-use hooks for React applications
- **Zero Dependencies** - Only depends on axios for HTTP requests
- **Tree-Shakeable** - Import only what you need
- **SSR Compatible** - Works in server-side rendering environments
- **Event System** - Subscribe to auth events (login, logout, token refresh)

## Installation

```bash
npm install @omam/authkit-sdk
# or
yarn add @omam/authkit-sdk
# or
pnpm add @omam/authkit-sdk
```

## Quick Start

### Vanilla JavaScript/TypeScript

```typescript
import { AuthKitClient } from '@omam/authkit-sdk';

// Initialize the client
const authKit = new AuthKitClient({
  authKitUrl: 'https://auth.yourapp.com',
  clientId: 'your-client-id',
  redirectUri: 'https://yourapp.com/callback',
});

// Login - redirect to authorization page
await authKit.login();

// Handle callback (in your callback route)
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');

if (code && state) {
  await authKit.handleCallback(code, state);
  const user = await authKit.getCurrentUser();
  console.log('Logged in as:', user);
}

// Check if authenticated
const isAuth = await authKit.isAuthenticated();

// Get current user
const user = await authKit.getCurrentUser();

// Logout
await authKit.logout();
```

### React

```tsx
import { AuthProvider, useAuth } from '@omam/authkit-sdk/react';

// Wrap your app with AuthProvider
function App() {
  return (
    <AuthProvider
      config={{
        authKitUrl: 'https://auth.yourapp.com',
        clientId: 'your-client-id',
        redirectUri: 'https://yourapp.com/callback',
      }}
    >
      <YourApp />
    </AuthProvider>
  );
}

// Use the useAuth hook in your components
function YourComponent() {
  const { user, isAuthenticated, isLoading, login, logout } = useAuth();

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (!isAuthenticated) {
    return <button onClick={login}>Login</button>;
  }

  return (
    <div>
      <h1>Welcome, {user?.first_name}!</h1>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

// Handle OAuth callback
function CallbackPage() {
  const { handleCallback } = useAuthCallback();
  const navigate = useNavigate();

  useEffect(() => {
    const urlParams = new URLSearchParams(window.location.search);
    const code = urlParams.get('code');
    const state = urlParams.get('state');

    if (code && state) {
      handleCallback(code, state)
        .then(() => navigate('/dashboard'))
        .catch((error) => console.error('Auth failed:', error));
    }
  }, []);

  return <div>Processing login...</div>;
}
```

### Next.js

```tsx
// app/providers.tsx
'use client';

import { AuthProvider } from '@omam/authkit-sdk/react';

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <AuthProvider
      config={{
        authKitUrl: process.env.NEXT_PUBLIC_AUTHKIT_URL!,
        clientId: process.env.NEXT_PUBLIC_CLIENT_ID!,
        redirectUri: process.env.NEXT_PUBLIC_REDIRECT_URI!,
      }}
    >
      {children}
    </AuthProvider>
  );
}

// app/layout.tsx
import { Providers } from './providers';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
```

## Configuration

### Basic Configuration

```typescript
const authKit = new AuthKitClient({
  // Required
  authKitUrl: 'https://auth.yourapp.com',
  clientId: 'your-client-id',
  redirectUri: 'https://yourapp.com/callback',

  // Optional
  clientSecret: 'your-client-secret', // For confidential clients
  scopes: ['read', 'write', 'profile', 'email'],
  pkce: true, // Enable PKCE (default: true)
  autoRefresh: true, // Auto-refresh tokens (default: true)
  refreshThreshold: 300, // Refresh 5 minutes before expiry
  debug: false, // Enable debug logging
  timeout: 30000, // HTTP request timeout (ms)
  headers: {}, // Custom headers for all requests
});
```

### Storage Adapters

```typescript
import {
  AuthKitClient,
  LocalStorageAdapter,
  MemoryStorageAdapter,
  CookieStorageAdapter,
} from '@omam/authkit-sdk';

// Use localStorage (default in browser)
const authKit = new AuthKitClient({
  storage: new LocalStorageAdapter(),
  // ...other config
});

// Use memory storage (for SSR or testing)
const authKit = new AuthKitClient({
  storage: new MemoryStorageAdapter(),
  // ...other config
});

// Use cookie storage
const authKit = new AuthKitClient({
  storage: new CookieStorageAdapter({
    domain: '.yourapp.com',
    secure: true,
    sameSite: 'lax',
    maxAge: 7, // days
  }),
  // ...other config
});

// Custom storage adapter
class MyStorageAdapter implements StorageAdapter {
  async getItem(key: string): Promise<string | null> {
    // Your implementation
  }
  async setItem(key: string, value: string): Promise<void> {
    // Your implementation
  }
  async removeItem(key: string): Promise<void> {
    // Your implementation
  }
  async clear(): Promise<void> {
    // Your implementation
  }
}
```

## API Reference

### AuthKitClient

#### Methods

##### `getAuthorizationUrl(additionalParams?)`
Generate OAuth authorization URL.

```typescript
const url = await authKit.getAuthorizationUrl({ prompt: 'consent' });
```

##### `login()`
Redirect to authorization endpoint (browser only).

```typescript
await authKit.login();
```

##### `handleCallback(code, state)`
Exchange authorization code for tokens.

```typescript
const tokens = await authKit.handleCallback(code, state);
```

##### `getCurrentUser()`
Get current authenticated user.

```typescript
const user = await authKit.getCurrentUser();
```

##### `refreshAccessToken()`
Manually refresh access token.

```typescript
const tokens = await authKit.refreshAccessToken();
```

##### `register(userData)`
Register a new user.

```typescript
const user = await authKit.register({
  email: 'user@example.com',
  password: 'securepassword',
  password_confirm: 'securepassword',
  first_name: 'John',
  last_name: 'Doe',
});
```

##### `logout()`
Logout and clear all tokens.

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

##### `isAuthenticated()`
Check if user is authenticated.

```typescript
const isAuth = await authKit.isAuthenticated();
```

##### `getAccessToken()`
Get current access token.

```typescript
const token = await authKit.getAccessToken();
```

##### `getTokenMetadata()`
Get detailed token metadata.

```typescript
const metadata = await authKit.getTokenMetadata();
// { accessToken, refreshToken, tokenType, expiresAt, scopes }
```

##### `getSocialLoginUrl(provider, config?)`
Get social login URL.

```typescript
const url = await authKit.getSocialLoginUrl('google');
```

##### `loginWithSocial(provider, config?)`
Redirect to social login (browser only).

```typescript
await authKit.loginWithSocial('google');
```

##### `handleSocialCallback()`
Handle social authentication callback.

```typescript
const user = await authKit.handleSocialCallback();
```

### Events

Subscribe to authentication events:

```typescript
// Token refreshed
authKit.on('token_refreshed', (data) => {
  console.log('Token refreshed:', data);
});

// Token expired
authKit.on('token_expired', (data) => {
  console.log('Token expired:', data);
});

// User logged in
authKit.on('user_logged_in', (data) => {
  console.log('User logged in:', data);
});

// User logged out
authKit.on('user_logged_out', () => {
  console.log('User logged out');
});

// User registered
authKit.on('user_registered', (data) => {
  console.log('User registered:', data);
});

// Auth error
authKit.on('auth_error', ({ error }) => {
  console.error('Auth error:', error);
});

// Unsubscribe
const unsubscribe = authKit.on('token_refreshed', handler);
unsubscribe(); // Remove listener
```

### React Hooks

#### `useAuth()`
Main authentication hook.

```tsx
const { user, isAuthenticated, isLoading, error, login, logout, refreshUser } = useAuth();
```

#### `useUser()`
Get user information.

```tsx
const { user, isLoading, error } = useUser();
```

#### `useToken()`
Get access token.

```tsx
const { token, isLoading } = useToken();
```

#### `useTokenMetadata()`
Get token metadata.

```tsx
const { metadata, isLoading } = useTokenMetadata();
```

#### `useAuthCallback()`
Handle OAuth callback.

```tsx
const { handleCallback, isProcessing, error } = useAuthCallback();
```

#### `useRegister()`
User registration.

```tsx
const { register, isLoading, error } = useRegister();

await register({
  email: 'user@example.com',
  password: 'password',
  password_confirm: 'password',
});
```

## Error Handling

The SDK provides custom error classes for better error handling:

```typescript
import {
  AuthKitError,
  AuthenticationError,
  TokenExpiredError,
  ValidationError,
} from '@omam/authkit-sdk';

try {
  await authKit.getCurrentUser();
} catch (error) {
  if (error instanceof TokenExpiredError) {
    // Token expired - try to refresh
    await authKit.refreshAccessToken();
  } else if (error instanceof AuthenticationError) {
    // Not authenticated - redirect to login
    await authKit.login();
  } else if (error instanceof ValidationError) {
    // Validation failed
    console.error('Validation error:', error.message);
  } else if (error instanceof AuthKitError) {
    // Generic AuthKit error
    console.error('AuthKit error:', error.code, error.message);
  }
}
```

### Error Types

- `AuthKitError` - Base error class
- `AuthenticationError` - Authentication failed (401)
- `AuthorizationError` - Authorization failed (403)
- `TokenError` - Token-related error
- `TokenExpiredError` - Token has expired
- `InvalidStateError` - OAuth state mismatch
- `ConfigurationError` - Invalid configuration
- `NetworkError` - Network request failed
- `StorageError` - Storage operation failed
- `ValidationError` - Validation failed
- `MissingParameterError` - Required parameter missing
- `PKCERequiredError` - PKCE required but not configured
- `RefreshTokenUnavailableError` - Refresh token not available

## Examples

See the [examples](./examples) directory for complete working examples:

- [Vanilla JavaScript](./examples/vanilla-js)
- [React](./examples/react)
- [Next.js](./examples/nextjs)
- [Node.js](./examples/node)
- [Vue](./examples/vue)

## Advanced Usage

### PKCE (Proof Key for Code Exchange)

PKCE is enabled by default for enhanced security. To disable:

```typescript
const authKit = new AuthKitClient({
  pkce: false,
  // ...other config
});
```

### Custom HTTP Headers

```typescript
const authKit = new AuthKitClient({
  headers: {
    'X-Custom-Header': 'value',
  },
  // ...other config
});
```

### Debug Mode

```typescript
const authKit = new AuthKitClient({
  debug: true, // Enable debug logging
  // ...other config
});
```

### Token Refresh Threshold

Automatically refresh tokens before they expire:

```typescript
const authKit = new AuthKitClient({
  autoRefresh: true,
  refreshThreshold: 300, // Refresh 5 minutes (300 seconds) before expiry
  // ...other config
});
```

## TypeScript Support

The SDK is written in TypeScript and provides comprehensive type definitions:

```typescript
import type {
  User,
  TokenResponse,
  RegistrationData,
  AuthKitConfig,
  StorageAdapter,
} from '@omam/authkit-sdk';

const user: User = await authKit.getCurrentUser();
const tokens: TokenResponse = await authKit.handleCallback(code, state);
```

## Browser Support

- Chrome/Edge (last 2 versions)
- Firefox (last 2 versions)
- Safari (last 2 versions)
- iOS Safari (last 2 versions)
- Android Chrome (last 2 versions)

## Node.js Support

- Node.js 14+

## License

MIT © OMAM