BREAKING: Removes Node.js/Bitwarden CLI dependency entirely. ## What changed - New native HTTP client talks directly to Vaultwarden API - Bitwarden-compatible crypto in pure Go: - AES-256-CBC + HMAC-SHA256 decryption - PBKDF2-SHA256 and Argon2id key derivation - HKDF key stretching (with legacy fallback) - Auto token refresh (never expires) - Background vault sync (configurable interval, default 5min) - Docker image: ~233MB → ~15MB (pure Alpine, no Node.js) ## New env vars - VAULTWARDEN_EMAIL (replaces CLIENT_ID/CLIENT_SECRET) - SYNC_INTERVAL (default: 5m) ## Removed - internal/vaultwarden/client_cli.go (CLI interaction) - Bitwarden CLI dependency (Node.js + npm package) - VAULTWARDEN_CLIENT_ID, VAULTWARDEN_CLIENT_SECRET env vars ## Added - internal/vaultwarden/crypto.go (encryption/decryption) - internal/vaultwarden/crypto_test.go (11 unit tests) - internal/vaultwarden/api_client.go (HTTP client) ## Unchanged - External API surface (GET /secret/:name, POST /refresh, GET /health) - All security middleware (auth, IP whitelist, rate limiting)
41 lines
1.3 KiB
Go
41 lines
1.3 KiB
Go
package vaultwarden
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/thijsherman/vaultwarden-api/pkg/logger"
|
|
)
|
|
|
|
// InitializeClient creates and initializes a fully authenticated vault client.
|
|
// clientID and clientSecret are optional — if provided, API key login is used (bypasses 2FA).
|
|
func InitializeClient(serverURL, email, password, clientID, clientSecret string, cacheTTL, syncInterval time.Duration) (*Client, error) {
|
|
logger.Info.Println("Initializing Vaultwarden native API client...")
|
|
|
|
api := NewAPIClient(serverURL, email, password, clientID, clientSecret)
|
|
client := NewClient(api, cacheTTL, syncInterval)
|
|
|
|
// Authenticate and perform initial sync with retry.
|
|
maxRetries := 3
|
|
var lastErr error
|
|
|
|
for attempt := 1; attempt <= maxRetries; attempt++ {
|
|
if attempt > 1 {
|
|
backoff := time.Duration(attempt*attempt) * 5 * time.Second
|
|
logger.Info.Printf("Retry attempt %d/%d after %v...", attempt, maxRetries, backoff)
|
|
time.Sleep(backoff)
|
|
}
|
|
|
|
if err := client.Initialize(); err != nil {
|
|
logger.Warn.Printf("Initialization failed (attempt %d/%d): %v", attempt, maxRetries, err)
|
|
lastErr = err
|
|
continue
|
|
}
|
|
|
|
logger.Info.Println("Vaultwarden client initialized successfully")
|
|
return client, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("failed to initialize after %d attempts: %w", maxRetries, lastErr)
|
|
}
|