@omx-sdk/core
Version:
Core module for OMX SDK with authentication and shared utilities
400 lines (312 loc) • 10.4 kB
Markdown
Core module for OMX SDK providing authentication, configuration, and shared utilities across all OMX services.
- 🔐 **JWT Token Management** - Automatic fetching and caching of JWT tokens from Supabase Edge Function
- 🔄 **Automatic Token Refresh** - Handles token expiration and renewal seamlessly
- 🚀 **Authenticated API Requests** - Built-in HTTP client with automatic token injection
- 🛡️ **Comprehensive Error Handling** - Graceful handling of auth errors, network issues, and rate limiting
- ⚡ **Caching & Performance** - Intelligent token caching with configurable TTL
- 🔧 **TypeScript Support** - Full type safety and IntelliSense support
- 🌐 **Retry Logic** - Exponential backoff and configurable retry strategies
- 🔒 **Secure by Design** - No sensitive Supabase credentials exposed to client
```bash
npm install @omx-sdk/core
pnpm add @omx-sdk/core
yarn add @omx-sdk/core
```
Create a Supabase Edge Function at `/functions/create-jwt-token/index.ts`:
```typescript
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts';
serve(async (req) => {
try {
const { clientId, secretKey } = await req.json();
// Validate credentials against your database
const { data: business, error } = await supabaseClient
.from('businesses')
.select('*')
.eq('client_id', clientId)
.eq('secret_key', secretKey)
.eq('is_active', true)
.single();
if (error || !business) {
return new Response(
JSON.stringify({ error: 'Invalid client credentials' }),
{ status: 401, headers: { 'Content-Type': 'application/json' } }
);
}
// Generate JWT token
const payload = {
sub: business.id,
business_id: business.id,
client_id: business.client_id,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour
iss: 'omx-auth',
scope: business.permissions,
};
const token = await jwt.sign(payload, Deno.env.get('JWT_SECRET'));
return new Response(
JSON.stringify({
token: token,
token_type: 'Bearer',
expires_in: 3600,
}),
{ headers: { 'Content-Type': 'application/json' } }
);
} catch (error) {
return new Response(JSON.stringify({ error: 'Internal server error' }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
});
```
```typescript
import { CoreAuth, AuthConfig } from '@omx-sdk/core';
// Configure authentication
const config: AuthConfig = {
clientId: 'your-business-client-id',
secretKey: 'your-business-secret-key',
supabaseFnUrl:
'https://your-project.supabase.co/functions/v1/create-jwt-token', // Optional: uses default if not provided
tokenCacheTtl: 50 * 60 * 1000, // Cache for 50 minutes (optional)
};
// Initialize authentication
const auth = new CoreAuth(config);
// Get JWT token (automatically cached)
const token = await auth.getToken();
// Make authenticated API requests
const response = await auth.makeAuthenticatedRequest(
'https://api.yourdomain.com/data',
{
method: 'GET',
}
);
if (response.success) {
console.log('Data:', response.data);
} else {
console.error('Error:', response.error);
}
```
```typescript
new CoreAuth(config: AuthConfig)
```
#### Methods
##### getToken(forceRefresh?: boolean): Promise<string>
Gets a valid JWT token, automatically handling caching and refresh.
```typescript
// Get cached token (if valid)
const token = await auth.getToken();
// Force refresh token
const freshToken = await auth.getToken(true);
```
Makes an authenticated HTTP request with automatic token handling.
```typescript
const response = await auth.makeAuthenticatedRequest('/api/users', {
method: 'POST',
body: { name: 'John Doe', email: 'john@example.com' },
timeout: 10000,
retries: 3,
});
```
Returns information about the current cached token.
```typescript
const info = auth.getTokenInfo();
console.log('Token valid:', info.isValid);
console.log('Expires at:', new Date(info.expiresAt));
```
Clears the cached token (useful for logout).
```typescript
auth.clearToken();
```
Updates the configuration and clears cached tokens.
```typescript
auth.updateConfig({
clientId: 'new-client-id',
secretKey: 'new-secret-key',
});
```
Cleans up resources and clears cache.
```typescript
auth.dispose();
```
```typescript
interface AuthConfig {
clientId: string; // Business client ID
secretKey: string; // Business secret key
supabaseFnUrl?: string; // Supabase Edge Function URL (optional - uses default production URL)
tokenCacheTtl?: number; // Token cache TTL in ms (default: 55 minutes)
maxRetries?: number; // Max retry attempts (default: 3)
retryDelay?: number; // Retry delay in ms (default: 1000)
}
```
```typescript
interface ApiRequestOptions {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
headers?: Record<string, string>;
body?: any;
timeout?: number; // Request timeout in ms
retries?: number; // Override default retry count
}
```
The module provides comprehensive error handling with specific error types:
```typescript
import {
InvalidCredentialsError,
TokenExpiredError,
NetworkError,
RateLimitError,
ConfigurationError,
} from '@omx-sdk/core';
try {
const response = await auth.makeAuthenticatedRequest('/api/data');
} catch (error) {
if (error instanceof InvalidCredentialsError) {
console.error('Invalid credentials:', error.message);
} else if (error instanceof TokenExpiredError) {
console.error('Token expired:', error.message);
} else if (error instanceof NetworkError) {
console.error('Network error:', error.message);
} else if (error instanceof RateLimitError) {
console.error('Rate limited. Retry after:', error.details?.retryAfter);
}
}
```
```typescript
import { CoreAuth } from '@omx-sdk/core';
const auth = new CoreAuth({
clientId: 'business-123',
secretKey: 'secret-456',
supabaseFnUrl:
'https://your-project.supabase.co/functions/v1/create-jwt-token', // Optional
});
// Get token
const token = await auth.getToken();
// Check token status
const tokenInfo = auth.getTokenInfo();
console.log('Token expires:', new Date(tokenInfo.expiresAt));
```
```typescript
// GET request
const users = await auth.makeAuthenticatedRequest('/api/users');
// POST request with data
const newUser = await auth.makeAuthenticatedRequest('/api/users', {
method: 'POST',
body: {
name: 'John Doe',
email: 'john@example.com',
},
});
// PUT request with custom headers
const updated = await auth.makeAuthenticatedRequest('/api/users/123', {
method: 'PUT',
headers: {
'X-Custom-Header': 'value',
},
body: { name: 'Jane Doe' },
});
```
```typescript
try {
const response = await auth.makeAuthenticatedRequest('/api/protected');
if (response.success) {
console.log('Success:', response.data);
} else {
console.error('API Error:', response.error);
}
} catch (error) {
if (error instanceof NetworkError) {
console.error('Network issue - retrying...');
// Implement retry logic
} else if (error instanceof RateLimitError) {
const waitTime = error.details?.retryAfter || 60000;
console.log(`Rate limited - waiting ${waitTime}ms`);
// Wait and retry
}
}
```
```typescript
const response = await auth.makeAuthenticatedRequest('/api/critical-data', {
method: 'POST',
body: criticalData,
timeout: 30000, // 30 second timeout
retries: 5, // 5 retry attempts
});
```
```typescript
// Monitor token status
setInterval(() => {
const tokenInfo = auth.getTokenInfo();
if (!tokenInfo.isValid) {
console.log('Token expired, will refresh on next request');
}
}, 60000); // Check every minute
```
```typescript
const businessAuth = new Map();
// Configure multiple businesses
const businesses = [
{ id: 'business1', clientId: 'client1', secretKey: 'secret1' },
{ id: 'business2', clientId: 'client2', secretKey: 'secret2' },
];
businesses.forEach((business) => {
businessAuth.set(
business.id,
new CoreAuth({
clientId: business.clientId,
secretKey: business.secretKey,
supabaseUrl: 'https://project.supabase.co',
supabaseAnonKey: 'anon-key',
})
);
});
// Use specific business auth
const auth = businessAuth.get('business1');
const data = await auth.makeAuthenticatedRequest('/api/business-data');
```
1. **Edge Function Security**: Use Supabase Edge Functions to keep sensitive database credentials secure
2. **Token Caching**: Use appropriate cache TTL (default 55 minutes recommended)
3. **Error Handling**: Always handle specific error types appropriately
4. **Resource Cleanup**: Call `dispose()` when done to clean up resources
5. **Environment Configuration**: Use different Edge Function URLs for dev/staging/production
6. **Rate Limiting**: Implement proper backoff strategies for rate-limited APIs
7. **Monitoring**: Log authentication failures and token refresh events
## Security Features
- 🔒 **No Exposed Credentials**: Supabase URL and keys are not exposed to client
- 🛡️ **Edge Function Isolation**: Authentication logic runs in secure Supabase environment
- 🔐 **JWT Token Security**: Short-lived tokens with automatic refresh
- 🚫 **No Client-Side Secrets**: All sensitive operations happen server-side
## Requirements
- Node.js 16+ or modern browser environment
- Supabase project with RPC function configured
- TypeScript 4.5+ (for TypeScript projects)
## License
MIT
## Support
For issues and questions, please visit our [GitHub repository](https://github.com/your-org/omx-sdk).