@simpleapps-com/augur-api
Version:
TypeScript client library for Augur microservices API endpoints
371 lines (291 loc) • 10.9 kB
Markdown
# Authentication Guide
Complete guide to authentication patterns in the Augur API Client.
## Standard Authentication (Most Common)
The standard authentication pattern works for 95% of use cases.
### Dual Authentication System
Every API request requires two pieces of authentication:
1. **Site ID** (`x-site-id` header) - Identifies which site/tenant you're working with
2. **Bearer Token** (JWT) - Proves you're authorized to access that site
```typescript
const api = new AugurAPI({
siteId: 'your-site-id', // Always required
bearerToken: 'your-jwt-token', // Required except for health checks
});
```
### How to Get Your Credentials
**Site ID**: Usually provided by your system administrator or found in your project configuration.
**Bearer Token**: Obtained through your application's login process. This is typically a JWT token that looks like:
`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...`
### Health Checks (No Authentication Required)
Health checks are the only endpoints that don't require a bearer token:
```typescript
// Health checks work without bearer token
const api = new AugurAPI({
siteId: 'your-site-id',
// No bearerToken needed
});
const health = await api.joomla.getHealthCheck();
const pricingHealth = await api.pricing.getHealthCheck();
const vmiPing = await api.vmi.health.ping();
```
### Dynamic Authentication Updates
Update credentials at runtime when needed:
```typescript
// Update token after login/refresh
api.setAuthToken('new-jwt-token');
// Switch to different site
api.setSiteId('different-site-id');
// Example: Token refresh scenario
async function refreshTokenIfNeeded() {
try {
await api.joomla.users.list({ limit: 1 });
} catch (error) {
if (error instanceof AuthenticationError) {
const newToken = await refreshJWTToken();
api.setAuthToken(newToken);
}
}
}
```
### Environment Variables Setup
Store credentials securely in environment variables:
```bash
# .env file
AUGUR_SITE_ID=your-site-id
AUGUR_JWT_TOKEN=your-jwt-token
```
```typescript
const api = new AugurAPI({
siteId: process.env.AUGUR_SITE_ID!,
bearerToken: process.env.AUGUR_JWT_TOKEN!,
});
```
### Security Best Practices
1. **Environment Variables**: Never hardcode credentials
2. **Token Rotation**: Implement regular token rotation in production
3. **HTTPS Only**: All API requests use HTTPS in production
4. **Secure Storage**: Never store tokens in localStorage for web apps
5. **Minimal Permissions**: Use tokens with minimal required permissions
```typescript
// ✅ Good: Environment-based configuration
const api = new AugurAPI({
siteId: process.env.AUGUR_SITE_ID!,
bearerToken: getSecurelyStoredToken(), // From secure storage
});
// ❌ Bad: Hardcoded credentials
const api = new AugurAPI({
siteId: 'hardcoded-site-id',
bearerToken: 'hardcoded-token', // Never do this!
});
```
---
## Advanced: Cross-Site Authentication
**⚠️ Advanced Feature**: Only needed for multi-tenant applications where you need to authenticate users across different sites.
### When You Need Cross-Site Auth
Cross-site authentication is needed when:
- You're building a multi-tenant application
- You need to authenticate a user from one site against a different site
- You're managing multiple client sites from a central application
### Prerequisites
- Access to the special `augur_info` site
- Admin-level JWT token with `augur_info` privileges
- Target site ID where you want to authenticate the user
### Option 1: Streamlined Utility Function (Recommended)
```typescript
import { authenticateUserForSite } from '@simpleapps-com/augur-api';
const result = await authenticateUserForSite({
targetSiteId: 'tenant_site_1',
username: 'user@tenant.com',
password: 'user-password',
augurInfoToken: 'admin-jwt-token' // JWT with augur_info admin privileges
});
if (result.success) {
// Ready-to-use API client for the target site
const userData = await result.targetSiteAPI!.joomla.users.get(result.userId!);
console.log('User authenticated:', result.username);
console.log('User data:', userData);
} else {
console.error('Authentication failed:', result.error);
}
```
### Option 2: Reusable Authenticator
```typescript
import { createCrossSiteAuthenticator } from '@simpleapps-com/augur-api';
// Create reusable authenticator
const crossSiteAuth = createCrossSiteAuthenticator('admin-jwt-token');
// Use for multiple sites
const result1 = await crossSiteAuth('tenant_site_1', 'user1@tenant.com', 'pass1');
const result2 = await crossSiteAuth('tenant_site_2', 'user2@tenant.com', 'pass2');
if (result1.success) {
const tenantData = await result1.targetSiteAPI!.joomla.content.list({ limit: 10 });
}
```
### Option 3: Manual Approach
```typescript
// Manual approach using the API client directly
const crossSiteAPI = new AugurAPI({
siteId: 'augur_info', // Special site for cross-site operations
bearerToken: 'admin-jwt-token', // JWT token with augur_info admin privileges
});
// Authenticate user against a different site
const authResult = await crossSiteAPI.joomla.users.verifyPassword({
username: 'user@tenant.com',
password: 'user-password',
siteId: 'tenant_site_1' // Target site for authentication
});
if (authResult.data.isVerified) {
// Create API instance for the target site
const tenantAPI = new AugurAPI({
siteId: 'tenant_site_1',
bearerToken: authResult.data.token, // Token scoped to tenant site
});
const userData = await tenantAPI.joomla.users.get(authResult.data.id);
}
```
### Cross-Site Auth Flow Diagram
```
[Your App] → [augur_info API] → [Target Site] → [User Validated] → [Target Site API Client]
↓ ↓ ↓ ↓ ↓
Admin Token → Verify User → Check Password → Return User Token → Ready for API calls
```
### Common Cross-Site Patterns
#### Multi-Tenant Dashboard
```typescript
// Authenticate users across multiple tenant sites
const authenticator = createCrossSiteAuthenticator(adminToken);
const tenants = ['site1', 'site2', 'site3'];
const userCredentials = { username: 'user@example.com', password: 'pass' };
const results = await Promise.all(
tenants.map(siteId =>
authenticator(siteId, userCredentials.username, userCredentials.password)
)
);
// Process results for each tenant
results.forEach((result, index) => {
if (result.success) {
console.log(`${tenants[index]}: User authenticated as ${result.username}`);
} else {
console.log(`${tenants[index]}: Authentication failed - ${result.error}`);
}
});
```
#### Site Switching
```typescript
// Allow users to switch between sites they have access to
async function switchUserToSite(username: string, password: string, targetSiteId: string) {
const result = await authenticateUserForSite({
targetSiteId,
username,
password,
augurInfoToken: process.env.AUGUR_INFO_ADMIN_TOKEN!
});
if (result.success) {
// Store the new site's API client
global.userAPI = result.targetSiteAPI;
global.currentSiteId = targetSiteId;
return { success: true, siteId: targetSiteId };
}
return { success: false, error: result.error };
}
```
### Cross-Site Error Handling
```typescript
try {
const result = await authenticateUserForSite({
targetSiteId: 'tenant-site',
username: 'user@tenant.com',
password: 'user-pass',
augurInfoToken: adminToken
});
if (!result.success) {
switch (result.error) {
case 'Invalid username or password':
// Handle wrong username/password
break;
case 'Site not found':
// Handle invalid target site (from API error)
break;
case 'Authentication failed':
// Handle general auth failures or network issues
break;
default:
// Handle other errors
break;
}
}
} catch (error) {
console.error('Cross-site auth failed:', error.message);
}
```
### Security Considerations for Cross-Site Auth
1. **Admin Token Security**: The `augur_info` admin token is very powerful - store it extremely securely
2. **Audit Logging**: Log all cross-site authentication attempts for security monitoring
3. **Rate Limiting**: Implement rate limiting to prevent brute force attacks
4. **Token Scoping**: Returned tokens are scoped to the target site only
5. **Session Management**: Properly manage and clean up cross-site sessions
### When NOT to Use Cross-Site Auth
Don't use cross-site authentication if:
- You're working with a single site (use standard auth)
- You don't have admin-level access to `augur_info`
- You're building a simple application without multi-tenancy
- Security requirements prohibit cross-site token exchange
---
## Troubleshooting Authentication
### Common Issues
**"Authentication failed" (401 Error)**
- Check your `bearerToken` is correct and not expired
- Verify your `siteId` matches your environment
- For cross-site auth, ensure your admin token has `augur_info` privileges
**"Forbidden" (403 Error)**
- Your token is valid but doesn't have permission for this operation
- Check if you need admin-level permissions
- Verify the site ID matches the token's scope
**"Site not found" (Cross-site auth)**
- Verify the target site ID exists and is accessible
- Check that the target site is properly configured
- Ensure your admin token has access to the target site
**Token Expiration**
- Implement token refresh logic
- Monitor token expiration times
- Have fallback authentication flow
### Authentication Testing
```typescript
// Test standard authentication
async function testAuth() {
try {
const api = new AugurAPI({
siteId: 'your-site-id',
bearerToken: 'your-token'
});
// Simple test call
const health = await api.joomla.getHealthCheck();
console.log('✅ Authentication working:', health.data.status);
// Test authenticated call
const users = await api.joomla.users.list({ limit: 1 });
console.log('✅ Authenticated API calls working');
} catch (error) {
console.error('❌ Authentication failed:', error.message);
}
}
// Test cross-site authentication
async function testCrossSiteAuth() {
try {
const result = await authenticateUserForSite({
targetSiteId: 'test-site',
username: 'test-user',
password: 'test-pass',
augurInfoToken: 'admin-token'
});
if (result.success) {
console.log('✅ Cross-site auth working');
const testCall = await result.targetSiteAPI!.joomla.getHealthCheck();
console.log('✅ Target site API working:', testCall.data.status);
} else {
console.error('❌ Cross-site auth failed:', result.error);
}
} catch (error) {
console.error('❌ Cross-site auth error:', error.message);
}
}
```
For most applications, stick with standard authentication. Only implement cross-site authentication if you specifically need multi-tenant capabilities.