UNPKG

logto-auth-node-sdk

Version:

A comprehensive Logto authentication client library with circuit breaker pattern support

865 lines (675 loc) â€ĸ 21.2 kB
# Logto Auth Node.js SDK A comprehensive TypeScript/JavaScript SDK for [Logto](https://logto.io/) - Modern auth infrastructure for developers. This SDK provides seamless integration with both Logto Cloud and self-hosted Logto OSS instances, featuring built-in resilience patterns and comprehensive API coverage. ## About Logto [Logto](https://logto.io/) is a modern authentication and user management solution designed for developers. It offers: - 🔐 **Complete Authentication Solution** - Sign-in, sign-up, profile management, and more - 🌐 **Multi-tenant Support** - Organizations and role-based access control - 🎨 **Customizable UI** - Pre-built sign-in experience with full customization options - 🔌 **Multiple Deployment Options** - Cloud-hosted or self-hosted OSS - 📱 **Cross-platform** - Web, mobile, and API applications - đŸ›Ąī¸ **Enterprise Ready** - SSO, SCIM, audit logs, and compliance features ## Features - 🚀 **Full Logto API Coverage** - Users, Organizations, Authentication, and Management APIs - 🔄 **Built-in Circuit Breaker** - Enhanced resilience and fault tolerance - đŸ›Ąī¸ **Automatic Token Management** - Token refresh and secure storage - 🔁 **Smart Retry Logic** - Exponential backoff with jitter - đŸ’Ē **TypeScript First** - Full type definitions and IntelliSense support - 📊 **Circuit Breaker Monitoring** - Real-time status and management - ⚡ **Lightweight** - Minimal dependencies, maximum performance - đŸŒŠī¸ **Multi-Environment** - Supports both Logto Cloud and self-hosted instances ## Installation ```bash npm install logto-auth-node-sdk ``` ```bash yarn add logto-auth-node-sdk ``` ```bash pnpm add logto-auth-node-sdk ``` ## Quick Start ### Logto Cloud Setup ```typescript import { LogtoClient } from 'logto-auth-node-sdk'; const client = new LogtoClient({ authEndpoint: 'https://your-tenant.logto.app', appId: 'your-app-id', appSecret: 'your-app-secret', resourceEndpoint: 'https://your-tenant.logto.app/api', }); ``` ### Self-hosted Logto OSS Setup ```typescript import { LogtoClient } from 'logto-auth-node-sdk'; const client = new LogtoClient({ authEndpoint: 'https://your-logto-instance.com', appId: 'your-app-id', appSecret: 'your-app-secret', resourceEndpoint: 'https://your-logto-instance.com/api', }); ``` ### Basic Usage ```typescript // Create a user const user = await client.createUser({ username: 'john_doe', primaryEmail: 'john@example.com', name: 'John Doe', password: 'secure-password' }); // Create an organization const org = await client.createOrganization({ name: 'Acme Corporation', description: 'A sample organization' }); // Add user to organization await client.addUsersToOrganization(org.id, { userIds: [user.id] }); ``` ## Getting Started with Logto ### Logto Cloud 1. Sign up at [Logto Cloud](https://cloud.logto.io) 2. Create a new application 3. Get your App ID and App Secret from the application settings 4. Use your tenant endpoint: `https://{your-tenant}.logto.app` ### Self-hosted Logto OSS For self-hosted deployment, follow the [Logto OSS deployment guide](https://docs.logto.io/logto-oss/deployment-and-configuration): #### Docker Deployment ```bash # Clone the repository git clone https://github.com/logto-io/logto.git cd logto # Start with Docker Compose docker compose up -d ``` #### Manual Deployment ```bash # Install dependencies npm install # Build the project npm run build # Set environment variables export DB_URL=postgresql://username:password@localhost:5432/logto export PORT=3001 # Start the application npm start ``` ## Configuration ### LogtoConfig Interface ```typescript interface LogtoConfig { /** Your Logto instance URL (Cloud: https://{tenant}.logto.app, OSS: your domain) */ authEndpoint: string; /** Application ID from Logto console */ appId: string; /** Application secret from Logto console */ appSecret: string; /** Resource endpoint URL */ resourceEndpoint: string; /** Request timeout in milliseconds (default: 10000) */ timeout?: number; /** Circuit breaker configuration */ circuitBreakerOptions?: CircuitBreakerOptions; /** Custom headers for requests */ headers?: Record<string, string>; /** Enable debug logging */ debug?: boolean; } ``` ### Circuit Breaker Options ```typescript interface CircuitBreakerOptions { /** Number of failures before opening circuit (default: 5) */ failureThreshold: number; /** Time in ms before attempting to close circuit (default: 30000) */ resetTimeout: number; /** Number of retries in half-open state (default: 2) */ halfOpenRetries: number; /** Enable circuit breaker monitoring */ monitoring?: boolean; } ``` ## API Reference ### User Management #### Create User ```typescript interface CreateUserRequest { username?: string; primaryEmail?: string; primaryPhone?: string; name?: string; avatar?: string; customData?: Record<string, any>; profile?: UserProfile; password?: string; } const user = await client.createUser({ username: 'john_doe', primaryEmail: 'john@example.com', name: 'John Doe', password: 'secure-password', customData: { department: 'Engineering', role: 'Developer' } }); ``` #### Get User ```typescript const user = await client.getUser(userId); ``` #### Update User ```typescript const updatedUser = await client.updateUser(userId, { name: 'John Smith', avatar: 'https://example.com/avatar.jpg', customData: { role: 'Senior Developer' } }); ``` #### User Password Management ```typescript // Update password await client.updateUserPassword(userId, { password: 'new-secure-password' }); // Verify password const isValid = await client.verifyUserPassword(userId, { password: 'password-to-check' }); ``` #### User Status Management ```typescript // Suspend user await client.updateUserSuspensionStatus(userId, { isSuspended: true }); // Reactivate user await client.updateUserSuspensionStatus(userId, { isSuspended: false }); ``` #### Delete User ```typescript await client.deleteUser(userId); ``` ### Organization Management #### Create Organization ```typescript const organization = await client.createOrganization({ name: 'Acme Corporation', description: 'Leading technology company', customData: { tier: 'enterprise', industry: 'technology' } }); ``` #### Get Organization ```typescript const org = await client.getOrganization(organizationId); ``` #### Update Organization ```typescript const updatedOrg = await client.updateOrganization(organizationId, { name: 'Acme Corp Ltd', description: 'Updated description', customData: { tier: 'premium' } }); ``` #### Organization User Management ```typescript // Add users to organization await client.addUsersToOrganization(organizationId, { userIds: ['user1', 'user2', 'user3'] }); // Remove user from organization await client.removeUserFromOrganization(organizationId, userId); ``` #### Delete Organization ```typescript await client.deleteOrganization(organizationId); ``` ## Advanced Features ### Circuit Breaker Management Monitor and manage circuit breakers for enhanced reliability: ```typescript // Get circuit breaker status const status = client.getCircuitBreakerStatus('logto-users'); console.log(`Circuit: ${status.name}, State: ${status.state}`); // Get all circuit breakers const allCircuits = client.getAllCircuitBreakers(); for (const [name, circuit] of allCircuits) { console.log(`${name}: ${circuit.state} (Failures: ${circuit.failureCount})`); } // Reset circuit breaker client.resetCircuitBreaker('logto-users'); ``` ### Error Handling ```typescript import { LogtoError, CircuitBreakerError } from 'logto-auth-node-sdk'; try { const user = await client.getUser('invalid-user-id'); } catch (error) { if (error instanceof CircuitBreakerError) { console.log('Service temporarily unavailable, please try again later'); } else if (error instanceof LogtoError) { console.log(`Logto API error: ${error.message} (Status: ${error.status})`); } else { console.log('Unexpected error:', error.message); } } ``` ### Token Management The SDK automatically handles token lifecycle: ```typescript // Tokens are automatically managed const user = await client.getUser(userId); // Token obtained if needed const org = await client.getOrganization(orgId); // Reuses existing token // Manual token refresh (rarely needed) await client.refreshToken(); ``` ## Environment Configuration ### Environment Variables ```bash # Logto Configuration LOGTO_AUTH_ENDPOINT=https://your-tenant.logto.app LOGTO_APP_ID=your-app-id LOGTO_APP_SECRET=your-app-secret LOGTO_RESOURCE_ENDPOINT=https://your-tenant.logto.app/api # Optional Configuration LOGTO_TIMEOUT=10000 LOGTO_DEBUG=false ``` ### Configuration from Environment ```typescript import { LogtoClient } from 'logto-auth-node-sdk'; const client = new LogtoClient({ authEndpoint: process.env.LOGTO_AUTH_ENDPOINT!, appId: process.env.LOGTO_APP_ID!, appSecret: process.env.LOGTO_APP_SECRET!, resourceEndpoint: process.env.LOGTO_RESOURCE_ENDPOINT!, timeout: parseInt(process.env.LOGTO_TIMEOUT || '10000'), debug: process.env.LOGTO_DEBUG === 'true' }); ``` ## Examples ### Complete User Management Example ```typescript import { LogtoClient } from 'logto-auth-node-sdk'; const client = new LogtoClient({ authEndpoint: process.env.LOGTO_AUTH_ENDPOINT!, appId: process.env.LOGTO_APP_ID!, appSecret: process.env.LOGTO_APP_SECRET!, resourceEndpoint: process.env.LOGTO_RESOURCE_ENDPOINT!, }); async function userManagementExample() { try { // Create user const user = await client.createUser({ username: 'alice_smith', primaryEmail: 'alice@company.com', name: 'Alice Smith', password: 'SecurePassword123!', customData: { role: 'developer', department: 'engineering', startDate: new Date().toISOString() } }); console.log('Created user:', user.id); // Verify password const isValidPassword = await client.verifyUserPassword(user.id, { password: 'SecurePassword123!' }); console.log('Password valid:', isValidPassword); // Update user profile const updatedUser = await client.updateUser(user.id, { name: 'Alice Johnson', // Married name customData: { ...user.customData, role: 'senior-developer' // Promotion } }); console.log('Updated user:', updatedUser.name); // Temporarily suspend user await client.updateUserSuspensionStatus(user.id, { isSuspended: true }); console.log('User suspended'); // Reactivate user await client.updateUserSuspensionStatus(user.id, { isSuspended: false }); console.log('User reactivated'); } catch (error) { console.error('User management error:', error); } } ``` ### Organization and Team Management ```typescript async function organizationExample() { try { // Create organization const organization = await client.createOrganization({ name: 'Tech Innovations Inc', description: 'A forward-thinking technology company', customData: { tier: 'enterprise', industry: 'software', founded: '2024', size: 'medium' } }); console.log('Created organization:', organization.id); // Create team members const teamMembers = await Promise.all([ client.createUser({ username: 'john_lead', primaryEmail: 'john@techinnovations.com', name: 'John Smith', password: 'LeaderPass123!', customData: { role: 'team-lead' } }), client.createUser({ username: 'sarah_dev', primaryEmail: 'sarah@techinnovations.com', name: 'Sarah Connor', password: 'DevPass123!', customData: { role: 'developer' } }), client.createUser({ username: 'mike_designer', primaryEmail: 'mike@techinnovations.com', name: 'Mike Johnson', password: 'DesignPass123!', customData: { role: 'designer' } }) ]); console.log(`Created ${teamMembers.length} team members`); // Add all users to organization await client.addUsersToOrganization(organization.id, { userIds: teamMembers.map(user => user.id) }); console.log('Added users to organization'); // Update organization details const updatedOrg = await client.updateOrganization(organization.id, { description: 'A rapidly growing technology company specializing in innovative solutions', customData: { ...organization.customData, size: 'large', // Company grew employees: teamMembers.length } }); console.log('Updated organization:', updatedOrg.name); // Remove one user (they left the company) await client.removeUserFromOrganization(organization.id, teamMembers[2].id); console.log('Removed user from organization'); } catch (error) { console.error('Organization management error:', error); } } ``` ### Error Handling and Resilience ```typescript import { LogtoClient, CircuitBreakerError, LogtoError } from 'logto-auth-node-sdk'; const client = new LogtoClient({ authEndpoint: process.env.LOGTO_AUTH_ENDPOINT!, appId: process.env.LOGTO_APP_ID!, appSecret: process.env.LOGTO_APP_SECRET!, resourceEndpoint: process.env.LOGTO_RESOURCE_ENDPOINT!, circuitBreakerOptions: { failureThreshold: 3, resetTimeout: 60000, halfOpenRetries: 1, monitoring: true } }); async function resilientOperations() { // Monitor circuit breaker health setInterval(() => { const circuits = client.getAllCircuitBreakers(); for (const [name, circuit] of circuits) { if (circuit.state !== 'CLOSED') { console.warn(`Circuit breaker ${name} is ${circuit.state}`); } } }, 30000); // Perform operations with error handling async function safeUserOperation(userId: string) { try { const user = await client.getUser(userId); return { success: true, data: user }; } catch (error) { if (error instanceof CircuitBreakerError) { console.log('Service temporarily unavailable - circuit breaker open'); return { success: false, reason: 'service_unavailable', retry: true }; } else if (error instanceof LogtoError) { console.log(`API error: ${error.message} (${error.status})`); return { success: false, reason: 'api_error', retry: error.status >= 500 }; } else { console.error('Unexpected error:', error); return { success: false, reason: 'unknown_error', retry: false }; } } } // Use the safe operation const result = await safeUserOperation('user-123'); if (!result.success && result.retry) { // Implement your retry logic here setTimeout(() => safeUserOperation('user-123'), 5000); } } ``` ## TypeScript Support The SDK is built with TypeScript and provides comprehensive type definitions: ```typescript import { LogtoClient, LogtoConfig, UserResponse, OrganizationResponse, CreateUserRequest, CreateOrganizationRequest, CircuitBreakerStatus, CircuitState } from 'logto-auth-node-sdk'; // Fully typed configuration const config: LogtoConfig = { authEndpoint: 'https://your-tenant.logto.app', appId: 'app-id', appSecret: 'app-secret', resourceEndpoint: 'https://your-tenant.logto.app/api' }; // Typed client instance const client: LogtoClient = new LogtoClient(config); // Typed responses const user: UserResponse = await client.getUser('user-id'); const org: OrganizationResponse = await client.getOrganization('org-id'); // Typed request objects const createUserRequest: CreateUserRequest = { username: 'john_doe', primaryEmail: 'john@example.com', name: 'John Doe' }; ``` ## Performance and Best Practices ### Connection Pooling ```typescript // Create a single client instance and reuse it const client = new LogtoClient(config); // Don't create new clients for each request // ❌ Bad async function badExample() { const client = new LogtoClient(config); return client.getUser('user-id'); } // ✅ Good async function goodExample(client: LogtoClient) { return client.getUser('user-id'); } ``` ### Batch Operations ```typescript // Process multiple users efficiently async function batchUserCreation(userData: CreateUserRequest[]) { const users = await Promise.all( userData.map(data => client.createUser(data)) ); return users; } // Add multiple users to organization in one call await client.addUsersToOrganization(orgId, { userIds: users.map(u => u.id) }); ``` ### Circuit Breaker Monitoring ```typescript // Set up monitoring function setupMonitoring(client: LogtoClient) { setInterval(() => { const circuits = client.getAllCircuitBreakers(); for (const [name, circuit] of circuits) { if (circuit.failureCount > 0) { console.log(`Circuit ${name}: ${circuit.failureCount} failures`); } if (circuit.state === 'OPEN') { // Alert your monitoring system console.error(`Circuit breaker ${name} is OPEN - service degraded`); } } }, 60000); // Check every minute } ``` ## Deployment Considerations ### Logto Cloud vs Self-hosted | Feature | Logto Cloud | Self-hosted OSS | |---------|-------------|-----------------| | **Setup** | Instant signup | Manual deployment | | **Maintenance** | Fully managed | Self-managed | | **Scaling** | Auto-scaling | Manual scaling | | **Updates** | Automatic | Manual updates | | **Customization** | Limited | Full control | | **Cost** | Usage-based pricing | Infrastructure costs | | **Support** | Official support | Community support | ### Environment-specific Configuration ```typescript // Development const devClient = new LogtoClient({ authEndpoint: 'http://localhost:3001', appId: 'dev-app-id', appSecret: 'dev-app-secret', resourceEndpoint: 'http://localhost:3001/api', debug: true }); // Production const prodClient = new LogtoClient({ authEndpoint: 'https://your-tenant.logto.app', appId: process.env.LOGTO_APP_ID!, appSecret: process.env.LOGTO_APP_SECRET!, resourceEndpoint: 'https://your-tenant.logto.app/api', timeout: 5000, circuitBreakerOptions: { failureThreshold: 5, resetTimeout: 30000, halfOpenRetries: 2 } }); ``` ## Troubleshooting ### Common Issues 1. **Authentication Errors** ```typescript // Check your app credentials console.log('App ID:', process.env.LOGTO_APP_ID); console.log('Endpoint:', process.env.LOGTO_AUTH_ENDPOINT); // Verify endpoint format // ✅ Correct: https://your-tenant.logto.app // ❌ Wrong: https://your-tenant.logto.app/ ``` 2. **Circuit Breaker Issues** ```typescript // Reset circuit breaker client.resetCircuitBreaker('logto-users'); // Check circuit status const status = client.getCircuitBreakerStatus('logto-users'); console.log('Circuit state:', status.state); ``` 3. **Network Timeouts** ```typescript // Increase timeout for slow networks const client = new LogtoClient({ ...config, timeout: 30000 // 30 seconds }); ``` ### Debug Mode ```typescript const client = new LogtoClient({ ...config, debug: true // Enables detailed logging }); ``` ## Migration Guide ### From v0.x to v1.x ```typescript // v1.x (New) import { LogtoClient } from 'logto-auth-node-sdk'; ``` ## Contributing We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details. ### Development Setup ```bash # Clone the repository git clone https://github.com/logto-io/logto-auth-node-sdk.git cd logto-auth-node-sdk # Install dependencies npm install # Run tests npm test # Build the project npm run build # Run linting npm run lint ``` ### Running Tests ```bash # Unit tests npm run test:unit # Integration tests (requires Logto instance) npm run test:integration # Coverage report npm run test:coverage ``` ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## Support and Resources ### Documentation - [Logto Documentation](https://docs.logto.io/) - [Logto OSS Deployment Guide](https://docs.logto.io/logto-oss/deployment-and-configuration) - [API Reference](https://docs.logto.io/api-reference) ### Community - [GitHub Issues](https://github.com/logto-io/logto-auth-node-sdk/issues) - [Logto Discord](https://discord.gg/logto) - [Logto Community Forum](https://github.com/logto-io/logto/discussions) ### Commercial Support - [Logto Cloud](https://cloud.logto.io/) - Managed Logto service - [Enterprise Support](https://logto.io/contact) - Priority support and consulting ## Changelog ### 1.0.0 - 🎉 **Initial Release** - ✨ Complete Logto API coverage - 🔄 Circuit breaker implementation - đŸ›Ąī¸ Automatic token management - đŸ’Ē Full TypeScript support - 📊 Circuit breaker monitoring - đŸŒŠī¸ Multi-environment support (Cloud + OSS) - 📚 Comprehensive documentation --- **Made with â¤ī¸ for the Logto community** *Logto Auth Node.js SDK - Bringing modern authentication to your Node.js applications*