# Token Persistence Implementation
This document describes the token persistence functionality implemented in the AMP Rust client.
## Overview
The token persistence feature automatically saves authentication tokens to disk and reloads them on subsequent runs, eliminating the need to re-authenticate for every API call. This improves performance and reduces API load while maintaining security.
## Implementation Details
### Core Components
1. **TokenData Structure** (`src/model.rs`)
- Stores the JWT token securely using `Secret<String>`
- Tracks expiration time and acquisition timestamp
- Implements serialization/deserialization for disk storage
2. **TokenManager** (`src/client.rs`)
- Handles all token lifecycle operations
- Implements thread-safe token management
- Provides automatic refresh and persistence logic
3. **Token File** (`token.json`)
- JSON file storing serialized token data
- Created automatically when persistence is enabled
- Removed when tokens are cleared or expired
### Key Features
#### Automatic Persistence
- Tokens are automatically saved to `token.json` when obtained or refreshed
- Tokens are automatically loaded from disk on client initialization
- Expired tokens are automatically removed from disk
#### Proactive Refresh
- Tokens are automatically refreshed 5 minutes before expiry
- Fallback to obtaining new tokens if refresh fails
- Thread-safe operations prevent race conditions
#### Security
- Tokens are stored using the `secrecy` crate for memory safety
- Sensitive data is properly zeroized when dropped
- File permissions should be restricted in production environments
#### Configuration
Token persistence is enabled when:
- `AMP_TESTS=live` (for live API testing)
- `AMP_TOKEN_PERSISTENCE=true` is set
- **NOT** in mock test environments (to prevent test pollution)
**Mock Test Detection**: The system automatically detects mock test environments by checking for:
- Mock credentials (`AMP_USERNAME=mock_user`, `AMP_PASSWORD=mock_pass`)
- Localhost/mock server URLs in `AMP_API_BASE_URL`
- When detected, persistence is disabled regardless of other settings
### Token File Format
```json
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"expires_at": "2024-01-02T12:00:00Z",
"obtained_at": "2024-01-01T12:00:00Z"
}
```
### API Methods
#### Core Token Management
- `get_token()` - Gets a valid token with automatic management
- `obtain_token()` - Forces obtaining a new token
- `refresh_token()` - Forces refreshing the current token
- `force_refresh()` - Bypasses normal refresh logic
#### Token Information
- `get_token_info()` - Returns detailed token information
- `clear_token()` - Removes token from memory and disk
### Thread Safety
The implementation uses several mechanisms to ensure thread safety:
1. **Semaphore-based Synchronization**
- Only one token operation can occur at a time
- Prevents race conditions during refresh/obtain operations
2. **Atomic Token Updates**
- Token data is updated atomically within critical sections
- Double-checking pattern prevents unnecessary operations
3. **Arc<Mutex<>> for Shared State**
- Token data is wrapped in Arc<Mutex<>> for safe sharing
- Minimal lock holding time for optimal performance
### Error Handling
The implementation includes comprehensive error handling:
- **TokenError Enum**: Specific error types for token operations
- **Retry Logic**: Exponential backoff for failed operations
- **Graceful Degradation**: Fallback to obtaining new tokens on refresh failure
- **Rate Limiting**: Proper handling of API rate limits
### Usage Examples
#### Basic Usage
```rust
use amp_rs::ApiClient;
let client = ApiClient::new()?;
let token = client.get_token().await?; // Automatically handles persistence
```
#### Token Information
```rust
if let Some(info) = client.get_token_info().await? {
println!("Token expires in: {:?}", info.expires_in);
println!("Token is expired: {}", info.is_expired);
}
```
#### Manual Token Management
```rust
// Force refresh
let new_token = client.force_refresh().await?;
// Clear token
client.clear_token().await?;
```
### Testing
The implementation includes comprehensive tests:
1. **Unit Tests**: Token data serialization/deserialization
2. **Integration Tests**: Token persistence lifecycle
3. **Environment Tests**: Configuration detection
4. **Example Programs**: Demonstration of functionality
Run tests with:
```bash
cargo test --test token_persistence
```
### Security Considerations
1. **File Permissions**: The `token.json` file should have restricted permissions (600) in production
2. **Token Rotation**: Tokens are automatically refreshed before expiry
3. **Memory Safety**: Sensitive data is properly zeroized using the `secrecy` crate
4. **Cleanup**: Expired tokens are automatically removed from disk
### Performance Benefits
1. **Reduced API Calls**: Tokens are reused across application runs
2. **Faster Startup**: No need to authenticate on every startup
3. **Proactive Refresh**: Tokens are refreshed before expiry to avoid interruptions
4. **Thread Safety**: Efficient synchronization minimizes blocking
### Configuration Options
Environment variables for token management:
```bash
# Enable token persistence
export AMP_TOKEN_PERSISTENCE=true
# Enable for live testing
export AMP_TESTS=live
# Retry configuration
export API_RETRY_MAX_ATTEMPTS=3
export API_RETRY_BASE_DELAY_MS=1000
export API_RETRY_MAX_DELAY_MS=30000
export API_REQUEST_TIMEOUT_SECONDS=10
```
## Implementation Status
✅ **COMPLETED** - The token persistence implementation is now fully functional and provides a robust, secure, and efficient solution for managing authentication tokens in the AMP Rust client.
### What's Working
1. **Automatic Token Loading**: Tokens are automatically loaded from `token.json` on client initialization
2. **Automatic Token Saving**: Tokens are automatically saved to disk when obtained or refreshed
3. **Proactive Refresh**: Tokens are refreshed 5 minutes before expiry
4. **Thread Safety**: All operations are thread-safe with proper synchronization
5. **Environment Detection**: Persistence is automatically enabled based on environment variables
6. **Error Handling**: Comprehensive error handling with graceful degradation
7. **Security**: Tokens are stored securely using the `secrecy` crate
8. **Cleanup**: Expired tokens are automatically removed from disk
### Key Features Implemented
- ✅ `TokenManager::load_token_from_disk()` - Loads and validates tokens from disk
- ✅ `TokenManager::save_token_to_disk()` - Saves tokens to disk with proper serialization
- ✅ `TokenManager::remove_token_from_disk()` - Removes token files from disk
- ✅ `TokenManager::should_persist_tokens()` - Environment-based persistence detection
- ✅ `TokenManager::is_mock_test_environment()` - Mock test environment detection
- ✅ `TokenManager::force_cleanup_token_files()` - Force cleanup for testing
- ✅ Automatic token loading during client initialization
- ✅ Automatic token saving during obtain/refresh operations
- ✅ Automatic token cleanup during clear operations
- ✅ Async constructor support for `ApiClient::new()` and `ApiClient::with_base_url()`
- ✅ Mock test pollution prevention
### Testing
All tests are passing:
- ✅ Token persistence lifecycle tests
- ✅ Token serialization/deserialization tests
- ✅ Environment detection tests
- ✅ All 31 mock API tests
- ✅ Token file management tests
## Conclusion
The token persistence implementation eliminates the need for manual token management while maintaining security best practices and providing excellent performance characteristics. The implementation is production-ready and fully integrated into the AMP Rust client.