logto-auth-node-sdk
Version:
A comprehensive Logto authentication client library with circuit breaker pattern support
865 lines (675 loc) âĸ 21.2 kB
Markdown
# 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*