Skip to main content

Overview

Salud uses QR codes as a convenient and secure method to share medical records with healthcare providers. QR codes encode all necessary information for access verification and record decryption.
QR codes contain encrypted data and access tokens - they are safe to display on your phone screen during appointments.

QR Code Data Structure

Each QR code contains the following JSON data:
interface QRCodeData {
  version: number              // Protocol version (currently 1)
  accessToken: string          // Cryptographic access token
  recordId: string             // Unique record identifier
  patientAddress: string       // Your Aleo wallet address
  expiresAt: number            // Unix timestamp of expiration
  encryptedViewKey?: string    // Encrypted with doctor's public key
  encryptedData?: string       // The encrypted record data
}

Version Control

Protocol version ensures compatibility across updates

Access Token

Cryptographic proof verified on blockchain

Encrypted View Key

Allows doctor to decrypt the medical data

Expiration

Built-in time limit prevents stale access

Generating QR Codes

Step-by-Step Process

1

Select Record

From the Records page, click on the record you want to share
2

Click Share

Click the “Share” button to open the share modal
3

Configure Access

  • Enter doctor’s Aleo address (optional)
  • Select access duration (1 hour to 7 days)
4

Generate QR Code

Click “Generate QR Code” to create the shareable QR code
5

Share with Doctor

Show the QR code on your screen or download it as an image

QR Code Generation Example

Here’s how QR codes are generated in the application:
// From ShareRecordModal.tsx:175-185
const qrData: QRCodeData = {
  version: 1,
  accessToken,
  recordId: record.recordId,
  patientAddress: user.address,
  expiresAt: expiresAt?.getTime() || 0,
  encryptedViewKey,
  encryptedData: record.data,
};

// Generate QR code from JSON data
<QRCodeSVG
  value={JSON.stringify(qrData)}
  size={200}
  level="H"  // High error correction
  includeMargin
/>
QR codes use high error correction (Level H) to ensure they remain scannable even if partially damaged or obscured.

QR Code Features

Visual Elements

200x200 pixels

Standard size optimized for mobile scanning

High Error Correction

Can recover from 30% data loss or damage

Margin Included

Built-in quiet zone for reliable scanning

Optional Logo

Can embed Salud logo in center

Live Countdown Timer

QR codes display a real-time countdown showing remaining access time:
// From ShareRecordModal.tsx:72-99
useEffect(() => {
  if (!expiresAt) return;

  const updateCountdown = () => {
    const now = new Date();
    const diff = expiresAt.getTime() - now.getTime();

    if (diff <= 0) {
      setCountdown('Expired');
      return;
    }

    const hours = Math.floor(diff / (1000 * 60 * 60));
    const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
    const seconds = Math.floor((diff % (1000 * 60)) / 1000);

    if (hours > 0) {
      setCountdown(`${hours}h ${minutes}m ${seconds}s`);
    } else if (minutes > 0) {
      setCountdown(`${minutes}m ${seconds}s`);
    } else {
      setCountdown(`${seconds}s`);
    }
  };

  updateCountdown();
  const interval = setInterval(updateCountdown, 1000);
  return () => clearInterval(interval);
}, [expiresAt]);

Scanning QR Codes

Doctor Portal Scanner

Healthcare providers use the Doctor Portal to scan patient QR codes:
1

Navigate to Doctor Portal

Click “Doctor Portal” in the main navigation
2

Connect Wallet

Doctor must connect their Aleo wallet first
3

Start Scanner

Click “Start Scanning” to activate the camera
4

Scan QR Code

Position the patient’s QR code within the scanner frame
5

View Record

Access is verified on blockchain and record is displayed

Scanner Implementation

The scanner uses the html5-qrcode library for camera access:
// From DoctorQRScanner.tsx:53-79
const startScanner = async () => {
  setError(null);
  setScanStatus('scanning');

  try {
    const scanner = new Html5Qrcode('qr-reader');
    scannerRef.current = scanner;

    await scanner.start(
      { facingMode: 'environment' },  // Use back camera
      {
        fps: 10,                      // 10 frames per second
        qrbox: { width: 250, height: 250 },  // Scan area size
        aspectRatio: 1,               // Square aspect ratio
      },
      onScanSuccess,
      onScanFailure
    );

    setCameraPermission(true);
  } catch (err) {
    console.error('Scanner error:', err);
    setCameraPermission(false);
    setError('Unable to access camera. Please grant camera permissions.');
    setScanStatus('idle');
  }
};
Camera permissions must be granted for QR code scanning. The browser will prompt for permission on first use.

Scan Verification Process

When a QR code is scanned, the system performs several checks:
The QR code JSON is parsed and validated for required fields
const data: QRCodeData = JSON.parse(decodedText);
if (!data.accessToken || !data.recordId || !data.patientAddress) {
  throw new Error('Invalid QR code format');
}
Verify the access hasn’t expired based on the embedded timestamp
if (data.expiresAt < Date.now()) {
  throw new Error('This access token has expired');
}
The access token is verified against the Aleo blockchain using verify_access
Doctor’s private key decrypts the view key from the QR code
const viewKey = decryptWithPrivateKey(
  data.encryptedViewKey, 
  doctorAddress
);
The decrypted view key is used to access the medical record

QR Code Actions

Copy Access Token

The access token can be copied as text:
// From ShareRecordModal.tsx:143-150
const handleCopyToken = async () => {
  if (!accessToken) return;
  const success = await copyToClipboard(accessToken);
  if (success) {
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  }
};

Download QR Code

QR codes can be downloaded as PNG images:
// From ShareRecordModal.tsx:152-173
const handleDownloadQR = () => {
  const svg = document.getElementById('qr-code-svg');
  if (!svg) return;

  const svgData = new XMLSerializer().serializeToString(svg);
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const img = new Image();

  img.onload = () => {
    canvas.width = img.width;
    canvas.height = img.height;
    ctx?.drawImage(img, 0, 0);
    const pngFile = canvas.toDataURL('image/png');
    const downloadLink = document.createElement('a');
    downloadLink.download = `salud-qr-${title}.png`;
    downloadLink.href = pngFile;
    downloadLink.click();
  };

  img.src = 'data:image/svg+xml;base64,' + btoa(svgData);
};
Downloaded QR codes can be printed or sent electronically to the healthcare provider ahead of appointments.

Scanner States

The QR scanner has five different states:

Idle

Scanner is ready to start - shows “Start Scanning” button

Scanning

Camera is active and looking for QR codes

Verifying

QR code found, verifying access on blockchain

Success

Access verified, displaying medical record

Error

Verification failed or QR code invalid

Security Considerations

QR Code Safety

Time-Limited

QR codes automatically expire after the specified duration

One-Time Use

Each QR code contains a unique access token

Blockchain Verified

Access verification happens on-chain, can’t be forged

Encrypted Data

Medical data in QR code is encrypted, requires view key

Best Practices

1

Display Only When Needed

Don’t leave QR codes visible longer than necessary
2

Verify Doctor First

Confirm you’re sharing with the correct healthcare provider
3

Use Short Durations

Select the minimum duration needed for the appointment
4

Delete Downloaded QR Codes

Remove saved QR code images after use
Never share QR code screenshots publicly or on social media. Anyone with the QR code can access the medical record during its valid period.

Doctor View After Scanning

After successful verification, doctors see:
  • Success Badge: Green confirmation that access is verified
  • Record Title: Name of the medical record
  • Record Type: Category badge (Lab Results, Prescription, etc.)
  • Record Description: Full medical information
  • Patient Address: Truncated Aleo address of the patient
  • Expiration Time: When access will expire
  • Access Token: The cryptographic token used for verification
  • Privacy Notice: Information about blockchain security
// From DoctorQRScanner.tsx:263-348
{scanStatus === 'success' && recordData && (
  <motion.div className="space-y-4">
    {/* Success badge */}
    <div className="flex items-center justify-center gap-2 rounded-lg bg-success-50 p-4">
      <CheckCircle2 className="text-success-600" />
      <span className="font-medium text-success-900">
        Access Verified Successfully
      </span>
    </div>

    {/* Record details */}
    <div className="rounded-xl border border-slate-200 bg-white p-6">
      <div className="mb-4 flex items-start gap-4">
        <div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-primary-100">
          <FileText className="text-primary-600" />
        </div>
        <div>
          <h3 className="font-semibold text-slate-900">
            {recordData.title}
          </h3>
          <Badge variant="primary" size="sm" className="mt-1">
            {RECORD_TYPES[recordData.recordType].name}
          </Badge>
        </div>
      </div>

      <p className="mb-4 text-sm text-slate-600">
        {recordData.description}
      </p>

      {/* Metadata */}
      <div className="space-y-2 border-t border-slate-100 pt-4">
        <div className="flex items-center justify-between text-sm">
          <span className="flex items-center gap-2 text-slate-500">
            <User size={14} />
            Patient
          </span>
          <span className="font-mono text-xs text-slate-700">
            {truncateAddress(recordData.patientAddress, 8, 6)}
          </span>
        </div>
        <div className="flex items-center justify-between text-sm">
          <span className="flex items-center gap-2 text-slate-500">
            <Clock size={14} />
            Access Expires
          </span>
          <span className="text-slate-700">
            {formatDateTime(recordData.expiresAt)}
          </span>
        </div>
      </div>
    </div>
  </motion.div>
)}

Troubleshooting

  • Ensure good lighting and steady camera position
  • Check that camera permissions are granted
  • Try adjusting distance between camera and QR code
  • Clean phone camera lens if blurry
  • Verify the QR code is from Salud Health app
  • Check if the QR code image is corrupted
  • Ensure the QR code hasn’t expired
  • Try generating a new QR code
  • Check if access has been revoked by patient
  • Verify you’re using the correct Aleo wallet address
  • Confirm the QR code hasn’t expired
  • Ensure blockchain connection is active
  • Grant camera permissions in browser settings
  • Check that no other app is using the camera
  • Try a different browser (Chrome/Safari recommended)
  • Restart the browser and try again

Next Steps

Access Sharing

Learn more about access grants and permissions

Privacy & Security

Understand the security model

Build docs developers (and LLMs) love