# Gas Stations Feature - Secrets Verification Guide ## Overview Complete guide for verifying Google Maps API key secrets are correctly configured in both backend and frontend containers. This document covers verification commands, troubleshooting steps, and security validation. ## Secrets Architecture ### Backend Secret Flow ``` Host filesystem: ./secrets/app/google-maps-api-key.txt ↓ (Docker volume mount) Container: /run/secrets/google-maps-api-key ↓ (Config loader reads at startup) Backend service: process.env or config object ↓ (Used in Google Maps client) Google Places API calls ``` ### Frontend Secret Flow ``` Host filesystem: ./secrets/app/google-maps-api-key.txt ↓ (Docker volume mount) Container: /run/secrets/google-maps-api-key ↓ (Entrypoint script reads at startup) Generated file: /usr/share/nginx/html/config.js ↓ (Loaded in index.html) Browser: window.CONFIG.googleMapsApiKey ↓ (Used in maps-loader.ts) Google Maps JavaScript API ``` ## Verification Commands ### 1. Host Filesystem Verification **Check secret file exists**: ```bash ls -la ./secrets/app/google-maps-api-key.txt ``` **Expected Output**: ``` -rw-r--r-- 1 user user 39 Jan 15 10:00 ./secrets/app/google-maps-api-key.txt ``` **View secret content** (only in development): ```bash cat ./secrets/app/google-maps-api-key.txt ``` **Expected**: API key starting with `AIzaSy...` (39 characters) **Check file permissions**: ```bash stat -c "%a %n" ./secrets/app/google-maps-api-key.txt ``` **Expected**: `644` (readable by owner and group) ### 2. Backend Container Verification **Check secret mounted in container**: ```bash docker compose exec mvp-backend cat /run/secrets/google-maps-api-key ``` **Expected**: Same API key as host file **Check secret file permissions in container**: ```bash docker compose exec mvp-backend ls -la /run/secrets/google-maps-api-key ``` **Expected**: Read-only file (`:ro` mount flag) **Check backend can access secret**: ```bash docker compose exec mvp-backend node -e " const fs = require('fs'); try { const key = fs.readFileSync('/run/secrets/google-maps-api-key', 'utf8').trim(); console.log('Secret loaded, length:', key.length); console.log('Starts with AIzaSy:', key.startsWith('AIzaSy')); } catch (error) { console.error('Failed to read secret:', error.message); } " ``` **Expected Output**: ``` Secret loaded, length: 39 Starts with AIzaSy: true ``` **Check backend logs for secret loading**: ```bash docker compose logs mvp-backend | grep -i "google.*api.*key" ``` **Expected** (log message should NOT show actual key, only status): ``` [Config] Google Maps API key loaded successfully ``` ### 3. Frontend Container Verification **Check secret mounted in container**: ```bash docker compose exec mvp-frontend cat /run/secrets/google-maps-api-key ``` **Expected**: Same API key as host file **Check config.js generated**: ```bash docker compose exec mvp-frontend cat /usr/share/nginx/html/config.js ``` **Expected Output**: ```javascript window.CONFIG = { googleMapsApiKey: 'AIzaSyYourActualKeyHere' }; ``` **Check entrypoint script ran**: ```bash docker compose logs mvp-frontend | grep -i config ``` **Expected Output**: ``` [Config] Loaded Google Maps API key from /run/secrets/google-maps-api-key [Config] Generated /usr/share/nginx/html/config.js ``` **Check config.js is served**: ```bash curl -s http://localhost:3000/config.js ``` **Expected**: JavaScript file with `window.CONFIG = {...}` ### 4. Browser Verification **Check config loaded in browser console**: 1. Open https://motovaultpro.com in browser 2. Open Developer Tools (F12) 3. Go to Console tab 4. Run: ```javascript console.log(window.CONFIG); ``` **Expected Output**: ```javascript { googleMapsApiKey: "AIzaSyYourActualKeyHere" } ``` **Check Google Maps script loads**: ```javascript console.log(typeof google !== 'undefined' ? 'Google Maps loaded' : 'Not loaded yet'); ``` **Check for config errors**: ```javascript // Should not show any errors related to config console.log(document.querySelectorAll('script[src*="config.js"]').length); // Should return: 1 ``` ### 5. API Integration Verification **Test backend can call Google Maps API**: ```bash docker compose exec mvp-backend node -e " const https = require('https'); const fs = require('fs'); const apiKey = fs.readFileSync('/run/secrets/google-maps-api-key', 'utf8').trim(); const url = \`https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=37.7749,-122.4194&radius=5000&type=gas_station&key=\${apiKey}\`; https.get(url, (res) => { let data = ''; res.on('data', (chunk) => data += chunk); res.on('end', () => { const json = JSON.parse(data); console.log('API Status:', json.status); console.log('Results:', json.results?.length || 0, 'stations'); }); }).on('error', (err) => { console.error('API Error:', err.message); }); " ``` **Expected Output**: ``` API Status: OK Results: 20 stations ``` **Test full API workflow with JWT**: ```bash # Get JWT token (from Auth0 or test token) export TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." # Search for stations curl -X POST http://localhost:3001/api/stations/search \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "latitude": 37.7749, "longitude": -122.4194, "radius": 5000 }' | jq '.stations | length' ``` **Expected**: Number greater than 0 (indicating stations found) ## Security Validation ### 1. Secret Not in Environment Variables **Check backend environment**: ```bash docker compose exec mvp-backend env | grep -i google ``` **Expected**: No GOOGLE_MAPS_API_KEY in environment (should use file mount) **Check frontend environment**: ```bash docker compose exec mvp-frontend env | grep -i google ``` **Expected**: No GOOGLE_MAPS_API_KEY in environment ### 2. Secret Not in Docker Images **Check backend image**: ```bash docker history motovaultpro-backend | grep -i google ``` **Expected**: No API key in image layers **Check frontend image**: ```bash docker history motovaultpro-frontend | grep -i google ``` **Expected**: No API key in image layers ### 3. Secret Not in Logs **Check all logs for API key exposure**: ```bash docker compose logs | grep -i "AIzaSy" ``` **Expected**: No matches (API key should never be logged) **Check for "secret" or "key" logging**: ```bash docker compose logs | grep -i "api.*key" | grep -v "loaded successfully" ``` **Expected**: Only status messages, no actual key values ### 4. File Permissions Secure **Check host file not world-readable**: ```bash ls -l ./secrets/app/google-maps-api-key.txt | awk '{print $1}' ``` **Expected**: `-rw-r--r--` or stricter (not `-rw-rw-rw-`) **Check container mount is read-only**: ```bash docker compose config | grep -A5 "google-maps-api-key" ``` **Expected**: Contains `:ro` flag ### 5. Secret Rotation Test **Update secret and verify change propagates**: ```bash # Backup current secret cp ./secrets/app/google-maps-api-key.txt ./secrets/app/google-maps-api-key.txt.bak # Update with new key (test key for rotation testing) echo "AIzaSyTestKeyForRotation" > ./secrets/app/google-maps-api-key.txt # Restart containers docker compose restart mvp-backend mvp-frontend # Wait for startup sleep 5 # Verify new key loaded in backend docker compose exec mvp-backend cat /run/secrets/google-maps-api-key # Verify new key in frontend config.js docker compose exec mvp-frontend cat /usr/share/nginx/html/config.js # Restore original key mv ./secrets/app/google-maps-api-key.txt.bak ./secrets/app/google-maps-api-key.txt # Restart again docker compose restart mvp-backend mvp-frontend ``` ## Troubleshooting ### Backend Can't Read Secret **Symptom**: ```bash docker compose logs mvp-backend | grep -i "google" # Shows: Failed to load Google Maps API key ``` **Solutions**: 1. **Check file exists on host**: ```bash ls -la ./secrets/app/google-maps-api-key.txt ``` 2. **Check docker-compose.yml mount**: ```bash grep -A5 "mvp-backend:" docker-compose.yml | grep google-maps ``` Should show: ```yaml - ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro ``` 3. **Check file in container**: ```bash docker compose exec mvp-backend ls -la /run/secrets/ ``` 4. **Restart backend**: ```bash docker compose restart mvp-backend ``` ### Frontend config.js Not Generated **Symptom**: ```bash docker compose exec mvp-frontend cat /usr/share/nginx/html/config.js # Shows: No such file or directory ``` **Solutions**: 1. **Check entrypoint script exists**: ```bash docker compose exec mvp-frontend ls -la /app/load-config.sh ``` 2. **Check entrypoint runs**: ```bash docker compose logs mvp-frontend | head -20 ``` Should show config generation messages. 3. **Run entrypoint manually**: ```bash docker compose exec mvp-frontend sh /app/load-config.sh ``` 4. **Restart frontend**: ```bash docker compose restart mvp-frontend ``` ### Browser Shows "API key undefined" **Symptom**: Browser console shows `window.CONFIG.googleMapsApiKey` is undefined **Solutions**: 1. **Check config.js loads**: Open browser DevTools > Network tab > Refresh page > Look for `config.js` 2. **Check config.js content**: ```bash curl http://localhost:3000/config.js ``` 3. **Check index.html loads config.js**: View page source, look for: ```html ``` 4. **Clear browser cache**: Hard refresh: Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (Mac) ### Google API Returns "Invalid API Key" **Symptom**: API calls return 400 error with "The provided API key is invalid" **Solutions**: 1. **Verify key format**: ```bash cat ./secrets/app/google-maps-api-key.txt | wc -c ``` Should be 39 characters (including newline) or 40. 2. **Check for extra whitespace**: ```bash cat ./secrets/app/google-maps-api-key.txt | od -c ``` Should only show key and newline, no spaces/tabs. 3. **Verify key in Google Cloud Console**: - Go to APIs & Services > Credentials - Confirm key exists and is not deleted 4. **Check API restrictions**: - Verify IP/domain restrictions don't block your server - Verify API restrictions include Places API ### Secret File Permissions Wrong **Symptom**: Container can't read secret file **Solutions**: ```bash # Fix file permissions chmod 644 ./secrets/app/google-maps-api-key.txt # Verify ownership ls -l ./secrets/app/google-maps-api-key.txt # Restart containers docker compose restart mvp-backend mvp-frontend ``` ## Automated Verification Script Create a verification script for convenience: ```bash #!/bin/bash # verify-secrets.sh echo "=== Gas Stations Secrets Verification ===" echo "" echo "1. Host file exists:" [ -f ./secrets/app/google-maps-api-key.txt ] && echo "✓ PASS" || echo "✗ FAIL" echo "" echo "2. Backend can read secret:" docker compose exec -T mvp-backend cat /run/secrets/google-maps-api-key >/dev/null 2>&1 && echo "✓ PASS" || echo "✗ FAIL" echo "" echo "3. Frontend can read secret:" docker compose exec -T mvp-frontend cat /run/secrets/google-maps-api-key >/dev/null 2>&1 && echo "✓ PASS" || echo "✗ FAIL" echo "" echo "4. Frontend config.js generated:" docker compose exec -T mvp-frontend cat /usr/share/nginx/html/config.js >/dev/null 2>&1 && echo "✓ PASS" || echo "✗ FAIL" echo "" echo "5. API key format valid:" KEY=$(cat ./secrets/app/google-maps-api-key.txt) [[ $KEY =~ ^AIzaSy ]] && echo "✓ PASS" || echo "✗ FAIL" echo "" echo "=== Verification Complete ===" ``` **Usage**: ```bash chmod +x verify-secrets.sh ./verify-secrets.sh ``` ## References - Architecture: `/backend/src/features/stations/docs/ARCHITECTURE.md` - Google Maps Setup: `/backend/src/features/stations/docs/GOOGLE-MAPS-SETUP.md` - Deployment Checklist: `/backend/src/features/stations/docs/deployment/DEPLOYMENT-CHECKLIST.md` - Runtime Config: `/frontend/docs/RUNTIME-CONFIG.md`