feat: implement OAuth2 token exchange and AES-256-GCM encryption
Phase 2.2: OAuth2 flow - Part 1 (Token exchange + encryption)
Implemented:
- OAuth2 authorization code exchange with PKCE
* Real HTTP POST to token endpoint
* Parses access_token, refresh_token, expires_in, token_type
* Calculates expires_at timestamp
- AES-256-GCM token encryption
* Uses TFTSR_ENCRYPTION_KEY env var (or dev default)
* Random nonce per encryption (12 bytes)
* Base64-encoded output with nonce prepended
* Proper key derivation (32 bytes)
- Updated credential storage
* store_pat() now encrypts tokens before DB storage
* get_pat() decrypts tokens on retrieval
* Stores both token_hash (audit) and encrypted_token (actual)
Dependencies added:
- mockito 1.7.2 (dev) - HTTP mocking for tests
- aes-gcm 0.10 - AES-256-GCM encryption
- rand 0.8 - Cryptographically secure RNG
TDD tests (20 passing with --test-threads=1):
- OAuth exchange: success, missing token, HTTP error, network error
- Encryption: roundtrip, different nonces, invalid data, wrong key
- PAT storage: store/retrieve, nonexistent service, replacement
Note: Tests require single-threaded execution due to env var
test isolation. This is acceptable for CI/CD.