Files
tony f793b526aa fix(security): owner auth gate, OAuth state cookie binding, 0600 token perms
- Add OWNER_SECRET-based session: signed HMAC cookie, /api/auth/owner login,
  requireOwner middleware. All Tesla routes refuse 401 without it.
- Bind OAuth state to a SameSite=Lax httpOnly cookie at /start, validate
  match in /callback with constant-time compare. Refuses unmatched callbacks.
- Token store now mkdir 0700, writeFile + rename atomic, mode 0600 with
  defensive chmod. Owner-only on disk.
- VIN masked to last 4 in responses; partner-register no longer echoes raw
  Tesla body to clients; coord bounds checked on send-to-nav.
- Client: useTesla also tracks owner status; Connect Tesla button opens an
  OwnerLoginModal when not authenticated, then continues to Tesla OAuth.

Conscious deferrals:
- Explicit CSRF tokens on POST routes: mitigated by SameSite=Lax cookies
  + same-origin CORS. Will revisit if cross-origin clients land.
- At-rest token encryption: deferred for single-user app; tokens are on a
  0700 Dokku volume readable only by the app uid. Will add AES-GCM if we
  multi-tenant.
2026-05-31 22:32:22 +01:00

47 lines
1.9 KiB
TypeScript

import 'dotenv/config';
const home = process.env.HOME || process.env.USERPROFILE || '';
const defaultGrokBin = process.env.GROK_BIN
|| (home ? `${home}/.grok/bin/grok` : '')
|| '/usr/local/bin/grok';
export const env = {
port: parseInt(process.env.PORT || '3000', 10),
nodeEnv: process.env.NODE_ENV || 'development',
appUrl: process.env.APP_URL || 'https://tesla-roadtrip.test',
apiUrl: process.env.API_URL || 'https://api.tony.codes',
// Auth
authSecret: process.env.AUTH_SECRET || '',
authUrl: process.env.AUTH_URL || 'https://auth.tony.codes',
authClientId: process.env.AUTH_CLIENT_ID || 'tesla-roadtrip',
// Database (optional — trips persist to memory if unset)
databaseUrl: process.env.DATABASE_URL || '',
// Grok / xAI — local personal CLI (your authenticated Heavy account) is preferred for development
grokBin: defaultGrokBin,
xaiApiKey: process.env.XAI_API_KEY || '',
grokEnabled: process.env.GROK_ENABLED !== 'false',
forceXaiApi: process.env.FORCE_XAI_API === 'true',
// Owner auth — single-user gate for the Tesla integration until
// auth.tony.codes is wired in. Set OWNER_SECRET to a long random string.
ownerSecret: process.env.OWNER_SECRET || '',
// Tesla Fleet API
tesla: {
// Public key served at /.well-known/appspecific/com.tesla.3p.public-key.pem
// for domain verification. Set TESLA_FLEET_PUBLIC_KEY to the PEM contents
// (multi-line; can include literal newlines).
publicKey: process.env.TESLA_FLEET_PUBLIC_KEY || '',
// OAuth client credentials Tesla gives you after partner approval.
clientId: process.env.TESLA_FLEET_CLIENT_ID || '',
clientSecret: process.env.TESLA_FLEET_CLIENT_SECRET || '',
// Where Tesla redirects after the user authorises.
redirectUri: process.env.TESLA_FLEET_REDIRECT_URI || 'https://roadtrip.tony.codes/api/auth/tesla/callback',
// Region: 'eu' or 'na'.
region: (process.env.TESLA_FLEET_REGION || 'eu') as 'eu' | 'na',
},
} as const;