UNPKG

@kodeglot/node-python-eid-reader

Version:

A cross-platform Node.js package to read Belgian eID card public data using Python eidreader with automatic dependency checking

664 lines (514 loc) 18.8 kB
# Belgian eID Reader [![npm version](https://img.shields.io/npm/v/@kodeglot/node-python-eid-reader.svg?logo=npm)](https://www.npmjs.com/package/@kodeglot/node-python-eid-reader) A cross-platform Node.js package to read Belgian eID card public data using Python eidreader with automatic dependency checking. Perfect for Electron apps and Node.js applications. ## Features - 🔍 **Cross-platform support**: macOS, Windows, and Linux - 🐍 **Python integration**: Uses the reliable eidreader Python package - 🔧 **Automatic setup**: Checks and installs missing dependencies - 🛡️ **Error handling**: Comprehensive error handling with specific error codes - ⚡ **Configurable**: Customizable retry logic and paths - 📦 **npm package**: Easy to install and use in any Node.js project - 🖥️ **Electron ready**: Works seamlessly in Electron applications - 🔄 **Real-time detection**: Instant PC/SC reader and card insertion/removal detection - 📡 **Event-driven**: Listen for reader and card events in real-time - 🎯 **Dual API**: Both traditional eID reading and modern PC/SC detection ## Installation ```bash npm install @kodeglot/node-python-eid-reader ``` ## Prerequisites Before using this package, you need to install: 1. **Python 3.x** - [Download from python.org](https://www.python.org/downloads/) 2. **Belgian eID middleware** - [Download from Belgian government](https://eid.belgium.be/en/using-your-eid/installing-eid-software) 3. **eidreader Python package** - Will be installed automatically ## Quick Start ### Basic Usage ```javascript import { readEidData } from '@kodeglot/node-python-eid-reader'; try { const eidData = await readEidData(); console.log('Card holder:', eidData.firstName, eidData.name); console.log('Card number:', eidData.cardNumber); } catch (error) { console.error('Failed to read eID:', error.message); } ``` ### Advanced Usage with Options ```javascript import { EidReader } from '@kodeglot/node-python-eid-reader'; const reader = new EidReader({ verbose: true, // Enable detailed logging maxRetries: 5, // Custom retry attempts retryDelay: 500, // Custom retry delay (ms) pythonPath: '/usr/bin/python3', // Custom Python path pkcs11LibPath: '/custom/path/libbeidpkcs11.dylib' // Custom PKCS#11 library }); try { const eidData = await reader.readEidData(); console.log('Success:', eidData); } catch (error) { console.error('Error:', error.message, error.code); } ``` ### Requirements Management ```javascript import { checkRequirements, installRequirements } from '@kodeglot/node-python-eid-reader'; // Check if all requirements are met const result = await checkRequirements(); // NOTE: In Electron/Node.js integration, check result.data.passed, not result.passed if (result.data && result.data.passed) { console.log('All requirements are met!'); } else { console.log('Missing requirements:', result.data?.results); // Attempt to install missing requirements const installResult = await installRequirements(); if (installResult.success) { if (installResult.data && installResult.data.installed) { console.log('Requirements installed successfully!'); } else { console.log('All requirements are already available!'); } } else { console.log('Failed to install requirements:', installResult.info); if (installResult.error) { console.log('Error:', installResult.error.message); } } } ``` ## API Reference ### Real-Time PC/SC Detection (New!) The package now includes **real-time smart card reader and card detection** using the cross-platform PC/SC subsystem. This provides instant detection of reader insertion/removal and card insertion/removal events. #### Quick Start - Real-Time Detection ```javascript import { createPcscDetector } from '@kodeglot/node-python-eid-reader'; // Create a detector for real-time monitoring const detector = await createPcscDetector(); // Listen for reader events detector.on('reader-connected', (reader) => { console.log('Reader connected:', reader.name); }); detector.on('reader-removed', (reader) => { console.log('Reader removed:', reader.name); }); // Listen for card events detector.on('card-inserted', (reader) => { console.log('Card inserted in:', reader.name); }); detector.on('card-removed', (reader) => { console.log('Card removed from:', reader.name); }); // Get current status const status = detector.getStatus(); console.log('Current readers:', status.readers); // Clean up when done detector.close(); ``` #### Convenience Functions ```javascript import { isPcscReaderAvailable, isPcscCardInserted, getPcscStatus } from '@kodeglot/node-python-eid-reader'; // Check if any reader is available const readerResult = await isPcscReaderAvailable(); if (readerResult.success && readerResult.data?.available) { console.log('PC/SC reader is available'); } // Check if any card is inserted const cardResult = await isPcscCardInserted(); if (cardResult.success && cardResult.data?.inserted) { console.log('Card is inserted in:', cardResult.data.readerName); } // Get detailed status const statusResult = await getPcscStatus(); if (statusResult.success) { console.log('All readers:', statusResult.data?.readers); } ``` #### PcscDetector Class The main class for real-time PC/SC monitoring. ```javascript import { PcscDetector } from '@kodeglot/node-python-eid-reader'; const detector = new PcscDetector(); // Events detector.on('reader-connected', (reader: PcscReaderStatus) => { // Reader was connected }); detector.on('reader-removed', (reader: PcscReaderStatus) => { // Reader was removed }); detector.on('card-inserted', (reader: { name: string }) => { // Card was inserted }); detector.on('card-removed', (reader: { name: string }) => { // Card was removed }); // Methods const status = detector.getStatus(); // Get current status detector.close(); // Clean up resources ``` #### Types ```typescript interface PcscReaderStatus { name: string; // Reader name/identifier cardPresent: boolean; // Whether a card is currently inserted } interface PcscStatus { readers: PcscReaderStatus[]; // List of all connected readers } ``` ### Traditional eID Reading API ### `readEidData(options?)` Convenience function to read eID data with default options. **Parameters:** - `options` (optional): `EidReaderOptions` - Configuration options **Returns:** `Promise<EidData>` - The eID card data ### `EidReader` Class Main class for reading eID data with full control over options. #### Constructor ```javascript new EidReader(options?: EidReaderOptions) ``` **Options:** - `verbose?: boolean` - Enable verbose logging (default: false) - `maxRetries?: number` - Maximum retry attempts (default: 3) - `retryDelay?: number` - Delay between retries in ms (default: 300) - `pythonPath?: string` - Custom Python interpreter path - `pkcs11LibPath?: string` - Custom PKCS#11 library path #### Methods - `readEidData(): Promise<EidData>` - Read eID data from the card - `checkRequirements(): Promise<{passed: boolean, results: any}>` - Check if requirements are met - `installRequirements(): Promise<ReaderResult<{installed: boolean}>>` - Install missing requirements ### `EidData` Interface ```typescript interface EidData { cardNumber: string; // Card number (unique identifier) chipNumber: string; // Chip number validityDateBegin: string; // Card validity start date validityDateEnd: string; // Card validity end date municipality: string; // Issuing municipality nationality: string; // Nationality birthLocation: string; // Birth location birthDate: string; // Birth date name: string; // Last name firstName: string; // First name(s) sex: string; // Gender documentType: string; // Document type address: { streetAndNumber: string; // Street and number zip: string; // ZIP code municipality: string; // Municipality }; photo: string; // Base64 encoded photo } ``` ### `EidReaderError` Class Custom error class with error codes for better error handling. **Error Codes:** - `REQUIREMENTS_NOT_MET` - Missing dependencies - `PYTHON_NOT_FOUND` - Python interpreter not found - `EIDREADER_FAILED` - Python eidreader script failed - `SPAWN_ERROR` - Failed to start Python process - `MAX_RETRIES_EXCEEDED` - Max retry attempts exceeded - `INVALID_OUTPUT` - Invalid output from eidreader - `PARSE_ERROR` - Failed to parse eID data ## Electron Integration This package works seamlessly in Electron applications with built-in enable/disable functionality and **real-time PC/SC detection**. Here's an example: ### Real-Time Detection in Electron ```javascript // main.js (Electron main process) import { createPcscDetector } from '@kodeglot/node-python-eid-reader'; let pcscDetector = null; let eidReaderEnabled = true; // Initialize PC/SC detection async function initializePcscDetection() { if (eidReaderEnabled) { pcscDetector = await createPcscDetector(); pcscDetector.on('reader-connected', (reader) => { mainWindow.webContents.send('reader-state-changed', { readerAvailable: true, cardInserted: false, cardInfo: null }); }); pcscDetector.on('reader-removed', () => { mainWindow.webContents.send('reader-state-changed', { readerAvailable: false, cardInserted: false, cardInfo: null }); }); pcscDetector.on('card-inserted', (reader) => { mainWindow.webContents.send('reader-state-changed', { readerAvailable: true, cardInserted: true, cardInfo: null }); }); pcscDetector.on('card-removed', () => { mainWindow.webContents.send('reader-state-changed', { readerAvailable: true, cardInserted: false, cardInfo: null }); }); } } // Handle enable/disable eID reader ipcMain.handle('set-eid-enabled', async (event, enabled) => { eidReaderEnabled = enabled; if (enabled) { await initializePcscDetection(); } else { if (pcscDetector) { pcscDetector.close(); pcscDetector = null; } } return { success: true, enabled: eidReaderEnabled }; }); // Get current PC/SC status ipcMain.handle('get-pcsc-status', async () => { if (!pcscDetector) { return { success: false, error: 'PC/SC detector not initialized' }; } const status = pcscDetector.getStatus(); const readerAvailable = status.readers.length > 0; const cardInserted = status.readers.some(reader => reader.cardPresent); return { success: true, data: { readerAvailable, cardInserted, readers: status.readers } }; }); ``` ```javascript // renderer.js (Electron renderer process) const { ipcRenderer } = require('electron'); // Listen for real-time reader/card state changes ipcRenderer.on('reader-state-changed', (event, data) => { console.log('Reader state changed:', data); updateUI(data.readerAvailable, data.cardInserted); }); // Get initial status async function getInitialStatus() { try { const result = await ipcRenderer.invoke('get-pcsc-status'); if (result.success) { updateUI(result.data.readerAvailable, result.data.cardInserted); } } catch (error) { console.error('Failed to get PC/SC status:', error); } } function updateUI(readerAvailable, cardInserted) { // Update your UI based on reader and card status document.getElementById('reader-status').textContent = readerAvailable ? 'Available' : 'Not Available'; document.getElementById('card-status').textContent = cardInserted ? 'Inserted' : 'Not Inserted'; } ``` ### Traditional eID Reading in Electron This package works seamlessly in Electron applications with built-in enable/disable functionality. Here's an example: ```javascript // main.js (Electron main process) import { readEidData } from '@kodeglot/node-python-eid-reader'; let eidReaderEnabled = true; // Default to enabled // Handle eID reading with enable/disable check ipcMain.handle('read-eid', async (event) => { if (!eidReaderEnabled) { return { success: false, error: 'eID reader is disabled', code: 'DISABLED' }; } try { const eidData = await readEidData({ verbose: true }); return { success: true, data: eidData }; } catch (error) { return { success: false, error: error.message, code: error.code }; } }); // Handle enable/disable eID reader ipcMain.handle('set-eid-enabled', async (event, enabled) => { eidReaderEnabled = enabled; return { success: true, enabled: eidReaderEnabled }; }); // Handle get eID reader status ipcMain.handle('get-eid-status', async () => { return { success: true, enabled: eidReaderEnabled }; }); ``` ```javascript // renderer.js (Electron renderer process) const { ipcRenderer } = require('electron'); async function readEID() { try { const result = await ipcRenderer.invoke('read-eid'); if (result.success) { console.log('eID data:', result.data); } else if (result.code === 'DISABLED') { console.log('eID reader is disabled'); } else { console.error('Failed:', result.error); } } catch (error) { console.error('IPC error:', error); } } async function toggleEidReader(enabled) { try { const result = await ipcRenderer.invoke('set-eid-enabled', enabled); if (result.success) { console.log(`eID reader ${enabled ? 'enabled' : 'disabled'}`); } } catch (error) { console.error('Toggle error:', error); } } async function getEidStatus() { try { const result = await ipcRenderer.invoke('get-eid-status'); if (result.success) { console.log(`eID reader is ${result.enabled ? 'enabled' : 'disabled'}`); } } catch (error) { console.error('Status error:', error); } } ``` ### Enable/Disable Features The Electron integration includes: - **Toggle Switch**: Visual toggle to enable/disable the eID reader - **Status Indicator**: Shows current enabled/disabled state - **Automatic Blocking**: Reading operations are blocked when disabled - **State Persistence**: Status is maintained during the app session - **Error Handling**: Proper error codes for disabled state ### Robust Requirements Check in Electron UI - The requirements check result is an object with a `data` property. - To check if all requirements are met, use `result.data.passed` in your UI code. - Example: ```js if (result.success) { if (result.data && result.data.passed) { // All requirements are met } else { // Some requirements are missing } } ``` - This ensures your UI accurately reflects the backend requirements check result. ### Improved Requirements Installation The `installRequirements()` function now provides better feedback: - **All requirements available**: Returns success with "All requirements are already available!" - **Requirements installed**: Returns success with "Requirements installed successfully!" - **Installation failed**: Returns failure with detailed info and error messages Example handling in your UI: ```js const result = await installRequirements(); if (result.success) { if (result.data && result.data.installed) { console.log('Requirements installed successfully!'); } else { console.log('All requirements are already available!'); } } else { console.log('Failed to install requirements:', result.info); if (result.error) { console.log('Error:', result.error.message); } } ``` ## Examples The package includes TypeScript examples demonstrating different usage patterns: ### Node.js Example ```bash npm run example:node ``` ### Electron Example ```bash npm run example:electron ``` The examples are written in TypeScript and show: - Basic usage with the convenience function - Advanced usage with the `EidReader` class - Error handling and requirements management - Electron integration with IPC handlers ## Troubleshooting ### Common Issues 1. **"Python not found"** - Ensure Python 3.x is installed and in your PATH - Use the `pythonPath` option to specify a custom Python path 2. **"eidreader not found"** - The package will attempt to install it automatically - Manual installation: `pip install eidreader` 3. **"Middleware not properly installed"** - Install Belgian eID middleware from [eid.belgium.be](https://eid.belgium.be) - On macOS, ensure the middleware is in `/Library/Belgium Identity Card/` - On Windows, ensure the middleware is in `C:\Program Files\Belgium Identity Card\` or `C:\Program Files (x86)\Belgium Identity Card\` 4. **"Device error"** - Ensure the eID card is properly inserted - Try removing and reinserting the card - Check if the card reader is working 5. **"Permission denied"** - On macOS/Linux, you may need to run with sudo for middleware access - The package will prompt for password when needed ### Debug Mode Enable verbose logging to see detailed information: ```javascript const reader = new EidReader({ verbose: true }); const eidData = await reader.readEidData(); ``` ### Environment Variables You can set these environment variables to customize behavior: - `PYKCS11LIB` - Path to PKCS#11 library - `DYLD_LIBRARY_PATH` - Library path (macOS) - `PYTHONPATH` - Python module search path ## Platform-Specific Notes ### macOS - Middleware typically installed in `/Library/Belgium Identity Card/` - May require sudo for first-time access - DYLD_LIBRARY_PATH is automatically configured ### Windows - Middleware typically installed in `C:\Program Files\Belgium Identity Card\` - Ensure the middleware is properly registered - May require running as administrator ### Linux - Middleware typically installed in `/usr/lib/` - May need to install additional packages: `sudo apt-get install libbeidpkcs11-0` ## Development ```bash # Clone the repository git clone https://github.com/Kodeglot/Node-Python-EID-Reader.git cd Node-Python-EID-Reader # Install dependencies npm install # Build the package npm run build # Run tests npm start # Check requirements npm run check # Run examples npm run example:node npm run example:electron ``` ## License MIT License - see [LICENSE](LICENSE) file for details. ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. ## Support If you encounter any issues: 1. Check the [troubleshooting section](#troubleshooting) 2. Search existing [issues](https://github.com/Kodeglot/Node-Python-EID-Reader/issues) 3. Create a new issue with detailed information about your problem ## Changelog ### 1.0.0 - Initial release - Cross-platform support - Automatic dependency checking - Electron integration - Comprehensive error handling