# Player Code HTTPS Integration Guide

## Server-Side Improvements Implemented

All critical and medium improvements have been implemented on the server:

### ✅ CORS Support Enabled
- **File**: `app/extensions.py` - CORS extension initialized
- **File**: `app/app.py` - CORS configured for `/api/*` endpoints
- All player API requests now support cross-origin requests
- Preflight OPTIONS requests are properly handled

### ✅ SSL Certificate Endpoint Added
- **Endpoint**: `GET /api/certificate`
- **Location**: `app/blueprints/api.py`
- Returns server certificate in PEM format with metadata:
  - Certificate content (PEM format)
  - Certificate info (subject, issuer, validity dates, fingerprint)
  - Integration instructions for different platforms

### ✅ HTTPS Configuration Updated
- **File**: `app/config.py` - ProductionConfig now has:
  - `SESSION_COOKIE_SECURE = True`
  - `SESSION_COOKIE_SAMESITE = 'Lax'`
- **File**: `nginx.conf` - Added:
  - CORS headers for all responses
  - OPTIONS request handling
  - X-Forwarded-Port header forwarding

### ✅ Nginx Proxy Configuration Enhanced
- Added CORS headers at nginx level for defense-in-depth
- Proper X-Forwarded headers for protocol/port detection
- HTTPS-friendly proxy configuration

---

## Required Player Code Changes

### 1. **For Python/Kivy Players Using Requests Library**

**Update:** Import and use certificate handling:

```python
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
import os

class DigiServerClient:
    def __init__(self, server_url, hostname, quickconnect_code, use_https=True):
        self.server_url = server_url
        self.hostname = hostname
        self.quickconnect_code = quickconnect_code
        self.session = requests.Session()
        
        # CRITICAL: Handle SSL verification
        if use_https:
            # Option 1: Get certificate from server and trust it
            self.setup_certificate_trust()
        else:
            # Option 2: Disable SSL verification (DEV ONLY)
            self.session.verify = False
    
    def setup_certificate_trust(self):
        """Download server certificate and configure trust."""
        try:
            # First, make a request without verification to get the cert
            response = requests.get(
                f"{self.server_url}/api/certificate",
                verify=False,
                timeout=5
            )
            
            if response.status_code == 200:
                cert_data = response.json()
                
                # Save certificate locally
                cert_path = os.path.expanduser('~/.digiserver/server_cert.pem')
                os.makedirs(os.path.dirname(cert_path), exist_ok=True)
                
                with open(cert_path, 'w') as f:
                    f.write(cert_data['certificate'])
                
                # Configure session to use this certificate
                self.session.verify = cert_path
                
                print(f"✓ Server certificate installed from {cert_data['certificate_info']['issuer']}")
                print(f"  Valid until: {cert_data['certificate_info']['valid_until']}")
                
        except Exception as e:
            print(f"⚠️ Failed to setup certificate trust: {e}")
            print("  Falling back to unverified connection (not recommended for production)")
            self.session.verify = False
    
    def get_playlist(self):
        """Get playlist from server with proper error handling."""
        try:
            response = self.session.get(
                f"{self.server_url}/api/playlists",
                params={
                    'hostname': self.hostname,
                    'quickconnect_code': self.quickconnect_code
                },
                timeout=10
            )
            response.raise_for_status()
            return response.json()
        
        except requests.exceptions.SSLError as e:
            print(f"❌ SSL Error: {e}")
            # Log error for debugging
            print("  This usually means the server certificate is not trusted.")
            print("  Try running: DigiServerClient.setup_certificate_trust()")
            raise
        
        except requests.exceptions.ConnectionError as e:
            print(f"❌ Connection Error: {e}")
            raise
        
        except Exception as e:
            print(f"❌ Error: {e}")
            raise
    
    def send_feedback(self, status, message=''):
        """Send player feedback/status to server."""
        try:
            response = self.session.post(
                f"{self.server_url}/api/player-feedback",
                json={
                    'hostname': self.hostname,
                    'quickconnect_code': self.quickconnect_code,
                    'status': status,
                    'message': message,
                    'timestamp': datetime.utcnow().isoformat()
                },
                timeout=10
            )
            response.raise_for_status()
            return response.json()
        except Exception as e:
            print(f"Error sending feedback: {e}")
            return None
```

### 2. **For Kivy Framework Specifically**

**Update:** In your Kivy HTTP client configuration:

```python
from kivy.network.urlrequest import UrlRequest
from kivy.logger import Logger
import ssl
import certifi

class DigiServerKivyClient:
    def __init__(self, server_url, hostname, quickconnect_code):
        self.server_url = server_url
        self.hostname = hostname
        self.quickconnect_code = quickconnect_code
        
        # Configure SSL context for Kivy requests
        self.ssl_context = self._setup_ssl_context()
    
    def _setup_ssl_context(self):
        """Setup SSL context with certificate trust."""
        try:
            # Try to get server certificate
            import requests
            response = requests.get(
                f"{self.server_url}/api/certificate",
                verify=False,
                timeout=5
            )
            
            if response.status_code == 200:
                cert_data = response.json()
                cert_path = os._get_cert_path()
                
                with open(cert_path, 'w') as f:
                    f.write(cert_data['certificate'])
                
                # Create SSL context
                context = ssl.create_default_context()
                context.load_verify_locations(cert_path)
                
                Logger.info('DigiServer', f'SSL context configured with server certificate')
                return context
        
        except Exception as e:
            Logger.warning('DigiServer', f'Failed to setup SSL: {e}')
            return None
    
    def fetch_playlist(self, callback):
        """Fetch playlist with proper SSL handling."""
        url = f"{self.server_url}/api/playlists"
        params = f"?hostname={self.hostname}&quickconnect_code={self.quickconnect_code}"
        
        headers = {
            'Content-Type': 'application/json',
            'User-Agent': 'Kiwy-Signage-Player/1.0'
        }
        
        request = UrlRequest(
            url + params,
            on_success=callback,
            on_error=self._on_error,
            on_failure=self._on_failure,
            headers=headers
        )
        
        return request
    
    def _on_error(self, request, error):
        Logger.error('DigiServer', f'Request error: {error}')
    
    def _on_failure(self, request, result):
        Logger.error('DigiServer', f'Request failed: {result}')
```

### 3. **Environment Configuration**

**Add to player app_config.json or environment:**

```json
{
  "server": {
    "url": "https://192.168.0.121",
    "hostname": "player1",
    "quickconnect_code": "ABC123XYZ",
    "verify_ssl": false,
    "use_server_certificate": true,
    "certificate_path": "~/.digiserver/server_cert.pem"
  },
  "connection": {
    "timeout": 10,
    "retry_attempts": 3,
    "retry_delay": 5
  }
}
```

---

## Testing Checklist

### Server-Side Tests

- [ ] Verify CORS headers present: `curl -v https://192.168.0.121/api/health`
- [ ] Check certificate endpoint: `curl -k https://192.168.0.121/api/certificate`
- [ ] Test OPTIONS preflight: `curl -X OPTIONS https://192.168.0.121/api/playlists`
- [ ] Verify X-Forwarded headers: `curl -v https://192.168.0.121/`

### Player Connection Tests

- [ ] Player connects with HTTPS successfully
- [ ] Player fetches playlist without SSL errors
- [ ] Player receives status update confirmation
- [ ] Player sends feedback/heartbeat correctly

### Integration Tests

```bash
# Test certificate retrieval
curl -k https://192.168.0.121/api/certificate | jq '.certificate_info'

# Test CORS preflight for player
curl -X OPTIONS https://192.168.0.121/api/playlists \
  -H "Origin: http://192.168.0.121" \
  -H "Access-Control-Request-Method: GET" \
  -v

# Simulate player playlist fetch
curl -k https://192.168.0.121/api/playlists \
  --data-urlencode "hostname=test-player" \
  --data-urlencode "quickconnect_code=test123" \
  -H "Origin: *"
```

---

## Migration Steps

### For Existing Players

1. **Update player code** with new SSL handling from this guide
2. **Restart player application** to pick up changes
3. **Verify connection** works with HTTPS server
4. **Monitor logs** for any SSL-related errors

### For New Players

1. **Deploy updated player code** with SSL support from the start
2. **Configure with HTTPS server URL**
3. **Run initialization** to fetch and trust server certificate

---

## Troubleshooting

### "SSL: CERTIFICATE_VERIFY_FAILED"
- Player is rejecting the self-signed certificate
- **Solution**: Run certificate trust setup or disable SSL verification

### "Connection Refused"
- Server HTTPS port not accessible
- **Solution**: Check nginx is running, port 443 is open, firewall rules

### "CORS error"
- Browser/HTTP client blocking cross-origin request
- **Solution**: Verify CORS headers in response, check Origin header

### "Certificate not found at endpoint"
- Server certificate file missing
- **Solution**: Verify cert.pem exists at `/etc/nginx/ssl/cert.pem`

---

## Security Recommendations

1. **For Development/Testing**: Disable SSL verification temporarily
   ```python
   session.verify = False
   ```

2. **For Production**:
   - Use proper certificates (Let's Encrypt recommended)
   - Deploy certificate trust setup at player initialization
   - Monitor SSL certificate expiration
   - Implement certificate pinning for critical deployments

3. **For Self-Signed Certificates**:
   - Use `/api/certificate` endpoint to distribute certificates
   - Store certificates in secure location on device
   - Implement certificate update mechanism
   - Log certificate trust changes for auditing

---

## Next Steps

1. **Implement SSL handling** in player code using examples above
2. **Test with HTTP first** to ensure API works
3. **Enable HTTPS** and test with certificate handling
4. **Deploy to production** with proper SSL setup
5. **Monitor** player connections and SSL errors

