npm package: @pytak/auth
Description: Distributed token verification and refresh management package for Pytak microservices. This package provides JWT token verification, automatic key rotation, and token refresh management across all microservices.
Latest version: 0.1.0

# @pytak/auth

Distributed token verification and refresh management package for Pytak microservices. This package provides JWT token verification, automatic key rotation, and token refresh management across all microservices.

## Features

- JWT verification with RSA public/private key pairs
- Automatic key rotation and caching
- Token refresh management with configurable grace periods
- Express middleware for easy integration
- Redis-based token caching (using @pytak/database)
- Full TypeScript support
- Configurable retry and timeout settings

## Installation

```bash
pnpm add @pytak/auth @pytak/database express
```

Note: @pytak/database is a required peer dependency as it provides the Redis connection management.

## Quick Start

```typescript
import { createAuthMiddleware, validateConfig } from '@pytak/auth';
import express from 'express';

const app = express();

const config = validateConfig({
  auth: {
    baseUrl: 'http://auth-service:3000',
    timeout: 5000,
    retries: 3
  },
  token: {
    gracePeriod: 300,     // 5 minutes
    refreshWindow: 60     // 1 minute
  },
  keyRotationCheck: 300   // 5 minutes
});

// Add auth middleware to protected routes
app.use('/api', createAuthMiddleware(config));

// Protected route example
app.get('/api/profile', (req, res) => {
  // req.user contains verified token payload
  const { userId, roles } = req.user;
  res.json({ userId, roles });
});
```

## Configuration

### Auth Service Configuration
```typescript
interface AuthConfig {
  auth: {
    baseUrl: string;      // Auth service URL
    timeout: number;      // Request timeout in ms
    retries: number;      // Number of retries for failed requests
  };
  token: {
    gracePeriod: number;  // Grace period for expired tokens in seconds
    refreshWindow: number; // Window to trigger refresh before expiry in seconds
  };
  keyRotationCheck: number; // Interval to check for new public keys in seconds
}
```

### Environment Variables
The package uses @pytak/database for Redis configuration. Ensure these environment variables are set:
```env
DB_HOST=localhost      # Database host
DB_PORT=5432          # Database port
DB_USER=pytak         # Database user
DB_PASS=pytak         # Database password
DB_NAME=pytak         # Database name

REDIS_HOST=localhost  # Redis host
REDIS_PORT=6379      # Redis port
REDIS_PASSWORD=      # Redis password (optional)
```

## Detailed Usage

### Manual Token Verification

```typescript
import { TokenVerifier, validateConfig } from '@pytak/auth';

const verifier = new TokenVerifier(config);

try {
  const payload = await verifier.verify(token);
  console.log('Token verified:', payload);
} catch (error) {
  if (error.code === 'TOKEN_EXPIRED') {
    // Handle expired token
  } else if (error.code === 'INVALID_TOKEN') {
    // Handle invalid token
  }
}
```

### Token Refresh Management

```typescript
import { TokenVerifier } from '@pytak/auth';

const verifier = new TokenVerifier(config);

try {
  const newTokens = await verifier.refresh(refreshToken);
  // Store new tokens
  console.log('New tokens:', newTokens);
} catch (error) {
  if (error.code === 'REFRESH_TOKEN_EXPIRED') {
    // Handle expired refresh token
  }
}
```

### Custom Middleware Integration

```typescript
import { TokenVerifier } from '@pytak/auth';

function customAuthMiddleware(config) {
  const verifier = new TokenVerifier(config);
  
  return async (req, res, next) => {
    const token = req.headers['x-custom-token'];
    
    try {
      const payload = await verifier.verify(token);
      req.user = payload;
      next();
    } catch (error) {
      res.status(401).json({ error: error.message });
    }
  };
}
```

## Error Handling

The package exports `AuthError` class with the following error types:

```typescript
// Token expired
AuthError.TokenExpired(message?: string)

// Invalid token format or signature
AuthError.InvalidToken(message?: string)

// Auth service unavailable
AuthError.ServiceUnavailable(message?: string)

// Refresh token expired
AuthError.RefreshTokenExpired(message?: string)
```

Error response format:
```typescript
{
  error: string;      // Error code
  message: string;    // Error message
  details?: any;      // Additional error details
}
```

## TypeScript Support

### Token Payload Type
```typescript
interface TokenPayload {
  userId: string;
  roles: string[];
  sessionId: string;
  keyVersion: string;
  exp: number;
  iat: number;
}
```

### Express Request Extension
```typescript
// Automatically extends Express.Request
declare global {
  namespace Express {
    interface Request {
      user?: TokenPayload;
    }
  }
}
```

## Integration with Auth Service

This package is designed to work with Pytak's Auth Service. The Auth Service must:

1. Expose a public key endpoint:
```
GET /auth/keys
Response: {
  keys: Array<{
    kid: string;
    publicKey: string;
    expiresAt: number;
  }>
}
```

2. Expose a token refresh endpoint:
```
POST /auth/refresh
Body: { refreshToken: string }
Response: {
  accessToken: string;
  refreshToken: string;
  expiresIn: number;
}
```

## Performance Considerations

- Public keys are cached in Redis
- Token verification is performed locally using cached public keys
- Automatic background refresh for tokens nearing expiration
- Configurable retry strategy for failed requests
- Grace period for expired tokens to handle clock skew

## Security Considerations

- RSA public/private key pairs for token signing
- Automatic key rotation support
- Token blacklist support via Redis
- Configurable token expiry and refresh windows
- Secure error messages that don't leak sensitive information

## Contributing

Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.

## License

This package is part of the Pytak platform and is licensed under the MIT License - see the LICENSE file for details.