Skip to main content
โšก Calmops

Building Telemedicine Platforms: Architecture, Compliance, and Best Practices

Introduction

Telemedicine platforms have become essential healthcare infrastructure, especially post-pandemic. Building a compliant, scalable telemedicine system requires careful attention to HIPAA regulations, real-time video streaming, patient data security, and integration with existing healthcare systems. This guide covers the complete architecture and implementation of production telemedicine platforms.

Key Statistics:

  • Telemedicine market growing at 38% CAGR
  • 76% of patients prefer telemedicine for routine visits
  • HIPAA violations cost $100-$50,000 per incident
  • 99.9% uptime required for healthcare systems

Core Concepts & Terminology

1. Telemedicine

Remote delivery of healthcare services using telecommunications technology.

2. HIPAA Compliance

Meeting Health Insurance Portability and Accountability Act requirements for patient data protection.

3. End-to-End Encryption

Encrypting data so only sender and recipient can read it, not even the platform.

4. Real-Time Communication

Low-latency video/audio streaming for synchronous consultations.

5. Patient Portal

Secure interface for patients to access medical records and schedule appointments.

6. EHR Integration

Connecting telemedicine platform with Electronic Health Records systems.

7. Audit Logging

Recording all access to patient data for compliance and security.

8. Role-Based Access Control

Restricting access based on user roles (doctor, nurse, patient, admin).

9. Prescription Management

System for doctors to issue and patients to fill prescriptions electronically.

Tracking patient consent for data sharing and treatment.


Telemedicine Platform Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Patient/Provider Apps                     โ”‚
โ”‚         (Web, iOS, Android, Desktop Clients)                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                     โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              API Gateway & Load Balancer                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”‚
โ”‚  โ”‚ Authenticationโ”‚  โ”‚ Rate Limitingโ”‚  โ”‚ SSL/TLS      โ”‚      โ”‚
โ”‚  โ”‚ & Authorizationโ”‚ โ”‚              โ”‚  โ”‚ Encryption   โ”‚      โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                     โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              Core Services Layer                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”‚
โ”‚  โ”‚ Appointment  โ”‚  โ”‚ Video        โ”‚  โ”‚ Patient      โ”‚      โ”‚
โ”‚  โ”‚ Service      โ”‚  โ”‚ Service      โ”‚  โ”‚ Service      โ”‚      โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”‚
โ”‚  โ”‚ Prescription โ”‚  โ”‚ Messaging    โ”‚  โ”‚ Billing      โ”‚      โ”‚
โ”‚  โ”‚ Service      โ”‚  โ”‚ Service      โ”‚  โ”‚ Service      โ”‚      โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                     โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              Data & Integration Layer                        โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”‚
โ”‚  โ”‚ Patient DB   โ”‚  โ”‚ EHR          โ”‚  โ”‚ Audit Logs   โ”‚      โ”‚
โ”‚  โ”‚ (Encrypted)  โ”‚  โ”‚ Integration  โ”‚  โ”‚ (Immutable)  โ”‚      โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                     โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              Infrastructure Layer                            โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”‚
โ”‚  โ”‚ Video CDN    โ”‚  โ”‚ Message Queueโ”‚  โ”‚ Cache Layer  โ”‚      โ”‚
โ”‚  โ”‚ (WebRTC)     โ”‚  โ”‚ (Kafka)      โ”‚  โ”‚ (Redis)      โ”‚      โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Video Streaming Implementation

WebRTC Setup

from fastapi import FastAPI, WebSocket, HTTPException
from fastapi.responses import HTMLResponse
import json
import asyncio
from typing import Dict, Set
import logging

app = FastAPI()
logger = logging.getLogger(__name__)

# Store active connections
class ConnectionManager:
    def __init__(self):
        self.active_connections: Dict[str, Set[WebSocket]] = {}
        self.peer_connections: Dict[str, Dict] = {}
    
    async def connect(self, appointment_id: str, websocket: WebSocket, user_id: str):
        """Accept WebSocket connection"""
        await websocket.accept()
        
        if appointment_id not in self.active_connections:
            self.active_connections[appointment_id] = set()
        
        self.active_connections[appointment_id].add(websocket)
        
        # Store peer info
        if appointment_id not in self.peer_connections:
            self.peer_connections[appointment_id] = {}
        
        self.peer_connections[appointment_id][user_id] = {
            'websocket': websocket,
            'user_id': user_id
        }
        
        logger.info(f"User {user_id} connected to appointment {appointment_id}")
    
    def disconnect(self, appointment_id: str, user_id: str):
        """Remove connection"""
        if appointment_id in self.active_connections:
            # Find and remove websocket
            for ws in list(self.active_connections[appointment_id]):
                if self.peer_connections.get(appointment_id, {}).get(user_id, {}).get('websocket') == ws:
                    self.active_connections[appointment_id].discard(ws)
        
        if appointment_id in self.peer_connections:
            self.peer_connections[appointment_id].pop(user_id, None)
        
        logger.info(f"User {user_id} disconnected from appointment {appointment_id}")
    
    async def broadcast_to_appointment(self, appointment_id: str, message: dict, 
                                      exclude_user: str = None):
        """Broadcast message to all users in appointment"""
        if appointment_id not in self.active_connections:
            return
        
        for websocket in self.active_connections[appointment_id]:
            try:
                # Check if we should exclude this user
                should_send = True
                if exclude_user:
                    for user_id, peer_info in self.peer_connections.get(appointment_id, {}).items():
                        if user_id == exclude_user and peer_info['websocket'] == websocket:
                            should_send = False
                            break
                
                if should_send:
                    await websocket.send_json(message)
            except Exception as e:
                logger.error(f"Error broadcasting message: {e}")

manager = ConnectionManager()

@app.websocket("/ws/video/{appointment_id}/{user_id}")
async def websocket_endpoint(websocket: WebSocket, appointment_id: str, user_id: str):
    """WebSocket endpoint for video streaming"""
    
    # Verify user has access to appointment
    if not await verify_appointment_access(appointment_id, user_id):
        await websocket.close(code=4003, reason="Unauthorized")
        return
    
    await manager.connect(appointment_id, websocket, user_id)
    
    try:
        while True:
            data = await websocket.receive_text()
            message = json.loads(data)
            
            # Handle different message types
            if message['type'] == 'offer':
                # Forward SDP offer to other peer
                await manager.broadcast_to_appointment(
                    appointment_id,
                    {
                        'type': 'offer',
                        'from': user_id,
                        'sdp': message['sdp']
                    },
                    exclude_user=user_id
                )
            
            elif message['type'] == 'answer':
                # Forward SDP answer
                await manager.broadcast_to_appointment(
                    appointment_id,
                    {
                        'type': 'answer',
                        'from': user_id,
                        'sdp': message['sdp']
                    },
                    exclude_user=user_id
                )
            
            elif message['type'] == 'ice-candidate':
                # Forward ICE candidate
                await manager.broadcast_to_appointment(
                    appointment_id,
                    {
                        'type': 'ice-candidate',
                        'from': user_id,
                        'candidate': message['candidate']
                    },
                    exclude_user=user_id
                )
            
            elif message['type'] == 'chat':
                # Log and forward chat message
                await log_message(appointment_id, user_id, message['text'])
                await manager.broadcast_to_appointment(
                    appointment_id,
                    {
                        'type': 'chat',
                        'from': user_id,
                        'text': message['text'],
                        'timestamp': datetime.now().isoformat()
                    }
                )
    
    except Exception as e:
        logger.error(f"WebSocket error: {e}")
    
    finally:
        manager.disconnect(appointment_id, user_id)

async def verify_appointment_access(appointment_id: str, user_id: str) -> bool:
    """Verify user has access to appointment"""
    # Query database
    appointment = await db.get_appointment(appointment_id)
    if not appointment:
        return False
    
    # Check if user is provider or patient
    return user_id in [appointment['provider_id'], appointment['patient_id']]

async def log_message(appointment_id: str, user_id: str, text: str):
    """Log chat message for compliance"""
    await db.insert_message({
        'appointment_id': appointment_id,
        'user_id': user_id,
        'text': text,
        'timestamp': datetime.now(),
        'encrypted': True
    })

Patient Management System

Appointment Scheduling

from datetime import datetime, timedelta
from enum import Enum
import uuid

class AppointmentStatus(Enum):
    SCHEDULED = "scheduled"
    CONFIRMED = "confirmed"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    CANCELLED = "cancelled"
    NO_SHOW = "no_show"

class AppointmentService:
    """Manage telemedicine appointments"""
    
    def __init__(self, db_connection):
        self.db = db_connection
    
    async def create_appointment(self, patient_id: str, provider_id: str,
                                appointment_time: datetime,
                                reason: str,
                                duration_minutes: int = 30) -> dict:
        """Create new appointment"""
        
        # Verify provider availability
        if not await self.check_provider_availability(provider_id, appointment_time, duration_minutes):
            raise ValueError("Provider not available at this time")
        
        # Verify patient consent
        if not await self.verify_patient_consent(patient_id):
            raise ValueError("Patient has not provided consent")
        
        appointment = {
            'id': str(uuid.uuid4()),
            'patient_id': patient_id,
            'provider_id': provider_id,
            'appointment_time': appointment_time,
            'duration_minutes': duration_minutes,
            'reason': reason,
            'status': AppointmentStatus.SCHEDULED.value,
            'created_at': datetime.now(),
            'video_room_id': str(uuid.uuid4()),
            'notes': '',
            'prescription_ids': []
        }
        
        # Store in database
        await self.db.insert('appointments', appointment)
        
        # Send notifications
        await self.send_appointment_notification(patient_id, provider_id, appointment)
        
        # Log for audit
        await self.audit_log('appointment_created', appointment['id'], patient_id)
        
        return appointment
    
    async def check_provider_availability(self, provider_id: str,
                                         appointment_time: datetime,
                                         duration_minutes: int) -> bool:
        """Check if provider is available"""
        
        # Query existing appointments
        existing = await self.db.query(
            """SELECT * FROM appointments 
               WHERE provider_id = ? 
               AND status IN ('scheduled', 'confirmed', 'in_progress')
               AND appointment_time <= ? 
               AND DATE_ADD(appointment_time, INTERVAL duration_minutes MINUTE) > ?""",
            (provider_id, appointment_time, appointment_time)
        )
        
        return len(existing) == 0
    
    async def verify_patient_consent(self, patient_id: str) -> bool:
        """Verify patient has given consent"""
        consent = await self.db.query(
            "SELECT * FROM patient_consents WHERE patient_id = ? AND is_active = 1",
            (patient_id,)
        )
        return len(consent) > 0
    
    async def start_appointment(self, appointment_id: str) -> dict:
        """Start appointment and generate video room"""
        
        appointment = await self.db.get('appointments', appointment_id)
        if not appointment:
            raise ValueError("Appointment not found")
        
        # Update status
        appointment['status'] = AppointmentStatus.IN_PROGRESS.value
        appointment['started_at'] = datetime.now()
        
        await self.db.update('appointments', appointment_id, appointment)
        
        # Log for audit
        await self.audit_log('appointment_started', appointment_id, appointment['provider_id'])
        
        return {
            'appointment_id': appointment_id,
            'video_room_id': appointment['video_room_id'],
            'patient_id': appointment['patient_id'],
            'provider_id': appointment['provider_id']
        }
    
    async def end_appointment(self, appointment_id: str, 
                             notes: str = '') -> dict:
        """End appointment and save notes"""
        
        appointment = await self.db.get('appointments', appointment_id)
        if not appointment:
            raise ValueError("Appointment not found")
        
        # Update status
        appointment['status'] = AppointmentStatus.COMPLETED.value
        appointment['ended_at'] = datetime.now()
        appointment['notes'] = notes
        
        await self.db.update('appointments', appointment_id, appointment)
        
        # Generate summary
        summary = await self.generate_appointment_summary(appointment)
        
        # Log for audit
        await self.audit_log('appointment_completed', appointment_id, appointment['provider_id'])
        
        return summary
    
    async def generate_appointment_summary(self, appointment: dict) -> dict:
        """Generate appointment summary for medical record"""
        
        return {
            'appointment_id': appointment['id'],
            'date': appointment['appointment_time'].date(),
            'duration_minutes': appointment['duration_minutes'],
            'provider_id': appointment['provider_id'],
            'patient_id': appointment['patient_id'],
            'reason': appointment['reason'],
            'notes': appointment['notes'],
            'prescriptions': appointment.get('prescription_ids', []),
            'follow_up_date': None  # Set by provider
        }
    
    async def send_appointment_notification(self, patient_id: str, 
                                           provider_id: str,
                                           appointment: dict):
        """Send appointment notifications"""
        
        # Send to patient
        await self.send_notification(
            patient_id,
            f"Appointment scheduled with {appointment['provider_id']} on {appointment['appointment_time']}"
        )
        
        # Send to provider
        await self.send_notification(
            provider_id,
            f"New appointment with patient {appointment['patient_id']} on {appointment['appointment_time']}"
        )
    
    async def audit_log(self, action: str, appointment_id: str, user_id: str):
        """Log action for compliance"""
        await self.db.insert('audit_logs', {
            'action': action,
            'appointment_id': appointment_id,
            'user_id': user_id,
            'timestamp': datetime.now(),
            'ip_address': None  # Set from request context
        })

# Usage
service = AppointmentService(db)

appointment = await service.create_appointment(
    patient_id='patient_123',
    provider_id='provider_456',
    appointment_time=datetime.now() + timedelta(days=1),
    reason='Follow-up consultation',
    duration_minutes=30
)

print(f"Appointment created: {appointment['id']}")

HIPAA Compliance Implementation

Encryption & Access Control

from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2
import os
import base64

class HIPAACompliantStorage:
    """Store patient data with HIPAA compliance"""
    
    def __init__(self, master_key: str):
        # Derive encryption key from master key
        kdf = PBKDF2(
            algorithm=hashes.SHA256(),
            length=32,
            salt=b'telemedicine_salt',
            iterations=100000,
        )
        key = base64.urlsafe_b64encode(kdf.derive(master_key.encode()))
        self.cipher = Fernet(key)
    
    def encrypt_patient_data(self, patient_data: dict) -> dict:
        """Encrypt sensitive patient data"""
        
        sensitive_fields = ['ssn', 'date_of_birth', 'address', 'phone', 'email']
        encrypted_data = patient_data.copy()
        
        for field in sensitive_fields:
            if field in encrypted_data:
                value = str(encrypted_data[field])
                encrypted_value = self.cipher.encrypt(value.encode())
                encrypted_data[field] = encrypted_value.decode()
        
        return encrypted_data
    
    def decrypt_patient_data(self, encrypted_data: dict) -> dict:
        """Decrypt patient data"""
        
        sensitive_fields = ['ssn', 'date_of_birth', 'address', 'phone', 'email']
        decrypted_data = encrypted_data.copy()
        
        for field in sensitive_fields:
            if field in decrypted_data:
                try:
                    encrypted_value = decrypted_data[field].encode()
                    decrypted_value = self.cipher.decrypt(encrypted_value)
                    decrypted_data[field] = decrypted_value.decode()
                except Exception as e:
                    logger.error(f"Decryption error for {field}: {e}")
        
        return decrypted_data

class RoleBasedAccessControl:
    """Implement RBAC for healthcare roles"""
    
    ROLES = {
        'patient': {
            'permissions': ['view_own_records', 'schedule_appointment', 'message_provider']
        },
        'provider': {
            'permissions': ['view_patient_records', 'create_prescription', 'view_appointments']
        },
        'nurse': {
            'permissions': ['view_patient_records', 'update_vitals', 'message_patient']
        },
        'admin': {
            'permissions': ['view_all_records', 'manage_users', 'view_audit_logs']
        }
    }
    
    def __init__(self, db_connection):
        self.db = db_connection
    
    async def check_permission(self, user_id: str, resource_id: str,
                              action: str) -> bool:
        """Check if user has permission for action"""
        
        # Get user role
        user = await self.db.get('users', user_id)
        if not user:
            return False
        
        role = user['role']
        
        # Check if role has permission
        if action not in self.ROLES.get(role, {}).get('permissions', []):
            return False
        
        # Check resource-specific access
        if action == 'view_patient_records':
            return await self.check_patient_access(user_id, resource_id)
        
        return True
    
    async def check_patient_access(self, user_id: str, patient_id: str) -> bool:
        """Check if user can access patient records"""
        
        user = await self.db.get('users', user_id)
        
        # Patient can access own records
        if user['role'] == 'patient' and user_id == patient_id:
            return True
        
        # Provider can access if they have active appointment
        if user['role'] == 'provider':
            appointment = await self.db.query(
                """SELECT * FROM appointments 
                   WHERE provider_id = ? AND patient_id = ? 
                   AND status IN ('scheduled', 'confirmed', 'in_progress', 'completed')""",
                (user_id, patient_id)
            )
            return len(appointment) > 0
        
        # Admin can access all
        if user['role'] == 'admin':
            return True
        
        return False

# Usage
storage = HIPAACompliantStorage(master_key='your_master_key')
rbac = RoleBasedAccessControl(db)

# Encrypt patient data
patient_data = {
    'name': 'John Doe',
    'ssn': '123-45-6789',
    'date_of_birth': '1990-01-01',
    'email': '[email protected]'
}

encrypted = storage.encrypt_patient_data(patient_data)
print(f"Encrypted: {encrypted}")

# Check access
has_access = await rbac.check_permission('provider_123', 'patient_456', 'view_patient_records')
print(f"Has access: {has_access}")

Best Practices

  1. End-to-End Encryption: Encrypt all patient communications
  2. Audit Logging: Log all access to patient data
  3. Access Control: Implement strict RBAC
  4. Data Minimization: Only collect necessary data
  5. Secure APIs: Use OAuth2 and API keys
  6. Regular Backups: Backup patient data securely
  7. Incident Response: Have plan for data breaches
  8. Staff Training: Train staff on HIPAA compliance
  9. Vendor Management: Vet third-party vendors
  10. Regular Audits: Conduct security audits

Common Pitfalls

  1. Unencrypted Data: Storing patient data in plaintext
  2. No Audit Logs: Unable to track data access
  3. Weak Access Control: Allowing unauthorized access
  4. Poor Video Security: Unencrypted video streams
  5. No Backup Plan: Data loss without recovery
  6. Ignoring Compliance: Not following HIPAA rules
  7. Poor Integration: Insecure EHR integration
  8. No Monitoring: Unable to detect breaches
  9. Weak Authentication: Easy to guess passwords
  10. No Incident Plan: Unprepared for emergencies

Compliance Checklist

  • โœ… End-to-end encryption for all communications
  • โœ… Role-based access control implemented
  • โœ… Audit logging for all data access
  • โœ… Patient consent management
  • โœ… Secure video streaming (WebRTC with DTLS)
  • โœ… Regular security audits
  • โœ… Incident response plan
  • โœ… Staff HIPAA training
  • โœ… Business associate agreements
  • โœ… Regular backups and disaster recovery

External Resources


Conclusion

Building compliant telemedicine platforms requires careful attention to security, privacy, and regulatory requirements. By implementing the architecture and practices in this guide, you can create a secure, scalable platform that meets HIPAA requirements while providing excellent patient care. The key is treating security and compliance as core features, not afterthoughts.

Next Steps:

  1. Design secure architecture
  2. Implement encryption and access control
  3. Set up audit logging
  4. Conduct security audit
  5. Deploy and monitor

Comments