""" Token generation and validation utilities """ import secrets import string from datetime import datetime, timedelta from typing import Optional class TokenGenerator: """ Secure token generator for authentication flows Generates various types of tokens: - Request tokens (for auth flow) - Session tokens - One-time tokens - Recovery codes """ ALPHANUMERIC = string.ascii_letters + string.digits URLSAFE = string.ascii_letters + string.digits + "-_" @classmethod def generate_request_token(cls, length: int = 32) -> str: """Generate secure request token""" return f"req_{secrets.token_urlsafe(length)}" @classmethod def generate_session_token(cls, length: int = 32) -> str: """Generate secure session token""" return f"sess_{secrets.token_urlsafe(length)}" @classmethod def generate_refresh_token(cls, length: int = 48) -> str: """Generate secure refresh token""" return f"rt_{secrets.token_urlsafe(length)}" @classmethod def generate_nonce(cls, length: int = 32) -> str: """Generate cryptographic nonce""" return secrets.token_urlsafe(length) @classmethod def generate_pin(cls, length: int = 6) -> str: """Generate numeric PIN code""" return ''.join(secrets.choice(string.digits) for _ in range(length)) @classmethod def generate_recovery_code(cls, segments: int = 4, segment_length: int = 4) -> str: """Generate human-readable recovery code""" segments_list = [ ''.join(secrets.choice(cls.ALPHANUMERIC) for _ in range(segment_length)) for _ in range(segments) ] return '-'.join(segments_list).upper() @classmethod def generate_api_key(cls, prefix: str = "qk", length: int = 32) -> str: """Generate API key with prefix""" return f"{prefix}_{secrets.token_urlsafe(length)}" @classmethod def generate_device_id(cls) -> str: """Generate unique device identifier""" return f"dev_{secrets.token_urlsafe(16)}" @classmethod def generate_qr_payload( cls, request_token: str, nonce: str, expires_at: datetime, base_url: str = "quixzoom://auth" ) -> str: """ Generate QR code payload Format: quixzoom://auth?token=&nonce=&exp= """ exp_timestamp = int(expires_at.timestamp()) return f"{base_url}?token={request_token}&nonce={nonce}&exp={exp_timestamp}" @classmethod def parse_qr_payload(cls, payload: str) -> Optional[dict]: """ Parse QR code payload Returns: dict with token, nonce, exp or None if invalid """ try: # Remove scheme if '://' in payload: payload = payload.split('://', 1)[1] # Parse query parameters if '?' not in payload: return None query_string = payload.split('?', 1)[1] params = {} for param in query_string.split('&'): if '=' in param: key, value = param.split('=', 1) params[key] = value return { "token": params.get("token"), "nonce": params.get("nonce"), "exp": int(params.get("exp", 0)) if params.get("exp") else None } except Exception: return None @classmethod def is_token_format_valid(cls, token: str, prefix: Optional[str] = None) -> bool: """Check if token has valid format""" if not token or len(token) < 10: return False if prefix and not token.startswith(prefix): return False # Check for valid characters valid_chars = set(cls.URLSAFE + '_') return all(c in valid_chars for c in token.replace(prefix + '_', '') if prefix else token) # Global token generator token_generator = TokenGenerator()