Secure, ephemeral MFA secret key sharing service for Cloud Phone devices, with or without a camera.
- Sign up for the Developer Program
- Enable Developer Mode on your Cloud Phone device
- Add this widget with the URL: https://mfa.cloudphone.tech/sync.html and icon
- Visit https://mfa.cloudphone.tech/ on your desktop or mobile (Android or iOS) and scan or enter an MFA secret
- Open MFA Sync on Cloud Phone and enter the PIN
This app processes data in the Key URI format.
otpauth://totp/Zoho:email@zoho.com?secret=JBSWY3DPEHPK3PXP&period=30&digits=6&algorithm=SHA1&issuer=Zoho
See Key Uri Format.
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Phone │ │ │ │ Feature │
│ (Admin) │────────>│ VPS Server │<────────│ Phone │
│ Scan QR Code│ │ + Redis │ │ Enter PIN │
└─────────────┘ └──────────────┘ └─────────────┘
│ │ │
│ │ │
└─── Generate PIN ───────┤ │
│ │
└──── Retrieve Secret ────┘
- ✅ Ephemeral Storage: Secrets auto-delete after retrieval or expiration
- ✅ Encrypted at Rest: AES-256-GCM encryption in Redis
- ✅ Rate Limited: Protection against brute force attacks
- ✅ T9-Friendly: Simple numeric PIN entry for feature phones
- ✅ QR Scanner: Built-in camera scanning for admin interface
- ✅ TOTP Generator: Full authenticator app functionality in browser
- ✅ Zero Dependencies: Client-side uses only Web Crypto API
# Install dependencies
npm install
# Start Redis
docker run -d -p 6379:6379 redis:7-alpine
# Start server
npm start
# Access at http://localhost:3000# Build and run with Docker Compose
docker-compose up -d
# View logs
docker-compose logs -f
# Stop
docker-compose down- VPS with Docker installed
- Domain name (optional, for HTTPS)
- GitHub repository secrets configured
Add these secrets to your GitHub repository:
VPS_HOST=your.server.ip
VPS_USER=your-ssh-user
VPS_SSH_KEY=<your-private-ssh-key>
ALLOWED_ORIGINS=https://mfa.cloudphone.tech# SSH into your VPS
ssh user@your-server
# Install Docker (if not installed)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Create deployment directory
sudo mkdir -p /opt/mfa-sync
sudo chown $USER:$USER /opt/mfa-sync
cd /opt/mfa-sync
# Clone repository
git clone https://github.com/yourusername/mfa-sync.git .
# Create docker-compose.yml
cp docker-compose.yml /opt/mfa-sync/
# Start services
docker-compose up -d# Install certbot
sudo apt-get update
sudo apt-get install certbot
# Generate certificate
sudo certbot certonly --standalone -d mfa.cloudphone.tech
# Copy certificates to nginx directory
sudo mkdir -p /opt/mfa-sync/ssl
sudo cp /etc/letsencrypt/live/mfa.cloudphone.tech/fullchain.pem /opt/mfa-sync/ssl/cert.pem
sudo cp /etc/letsencrypt/live/mfa.cloudphone.tech/privkey.pem /opt/mfa-sync/ssl/key.pem
sudo chown -R $USER:$USER /opt/mfa-sync/ssl
# Restart nginx
docker-compose restart nginxAccess from your smartphone with a camera:
https://mfa.cloudphone.tech.com/
- Click "📷 Scan QR Code"
- Point camera at MFA QR code
- Secret auto-fills, or paste manually
- Click "Generate PIN"
- Display shows 6-digit PIN
Access from your feature phone browser:
https://mfa.cloudphone.tech/sync.html
- Enter the 6-digit PIN from admin interface
- Click "Retrieve Secret" (or press Enter)
- Account added with live TOTP codes
- Codes refresh every 30 seconds
Store an MFA secret and generate PIN.
Request:
{
"secret": "JBSWY3DPEHPK3PXP",
"label": "GitHub",
"issuer": "GitHub",
"ttl": 300
}Response:
{
"pin": "123456",
"expiresIn": 300,
"expiresAt": "2025-10-22T10:15:00.000Z"
}Retrieve MFA secret with PIN (one-time use).
Request:
{
"pin": "123456"
}Response:
{
"secret": "JBSWY3DPEHPK3PXP",
"label": "GitHub",
"issuer": "GitHub",
"type": "totp",
"algorithm": "SHA1",
"digits": 6,
"period": 30
}Health check endpoint.
Response:
{
"status": "healthy",
"redis": "connected"
}✅ Provides secure, temporary exchange of MFA secrets ✅ Encrypts secrets at rest with AES-256-GCM ✅ Automatically deletes secrets after retrieval ✅ Rate limits to prevent brute force attacks ✅ Uses in-memory storage (Redis) with TTL ✅ One-time PIN retrieval only
- ✅ Protects against: Interception, storage leaks, unauthorized access
⚠️ Limited protection: Network-level attacks require HTTPS- ❌ Does not protect: Compromised admin device, malicious VPS
PORT=3000 # Server port
REDIS_URL=redis://localhost:6379 # Redis connection string
ALLOWED_ORIGINS=* # CORS origins (comma-separated)
NODE_ENV=production # Environment
PIN_TTL=120 # Pin Time to Live (TTL) in seconds# Check Redis is running
docker ps | grep redis
# View Redis logs
docker-compose logs redis
# Test Redis connection
docker exec -it mfa-sync-redis-1 redis-cli ping- Check TTL hasn't expired
- Verify PIN is exactly 6-8 digits
- Ensure no spaces in PIN entry
- Check if already retrieved (
/api/check/:pin)
- Ensure HTTPS is enabled (required for camera API)
- Check browser permissions
- Try different browser (Chrome/Safari recommended)
Issues and pull requests welcome!
