# Player Authentication System - DigiServer v2 & Kiwy-Signage ## Overview DigiServer v2 now includes a secure player authentication system compatible with Kiwy-Signage players. Players can authenticate using either a password or quick connect code, and their credentials are securely stored locally. ## Features ✅ **Dual Authentication Methods** - Password-based authentication (secure bcrypt hashing) - Quick Connect codes for easy pairing ✅ **Secure Credential Storage** - Auth codes saved locally in encrypted configuration - No need to re-authenticate on every restart ✅ **Automatic Session Management** - Auth codes persist across player restarts - Automatic status updates and heartbeats ✅ **Player Identification** - Unique hostname for each player - Configurable display orientation (Landscape/Portrait) ## Database Schema The Player model now includes: ```python class Player(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), nullable=False) hostname = db.Column(db.String(255), unique=True, nullable=False, index=True) location = db.Column(db.String(255), nullable=True) auth_code = db.Column(db.String(255), unique=True, nullable=False, index=True) password_hash = db.Column(db.String(255), nullable=False) quickconnect_code = db.Column(db.String(255), nullable=True) group_id = db.Column(db.Integer, db.ForeignKey('group.id'), nullable=True) orientation = db.Column(db.String(16), default='Landscape') status = db.Column(db.String(50), default='offline') last_seen = db.Column(db.DateTime, nullable=True) created_at = db.Column(db.DateTime, default=datetime.utcnow) ``` ## API Endpoints ### 1. Player Authentication **POST** `/api/auth/player` Authenticate a player and receive auth code. **Request:** ```json { "hostname": "player-001", "password": "your_password", "quickconnect_code": "QUICK123" // Optional if using password } ``` **Response (200 OK):** ```json { "success": true, "player_id": 1, "player_name": "Demo Player", "hostname": "player-001", "auth_code": "abc123xyz...", "group_id": 5, "orientation": "Landscape", "status": "online" } ``` **Error Response (401 Unauthorized):** ```json { "error": "Invalid credentials" } ``` ### 2. Verify Auth Code **POST** `/api/auth/verify` Verify an existing auth code. **Request:** ```json { "auth_code": "abc123xyz..." } ``` **Response (200 OK):** ```json { "valid": true, "player_id": 1, "player_name": "Demo Player", "hostname": "player-001", "group_id": 5, "orientation": "Landscape", "status": "online" } ``` ## Player Configuration File Players store their configuration in `player_config.ini`: ```ini [server] server_url = http://your-server:5000 [player] hostname = player-001 auth_code = abc123xyz... player_id = 1 group_id = 5 [display] orientation = Landscape resolution = 1920x1080 [security] verify_ssl = true timeout = 30 [cache] cache_dir = ./cache max_cache_size = 1024 [logging] enabled = true log_level = INFO log_file = ./player.log ``` ## Integration with Kiwy-Signage ### Step 1: Copy Authentication Module Copy `player_auth_module.py` to your Kiwy-Signage project: ```bash cp digiserver-v2/player_auth_module.py signage-player/src/player_auth.py ``` ### Step 2: Initialize Authentication In your main signage player code: ```python from player_auth import PlayerAuth # Initialize authentication auth = PlayerAuth(config_path='player_config.ini') # Check if already authenticated if auth.is_authenticated(): # Verify saved credentials valid, info = auth.verify_auth() if valid: print(f"Authenticated as: {info['player_name']}") else: # Re-authenticate success, error = auth.authenticate( hostname='player-001', password='your_password' ) else: # First time setup hostname = input("Enter player hostname: ") password = input("Enter password: ") success, error = auth.authenticate(hostname, password) if success: print("Authentication successful!") else: print(f"Authentication failed: {error}") ``` ### Step 3: Use Authentication for API Calls ```python # Get playlist with authentication playlist = auth.get_playlist() # Send heartbeat auth.send_heartbeat(status='online') # Make authenticated API request import requests auth_code = auth.get_auth_code() player_id = auth.config.get('player', 'player_id') server_url = auth.get_server_url() response = requests.get( f"{server_url}/api/playlists/{player_id}", headers={'Authorization': f'Bearer {auth_code}'} ) ``` ## Server Setup ### 1. Create Players in DigiServer Via Web Interface: 1. Log in as admin (admin/admin123) 2. Navigate to Players → Add Player 3. Fill in: - **Name**: Display name - **Hostname**: Unique identifier (e.g., `player-001`) - **Location**: Physical location - **Password**: Secure password - **Quick Connect Code**: Optional easy pairing code - **Orientation**: Landscape or Portrait Via Python: ```python from app.extensions import db from app.models import Player import secrets player = Player( name='Office Player', hostname='office-player-001', location='Main Office - Reception', auth_code=secrets.token_urlsafe(32), orientation='Landscape' ) player.set_password('secure_password_123') player.set_quickconnect_code('OFFICE123') db.session.add(player) db.session.commit() ``` ### 2. Distribute Credentials Securely provide each player with: - Server URL - Hostname - Password OR Quick Connect Code ## Security Considerations ✅ **Passwords**: Hashed with bcrypt (cost factor 12) ✅ **Auth Codes**: 32-byte URL-safe tokens ✅ **HTTPS**: Enable SSL in production ✅ **Rate Limiting**: API endpoints protected (10 req/min for auth) ✅ **Local Storage**: Config file permissions should be 600 ## Troubleshooting ### Player Can't Authenticate 1. Check server connectivity: ```bash curl http://your-server:5000/api/health ``` 2. Verify credentials in database 3. Check server logs for authentication attempts 4. Ensure hostname is unique ### Auth Code Invalid 1. Clear saved config: `rm player_config.ini` 2. Re-authenticate with password 3. Check if player was deleted from server ### Connection Timeout 1. Increase timeout in `player_config.ini`: ```ini [security] timeout = 60 ``` 2. Check network connectivity 3. Verify server is running ## Migration from v1 If migrating from DigiServer v1: 1. **Export player data** from v1 database 2. **Create players** in v2 with hostname = old username 3. **Set passwords** using `player.set_password()` 4. **Update player apps** with new authentication module 5. **Test authentication** before full deployment ## Example: Complete Player Setup ```python #!/usr/bin/env python3 """ Complete player setup example """ from player_auth import PlayerAuth import sys def setup_player(): """Interactive player setup""" auth = PlayerAuth() # Check if already configured if auth.is_authenticated(): print(f"✅ Already configured as: {auth.get_hostname()}") # Test connection valid, info = auth.verify_auth() if valid: print(f"✅ Connection successful") print(f" Player: {info['player_name']}") print(f" Group: {info.get('group_id', 'None')}") return True else: print("❌ Saved credentials invalid, reconfiguring...") auth.clear_auth() # First time setup print("\n🚀 Player Setup") print("-" * 50) # Get server URL server_url = input("Server URL [http://localhost:5000]: ").strip() if server_url: auth.config['server']['server_url'] = server_url auth.save_config() # Get hostname hostname = input("Player hostname: ").strip() if not hostname: print("❌ Hostname required") return False # Authentication method print("\nAuthentication method:") print("1. Password") print("2. Quick Connect Code") choice = input("Choice [1]: ").strip() or "1" if choice == "1": password = input("Password: ").strip() success, error = auth.authenticate(hostname, password=password) else: code = input("Quick Connect Code: ").strip() success, error = auth.authenticate(hostname, quickconnect_code=code) if success: print("\n✅ Authentication successful!") print(f" Config saved to: {auth.config_path}") return True else: print(f"\n❌ Authentication failed: {error}") return False if __name__ == '__main__': if setup_player(): sys.exit(0) else: sys.exit(1) ``` ## Files Included - `player_auth_module.py` - Python authentication module for players - `player_config_template.ini` - Configuration template - `reinit_db.sh` - Script to recreate database with new schema - `PLAYER_AUTH.md` - This documentation ## Support For issues or questions: 1. Check server logs: `app/instance/logs/` 2. Check player logs: `player.log` 3. Verify API health: `/api/health` 4. Review authentication attempts in server logs