npm package: @qapps/qauth-sdk
Description: Framework-agnostic authentication SDK for qAuth service
Author: ExaCall
Latest version: 2.0.0

# qAuth SDK

[![npm version](https://badge.fury.io/js/@qapps%2F@qapps/qauth-sdk.svg)](https://badge.fury.io/js/@qapps%2F@qapps/qauth-sdk)
[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A **framework-agnostic** TypeScript/JavaScript SDK for authentication and authorization using the qAuth service. This SDK provides robust token validation, permission checking, and seamless integration with popular frameworks like NestJS, Express.js, Next.js, and Fastify.

## 🚀 Features

- **🔐 Token Validation**: Validate JWT tokens and API keys via REST API
- **🛡️ Permission Checking**: Support for Role-Based Access Control (RBAC)
- **🌍 Multi-Environment**: Built-in support for dev, staging, and production environments
- **⚙️ Framework Agnostic**: Works with any JavaScript/TypeScript framework
- **📝 TypeScript First**: Full TypeScript support with comprehensive type definitions
- **🔄 Retry Logic**: Automatic retry with exponential backoff for transient failures
- **⏱️ Timeout Handling**: Configurable request timeouts
- **📊 Error Handling**: Comprehensive error types with detailed information

## 📦 Installation

```bash
npm install @qapps/qauth-sdk
```

```bash
yarn add @qapps/qauth-sdk
```

```bash
pnpm add @qapps/qauth-sdk
```

## 🏁 Quick Start

```typescript
import { QAuthClient } from '@qapps/qauth-sdk';

// Initialize the client
const qauth = new QAuthClient({
  environment: 'dev', // 'dev', 'staging', 'prd', or 'production'
  timeout: 5000, // Request timeout in milliseconds
  retryAttempts: 3, // Number of retry attempts
});

// Validate a token
const result = await qauth.validateToken('your-jwt-token-or-api-key');

if (result.isValid) {
  console.log('Token is valid!', result.data);
} else {
  console.log('Token is invalid:', result.message);
}
```

## 🔧 Configuration

### Environment Configuration

The SDK supports multiple environments:

```typescript
const qauth = new QAuthClient({
  environment: 'dev',        // Development environment
  // environment: 'staging',  // Staging environment
  // environment: 'prd',      // Production environment
  // environment: 'production', // Alias for 'prd'
});
```

### Custom Base URL

Override the default environment URLs:

```typescript
const qauth = new QAuthClient({
  baseURL: 'https://your-custom-domain.com/qauth',
});
```

### Advanced Configuration

```typescript
const qauth = new QAuthClient({
  environment: 'dev',
  timeout: 10000,          // 10 second timeout
  retryAttempts: 5,        // Retry up to 5 times
  enableLogging: true,     // Enable debug logging
});
```

## 🛠️ API Reference

### `QAuthClient`

#### Constructor

```typescript
new QAuthClient(config?: QAuthConfig)
```

#### Methods

##### `validateToken(token, permissions?)`

Validates a token with optional permission checking.

```typescript
async validateToken(
  token: string,
  permissions?: string[]
): Promise<ValidationResult>
```

**Parameters:**
- `token` - JWT token (with or without Bearer prefix) or API key
- `permissions` - Optional array of required permissions

**Returns:** `ValidationResult` object with validation details

##### `validateTokenStrict(token, permissions?)`

Same as `validateToken` but throws an error if validation fails.

```typescript
async validateTokenStrict(
  token: string,
  permissions?: string[]
): Promise<ValidationResult>
```

##### `isTokenValid(token)`

Simple boolean check for token validity.

```typescript
async isTokenValid(token: string): Promise<boolean>
```

##### `getTokenData(token)`

Get token data without permission validation.

```typescript
async getTokenData(token: string): Promise<TokenData | ApiKeyData | null>
```

## 🔐 Permission Validation

The SDK supports Role-Based Access Control (RBAC) by checking if the user has required permissions:

```typescript
// Validate token with required permissions
const result = await qauth.validateToken('token', [
  'users:read',
  'users:create',
  'posts:read'
]);

if (result.isValid) {
  // User has all required permissions
  console.log('Access granted');
} else {
  // Missing permissions or invalid token
  console.log('Access denied:', result.message);
}
```

## 🏗️ Framework Integration

### NestJS

Create a guard for route protection:

```typescript
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { QAuthClient } from '@qapps/qauth-sdk';

@Injectable()
export class QAuthGuard implements CanActivate {
  private qauth = new QAuthClient({ environment: 'dev' });

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);

    if (!token) return false;

    try {
      const result = await this.qauth.validateToken(token);
      request.user = result.data;
      return result.isValid;
    } catch {
      return false;
    }
  }

  private extractTokenFromHeader(request: any): string | undefined {
    const authHeader = request.headers.authorization;
    return authHeader?.startsWith('Bearer ') ? authHeader : undefined;
  }
}

// Use the guard
@Controller('users')
@UseGuards(QAuthGuard)
export class UsersController {
  @Get()
  getUsers() {
    return 'Protected route';
  }
}
```

### Express.js

Create middleware for route protection:

```typescript
import express from 'express';
import { QAuthClient } from '@qapps/qauth-sdk';

const qauth = new QAuthClient({ environment: 'dev' });

export const authMiddleware = (requiredPermissions?: string[]) => {
  return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
    try {
      const token = req.headers.authorization;
      
      if (!token) {
        return res.status(401).json({ error: 'No token provided' });
      }

      const result = await qauth.validateToken(token, requiredPermissions);
      
      if (result.isValid) {
        req.user = result.data;
        next();
      } else {
        res.status(401).json({ error: result.message });
      }
    } catch (error) {
      res.status(500).json({ error: 'Authentication error' });
    }
  };
};

// Use the middleware
app.get('/protected', authMiddleware(['users:read']), (req, res) => {
  res.json({ message: 'Access granted', user: req.user });
});
```

### Next.js

Create an API route handler:

```typescript
// pages/api/protected.ts or app/api/protected/route.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { QAuthClient } from '@qapps/qauth-sdk';

const qauth = new QAuthClient({ environment: 'dev' });

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  try {
    const token = req.headers.authorization;
    
    if (!token) {
      return res.status(401).json({ error: 'No token provided' });
    }

    const result = await qauth.validateToken(token);
    
    if (result.isValid) {
      res.json({ message: 'Access granted', user: result.data });
    } else {
      res.status(401).json({ error: result.message });
    }
  } catch (error) {
    res.status(500).json({ error: 'Authentication error' });
  }
}
```

### Fastify

Create a plugin for authentication:

```typescript
import fastify from 'fastify';
import { QAuthClient } from '@qapps/qauth-sdk';

const qauth = new QAuthClient({ environment: 'dev' });

// Register auth plugin
fastify.register(async function (fastify) {
  fastify.decorateRequest('user', null);

  fastify.addHook('preHandler', async (request, reply) => {
    if (request.url === '/protected') {
      const token = request.headers.authorization;
      
      if (!token) {
        return reply.code(401).send({ error: 'No token provided' });
      }

      try {
        const result = await qauth.validateToken(token);
        
        if (result.isValid) {
          request.user = result.data;
        } else {
          return reply.code(401).send({ error: result.message });
        }
      } catch (error) {
        return reply.code(500).send({ error: 'Authentication error' });
      }
    }
  });
});

// Protected route
fastify.get('/protected', async (request, reply) => {
  return { message: 'Access granted', user: request.user };
});
```

## 🔍 Error Handling

The SDK provides comprehensive error types for different scenarios:

```typescript
import { 
  QAuthError,
  NetworkError,
  ValidationError,
  ConfigurationError,
  PermissionError,
  TimeoutError,
  ResponseFormatError 
} from '@qapps/qauth-sdk';

try {
  const result = await qauth.validateToken('invalid-token');
} catch (error) {
  if (error instanceof NetworkError) {
    console.log('Network issue:', error.message);
  } else if (error instanceof ValidationError) {
    console.log('Validation failed:', error.message);
  } else if (error instanceof PermissionError) {
    console.log('Missing permissions:', error.missingPermissions);
  } else if (error instanceof TimeoutError) {
    console.log('Request timed out after:', error.timeoutMs, 'ms');
  } else if (error instanceof ConfigurationError) {
    console.log('Configuration error:', error.message);
  }
}
```

## 📊 Response Types

### ValidationResult

```typescript
interface ValidationResult {
  success: boolean;      // API request success
  isValid: boolean;      // Token/permission validity
  message: string;       // Human-readable message
  isExpired?: boolean;   // Token expiration status (JWT only)
  data?: TokenData | ApiKeyData; // Token/API key data
  extra?: Record<string, any>;   // Additional metadata
}
```

### TokenData (JWT)

```typescript
interface TokenData {
  tenantId: string;
  organizationId: string;
  userId: string;
  permissions: string[];
  extra: ExtraData;
}
```

### ApiKeyData

```typescript
interface ApiKeyData {
  tenantId: string;
  organizationId: string;
  apiKeyId: string;
  permissions: string[];
  extra: ExtraData;
}
```

## 🧪 Testing

The SDK includes comprehensive test coverage for all functionality:

```bash
# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Run tests in watch mode
npm run test:watch
```

## 🔧 Development

```bash
# Install dependencies
npm install

# Build the SDK
npm run build

# Run linting
npm run lint

# Format code
npm run format

# Type checking
npm run type-check
```

## 📝 TypeScript Support

The SDK is built with TypeScript and provides full type definitions:

```typescript
import { QAuthClient, QAuthConfig, ValidationResult, TokenData } from '@qapps/qauth-sdk';

const config: QAuthConfig = {
  environment: 'dev',
  timeout: 5000,
};

const client = new QAuthClient(config);
const result: ValidationResult = await client.validateToken('token');
```

## 🤝 Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests for any improvements.

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🏢 About ExaCall

This SDK is developed and maintained by ExaCall. For more information about our authentication services, please visit our website.

---

**Need help?** Check out our [documentation](https://docs.exacall.sa) or open an issue on GitHub.