Files
vw-api/internal/vaultwarden/init.go
T
Thijs Herman 9f8ebf2408 feat: replace Bitwarden CLI with native Go API client (#2)
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)
2026-02-20 13:28:38 +01:00

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)
}