This guide walks you through using FIDO2 as an encryption provider for VHSM.
The FIDO2 provider uses FIDO2/WebAuthn authentication to protect your dotenvx private keys. This provides hardware-backed security with the following benefits:
- Hardware Security: Keys are derived from FIDO2 credentials
- User Presence Required: Authentication required for decryption (PIN, biometric, touch, etc.)
- Cross-Platform: Works on Windows, macOS, and Linux
- No Password: No password to remember or forget
- Multiple Options: Supports Windows Hello, hardware security keys, and mobile authenticators
-
FIDO2-Compatible Authenticator
- Windows Hello: Built-in PIN, fingerprint, or facial recognition (Windows 10/11)
- Hardware Security Keys: YubiKey, Titan Security Key, or any FIDO2-compatible key
- Mobile Authenticators: Face ID, fingerprint via QR code on iPhone/Android
- Any FIDO2/WebAuthn compatible authenticator
-
Web Browser
- Chrome, Edge, Firefox, Safari, or any modern browser
- The provider opens a local web page for authentication
-
Node.js 18+
- Required for the VHSM CLI
-
Encryption: When you encrypt your keys, the provider:
- Opens a browser window
- Creates a FIDO2 credential using your authenticator (Windows Hello, security key, mobile, etc.)
- Derives an encryption key from the credential
- Encrypts your private key with AES-256-GCM
- Stores the credential ID with the encrypted data
-
Decryption: When you decrypt your keys, the provider:
- Opens a browser window
- Authenticates using the same FIDO2 authenticator (Windows Hello, security key, mobile, etc.)
- Derives the same encryption key
- Decrypts your private key
-
Security: The encryption key is:
- Derived from the FIDO2 credential ID
- Never stored on disk
- Requires physical device presence
- Unique per registration
First, make sure your Yubikey is working:
node test-fido2.jsThis will:
- Open a browser window
- Ask you to register your Yubikey
- Encrypt a test string
- Ask you to authenticate
- Decrypt and verify
Expected Output:
=== FIDO2/Yubikey Provider Test ===
FIDO2 Available: true
Creating FIDO2 provider...
✅ Provider created: fido2
Requires interaction: true
Original string: dotenvx_private_key_1234567890abcdef
Encrypting with FIDO2...
A browser window will open. Please follow the instructions.
🌐 Opening browser for authentication...
✅ Encrypted: abc123...
Decrypting with FIDO2...
A browser window will open. Please touch your Yubikey when prompted.
🔑 Please touch your Yubikey to decrypt...
🌐 Opening browser for authentication...
✅ Decrypted: dotenvx_private_key_1234567890abcdef
✅ Success! Decrypted string matches original
First, if you haven't already, encrypt your .env file with dotenvx:
# This creates .env.keys with your private keys
npx dotenvx encryptThen, encrypt the private keys with FIDO2:
# Encrypt with FIDO2 provider
vhsm encrypt -p fido2
# Or specify custom paths
vhsm encrypt -p fido2 -o .env.keys.encrypted -fk .env.keysWhat happens:
- A browser window opens
- You click "Register Yubikey"
- Your Yubikey blinks - touch it
- Your keys are encrypted
- The encrypted keys are saved to
.env.keys.encrypted - The original
.env.keysis deleted (unless you use--no-delete)
Example .env.keys.encrypted:
#/-----------------!VHSM_PRIVATE_KEYS!------------------/
#/ VHSM encrypted keys. DO NOT commit to source control /
#/------------------------------------------------------/
VHSM_PRIVATE_KEY=fido2:abc123def456:1234567890abcdef:fedcba0987654321:encrypted_data_here
Format: fido2:credentialId:iv:authTag:encryptedData
Now you can run your application with encrypted keys:
# Run with FIDO2 decryption
vhsm run -p fido2 -- node index.js
# Or with specific env files
vhsm run -p fido2 -f .env.production -- npm startWhat happens:
- A browser window opens automatically
- Your Yubikey blinks - touch it
- Keys are decrypted in memory
- Your application runs with decrypted environment variables
- Keys are cleared from memory after execution
If you need to view or restore your keys:
# Decrypt and display
vhsm decrypt -p fido2
# Restore to .env.keys file
vhsm decrypt -p fido2 --restoreIf the browser doesn't open, manually navigate to the URL shown in the console:
http://localhost:8765
- Make sure your Yubikey is plugged in
- Try a different USB port
- Check that your browser supports WebAuthn
- Close other applications using the Yubikey
Your encrypted key may be corrupted or from a different provider. Check:
- The key starts with
fido2:prefix - The file hasn't been manually edited
- You're using the correct
.env.keys.encryptedfile
- Make sure you're accessing
localhost, not127.0.0.1 - Some browsers require HTTPS, but localhost is exempt
- Try a different browser (Chrome/Edge recommended)
FIDO2 credentials are tied to the specific Yubikey that created them. To use on a different machine:
- You need the SAME physical Yubikey
- The credential must still be stored on that Yubikey
- Consider using multiple keys for different machines
Register a backup Yubikey by re-encrypting:
# Decrypt with first Yubikey
vhsm decrypt -p fido2 --restore
# Re-encrypt with second Yubikey
vhsm encrypt -p fido2 -fk .env.keysYou can configure FIDO2 behavior in .vhsmrc:
{
"provider": "fido2",
"enableCache": true,
"cacheTimeout": 3600000
}FIDO2 requires user interaction, so it's not suitable for:
- Automated CI/CD pipelines
- Unattended scripts
- Docker containers
For these scenarios, use password or dpapi providers instead.
FIDO2 requires a graphical browser, so it won't work over SSH without X11 forwarding. Options:
-
Use SSH with X11 forwarding:
ssh -X user@host
-
Use port forwarding:
# On remote machine vhsm run -p fido2 -- node app.js # Forward port 8765 from your local machine ssh -L 8765:localhost:8765 user@host # Open http://localhost:8765 in your local browser
-
Use a different provider for remote machines
✅ Your dotenvx private keys are encrypted with AES-256-GCM ✅ Decryption requires physical device presence ✅ Keys are never stored in plaintext on disk (after encryption) ✅ Session cache is memory-only and time-limited
.env files remain encrypted with dotenvx
- Keep your Yubikey secure - Physical possession = decryption capability
- Use backup keys - Register multiple Yubikeys
- Don't commit
.env.keys.encryptedto version control - Use cache wisely - Default 1 hour is good for development
- Verify localhost - Only authenticate on
localhost:8765
| Feature | FIDO2 | DPAPI | Password |
|---|---|---|---|
| Hardware-backed | ✅ Yes | ✅ Yes | ❌ No |
| User interaction | ✅ Required | ❌ Not needed | |
| Cross-platform | ✅ Yes | ❌ Windows only | ✅ Yes |
| Machine-bound | ❌ No* | ✅ Yes | ❌ No |
| CI/CD friendly | ❌ No | ✅ Yes | |
| Browser required | ✅ Yes | ❌ No | ❌ No |
*FIDO2 is key-bound, not machine-bound - same Yubikey works on any machine
Q: Can I use multiple Yubikeys? A: Each encryption creates a new credential. For multiple keys, decrypt and re-encrypt with each Yubikey.
Q: What if I lose my Yubikey? A: You'll need to restore from backup or decrypt with a backup Yubikey. Keep backups!
Q: Does this work offline? A: Yes! The local web server runs entirely on your machine. No internet required.
Q: Can I use this in Docker? A: Not easily. Docker containers can't access USB devices or display browsers without special configuration.
Q: Is this secure? A: Yes, when used properly. The FIDO2 protocol is designed for security, but physical device access = decryption.
Q: How is this different from Yubikey OTP? A: FIDO2 uses public-key cryptography and is more modern. It's specifically designed for authentication.
Q: Can I use this with GitHub Codespaces / VS Code Remote? A: Port forwarding can work, but local providers (DPAPI, password) are more practical for remote development.
- Learn about session caching
- Explore other providers
- Read about configuration options
- Check out CI/CD integration
If you encounter issues:
- Run
node test-fido2.jsto verify basic functionality - Check the Troubleshooting section
- Verify your Yubikey works with other FIDO2 apps
- Open an issue with detailed error messages
Note: This provider is experimental. Test thoroughly before using in production!