""" Generates Twilio Access Tokens for the CallSaathi Demo Caller Android app. Security model: - Uses API Key (SK...) + API Secret — NOT the Auth Token - Tokens are short-lived (configurable, default 1 hour) - Each token is scoped to only outgoing calls via TwiML App - Tokens do NOT expose account credentials Usage: from token_generator import create_demo_caller_token token_jwt = create_demo_caller_token() """ import os import time from twilio.jwt.access_token import AccessToken from twilio.jwt.access_token.grants import VoiceGrant from dotenv import load_dotenv load_dotenv() def create_demo_caller_token(identity: str = "demo-caller", ttl_seconds: int = 3600) -> str: """ Creates a Twilio Access Token for the demo caller Android app. Args: identity: Unique identifier for this client (default: "demo-caller") ttl_seconds: Token lifetime in seconds (default: 3600 = 1 hour) Returns: JWT string to pass to the Android app's Twilio Voice SDK Raises: EnvironmentError: If required environment variables are missing """ account_sid = os.getenv("TWILIO_ACCOUNT_SID") api_key_sid = os.getenv("TWILIO_API_KEY_SID") api_key_secret = os.getenv("TWILIO_API_KEY_SECRET") twiml_app_sid = os.getenv("TWILIO_TWIML_APP_SID") # Validate all required env vars are present missing = [] if not account_sid: missing.append("TWILIO_ACCOUNT_SID") if not api_key_sid: missing.append("TWILIO_API_KEY_SID") if not api_key_secret: missing.append("TWILIO_API_KEY_SECRET") if not twiml_app_sid: missing.append("TWILIO_TWIML_APP_SID") if missing: raise EnvironmentError( f"Missing required environment variables: {', '.join(missing)}\n" "Run: python scripts/verify_twilio_config.py to check your .env file." ) # Create the Access Token token = AccessToken( account_sid, api_key_sid, api_key_secret, identity=identity, ttl=ttl_seconds ) # Add Voice Grant — allows outgoing calls via the TwiML App voice_grant = VoiceGrant( outgoing_application_sid=twiml_app_sid, incoming_allow=False # Demo caller only makes calls, does not receive ) token.add_grant(voice_grant) jwt_string = token.to_jwt() # Python 3.10+ friendly time formatting expiry_time = time.strftime( '%Y-%m-%d %H:%M:%S UTC', time.gmtime(time.time() + ttl_seconds) ) print(f"[TokenGen] Token created for '{identity}', expires: {expiry_time}") return jwt_string def create_demo_caller_token_response() -> dict: """ Returns a dict ready to be returned as a FastAPI JSON response. """ try: token = create_demo_caller_token() return { "token": token, "identity": "demo-caller", "expires_in": 3600, "status": "ok" } except EnvironmentError as e: return { "token": None, "status": "error", "message": str(e) } if __name__ == "__main__": try: print("\n--- TWILIO TOKEN VERIFICATION ---") token = create_demo_caller_token() print("\nāœ… SUCCESS: Generated valid JWT token.") print(f"Token (First 20 chars): {token[:20]}...") print("\nIf you are seeing this, your .env keys are CORRECT.") print("Now copy these keys to your Hugging Face Secrets.") except Exception as e: print(f"\nāŒ ERROR: {str(e)}")