⚡ Node.jsNetworking / IoT
MAC Address Validation in Node.js
Validate MAC (Media Access Control) addresses in Node.js — accept colon, hyphen, or compact formats, detect address types (unicast, multicast, broadcast), identify locally-administered (randomised) MACs, and normalise to a canonical form for storage.
Also available in Python · For IMEI + MAC together see the IoT Device Validation guide
Contents
1. MAC address structure
A MAC address is a 48-bit (6-byte) identifier. The first 3 bytes form the OUI (Organizationally Unique Identifier) assigned to the manufacturer; the last 3 bytes are the device serial number.
00 : 1B : 44 : 11 : 3A : B7 ├───────────┤ ├───────────┤ OUI (3 bytes) NIC-specific (3 bytes) Manufacturer Device serial Bit 0 of byte 0: 0 = unicast, 1 = multicast Bit 1 of byte 0: 0 = globally unique (OUI-assigned) 1 = locally administered (may be randomised)
2. API response structure
Endpoint: GET /v0/mac-address?value=…
{ "valid": true, "normalized": "00:1B:44:11:3A:B7", "format": "colon", "type": "unicast", "isMulticast": false, "isLocal": false, "isBroadcast": false }
- Check
valid - Store
normalizedas canonical form (colon-separated uppercase) - Reject
isMulticastandisBroadcastfor device registration - Warn on
isLocal— likely a privacy/randomised MAC
3. Accepted input formats
// All three formats are accepted — normalized is always colon-separated uppercase await iv.macAddress('00:1B:44:11:3A:B7') // colon await iv.macAddress('00-1B-44-11-3A-B7') // hyphen await iv.macAddress('001B44113AB7') // compact await iv.macAddress('00:1b:44:11:3a:b7') // lowercase — also accepted // All return normalized: "00:1B:44:11:3A:B7"
4. Address types
| Type | Example | Suitable for device ID? |
|---|---|---|
| unicast + globally unique | 00:1B:44:11:3A:B7 | ✓ Yes — manufacturer-assigned |
| unicast + locally administered | 02:xx:xx:xx:xx:xx | ⚠️ Warn — may be randomised by OS |
| multicast | 01:xx:xx:xx:xx:xx | ✗ No — group address, not a device |
| broadcast | FF:FF:FF:FF:FF:FF | ✗ No — reserved broadcast address |
5. Locally-administered (randomised) addresses
⚠️iOS 14+, Android 10+, and Windows 10+ randomise Wi-Fi MAC addresses per network by default. If
isLocal: true, the address may change when the user reconnects to a different network or resets their device. Do not use as a stable device identifier without an explicit user opt-out of MAC randomisation.const result = await iv.macAddress(mac) if (result.valid && result.isLocal) { // Log a warning — the user's device may be using MAC randomisation logger.warn({ mac: result.normalized }, 'Locally-administered MAC — may be randomised') // Option: require the user to disable MAC randomisation for this network // and resubmit, or fall back to a different device identifier (IMEI, UUID) }
6. Validation with Promise.all
import { createClient } from '@isvalid-dev/sdk' const iv = createClient({ apiKey: process.env.ISVALID_API_KEY! }) async function validateMac(mac: string) { const result = await iv.macAddress(mac) if (!result.valid) throw new Error(`Invalid MAC address: ${mac}`) if (result.isBroadcast) throw new Error('Broadcast address — not a valid device identifier') if (result.isMulticast) throw new Error('Multicast address — not suitable for device registration') if (result.isLocal) { console.warn(`MAC ${result.normalized} is locally-administered — may be randomised`) } return result } // Register device — validate MAC and normalise before DB insert async function registerDevice(rawMac: string, deviceName: string) { const mac = await validateMac(rawMac) return { mac: mac.normalized, type: mac.type, deviceName } } // Batch validate from network scan async function validateNetworkScan(macs: string[]) { const results = await Promise.allSettled(macs.map(m => iv.macAddress(m))) return macs.map((mac, i) => ({ mac, result: results[i].status === 'fulfilled' ? results[i].value : null, error: results[i].status === 'rejected' ? results[i].reason?.message : null, })) } // Example const device = await registerDevice('00-1B-44-11-3A-B7', 'Raspberry Pi sensor') console.log(device.mac) // "00:1B:44:11:3A:B7"
7. Summary checklist
✓Accept colon, hyphen, and compact formats
✓Store normalized (colon uppercase) as canonical form
✓Reject multicast and broadcast addresses
✓Warn on locally-administered (randomised) MACs
✓Do not use MAC as sole persistent ID (may change)
✓Consider IMEI or UUID as stable device ID fallback
✓Run batch scans with Promise.allSettled
✓Return 422 with reason on invalid MAC input