
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
keystroke-dynamics
Advanced tools
A modern, passwordless authentication system that uses keystroke dynamics (typing patterns) to authenticate users. Experience the future of secure login where your typing rhythm becomes your password.
Keystroke dynamics analyzes the unique patterns in how you type - the timing between keystrokes, how long you hold keys down, and the rhythm of your typing. Just like your fingerprint, your typing pattern is unique to you!
<!DOCTYPE html>
<html>
<head>
<title>My Secure App</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- Your login form -->
<input type="text" id="username" placeholder="Type your username">
<button id="loginBtn">Login</button>
<!-- Include the scripts -->
<script src="https://cdn.jsdelivr.net/npm/keystroke-dynamics@latest/keystroke-dynamics.min.js"></script>
<script src="your-app.js"></script>
</body>
</html>
// Create the authentication system
const auth = new KeystrokeDynamics();
// Set up your user (one-time setup)
await auth.initialize('your-master-password', 'user@example.com');
// Train the system (collect 5 samples)
for (let i = 0; i < 5; i++) {
auth.startRecording();
// User types their username
await auth.addSample();
}
// When user tries to login
document.getElementById('username').addEventListener('input', (e) => {
if (e.target.value.length === 1) {
auth.startRecording(); // Start capturing keystrokes
}
});
document.getElementById('loginBtn').addEventListener('click', async () => {
try {
const result = await auth.verify();
if (result.isAuthentic && result.similarity >= 0.7) {
console.log('✅ Login successful!');
// Redirect to dashboard
} else {
console.log('❌ Authentication failed');
// Show password field
}
} catch (error) {
console.error('Authentication error:', error);
}
});
Here's a fully working login system:
class SecureLogin {
constructor() {
this.auth = new KeystrokeDynamics();
this.attempts = 0;
this.maxAttempts = 3;
this.setupEventListeners();
}
async init(masterPassword, username) {
await this.auth.initialize(masterPassword, username);
this.auth.setThreshold(0.7); // 70% similarity required
}
setupEventListeners() {
const usernameInput = document.getElementById('username');
const loginBtn = document.getElementById('loginBtn');
usernameInput.addEventListener('input', (e) => {
if (e.target.value.length === 1 && !this.auth.isRecording) {
this.auth.startRecording();
this.showStatus('Recording your typing pattern...');
}
});
loginBtn.addEventListener('click', () => this.handleLogin());
}
async handleLogin() {
try {
const result = await this.auth.verify();
if (result.isAuthentic && result.similarity >= 0.7) {
this.loginSuccess();
} else {
this.attempts++;
if (this.attempts >= this.maxAttempts) {
this.showPasswordField();
} else {
this.showRetryMessage(result.similarity);
}
}
} catch (error) {
this.handleError(error);
}
}
loginSuccess() {
this.showStatus('✅ Login successful!', 'success');
// Redirect or update UI
window.location.href = '/dashboard';
}
showPasswordField() {
document.getElementById('passwordField').style.display = 'block';
this.showStatus('⚠️ Biometric authentication failed. Please enter password.', 'warning');
}
showRetryMessage(similarity) {
const attemptsLeft = this.maxAttempts - this.attempts;
const similarityPercent = Math.round(similarity * 100);
this.showStatus(
`❌ Similarity: ${similarityPercent}% (need 70%). ${attemptsLeft} attempts left.`,
'error'
);
document.getElementById('username').value = '';
}
showStatus(message, type = 'info') {
const statusDiv = document.getElementById('status');
statusDiv.textContent = message;
statusDiv.className = `status ${type}`;
}
handleError(error) {
console.error('Authentication error:', error);
this.showStatus('🔥 Authentication system error', 'error');
}
}
// Initialize when page loads
document.addEventListener('DOMContentLoaded', async () => {
const login = new SecureLogin();
// Check if user exists, otherwise set up new user
if (!login.auth.isReady()) {
await login.init('secure-master-password', 'john@example.com');
// Show training interface
}
});
// Choose your security level
auth.setThreshold('low'); // 60% - Relaxed security
auth.setThreshold('medium'); // 70% - Balanced (recommended)
auth.setThreshold('high'); // 80% - Strict security
auth.setThreshold('max'); // 90% - Maximum security
// Or use custom threshold
auth.setThreshold(0.75); // 75% similarity required
// Minimum samples for training
const minSamples = 3; // Quick setup
const recommended = 5; // Balanced accuracy
const maxSamples = 10; // Best accuracy
// Check training status
console.log(`Samples collected: ${result.sampleCount}`);
console.log(`Accuracy: ${Math.round(result.similarity * 100)}%`);
| Browser | Version | Status |
|---|---|---|
| Chrome | 60+ | ✅ Full Support |
| Firefox | 55+ | ✅ Full Support |
| Safari | 11+ | ✅ Full Support |
| Edge | 79+ | ✅ Full Support |
| Opera | 47+ | ✅ Full Support |
Requirements:
Start with a regular login form and enhance it:
// Check if biometrics are available
if (window.KeystrokeDynamics && auth.isReady()) {
enableBiometricLogin();
} else {
showTraditionalLogin();
}
Provide clear user feedback:
/* Recording state */
.input-recording {
border: 2px solid #007bff;
animation: pulse 1s infinite;
}
/* Success state */
.input-success {
border: 2px solid #28a745;
}
/* Error state */
.input-error {
border: 2px solid #dc3545;
}
// Provide helpful tips
const tips = [
"Type naturally at your normal pace",
"Use the same typing style as during training",
"Ensure you're typing in the correct field",
"Try to maintain consistent rhythm"
];
function showRandomTip() {
const tip = tips[Math.floor(Math.random() * tips.length)];
document.getElementById('helpText').textContent = tip;
}
initialize(masterPassword, phrase)Set up the system for a new user.
await auth.initialize('secure123', 'user@example.com');
startRecording(targetElement?)Begin capturing keystroke data.
auth.startRecording(inputElement); // Bind to specific input
auth.startRecording(); // Capture globally
addSample()Add a training sample to improve accuracy.
auth.startRecording();
// User types...
await auth.addSample();
verify()Verify current keystroke pattern.
const result = await auth.verify();
console.log(result);
/*
{
isAuthentic: true,
similarity: 0.85,
threshold: 0.70,
sampleCount: 5
}
*/
setThreshold(level)Configure security level.
auth.setThreshold(0.8); // Numeric (0.0-1.0)
auth.setThreshold('medium'); // String level
auth.isReady() // System initialized?
auth.isRecording // Currently capturing?
auth.phrase // Training phrase
auth.threshold // Current threshold
// Lazy load for better performance
class LazyAuth {
async getAuth() {
if (!this.auth) {
this.auth = new KeystrokeDynamics();
await this.auth.initialize(password, phrase);
}
return this.auth;
}
}
// Clean up resources
auth.clearSignatures(); // Remove old training data
auth.reset(); // Complete system reset
try {
await auth.verify();
} catch (error) {
switch (error.code) {
case 'NO_DATA':
console.log('No keystrokes captured');
break;
case 'INSUFFICIENT_SAMPLES':
console.log('Need more training data');
break;
case 'VERIFY_FAILED':
console.log('Authentication failed');
break;
default:
console.log('Unknown error:', error.message);
}
}
// Enable detailed logging
window.enableDebug = true;
// Monitor keystroke capture
auth.onKeystroke = (event) => {
console.log('Captured:', event.key, event.timestamp);
};
// Quick checkout without passwords
if (await biometricAuth.verify()) {
processPayment();
} else {
requirePasswordAndOTP();
}
// High-security transactions
auth.setThreshold('max'); // 90% similarity
const result = await auth.verify();
if (result.similarity > 0.9) {
allowTransaction();
}
// Employee access control
const employee = await auth.authenticate(employeeId);
if (employee && result.isAuthentic) {
grantSystemAccess(employee.permissions);
}
// Secure exam authentication
auth.setThreshold('high');
if (await auth.verify()) {
startExam();
} else {
requireProctorVerification();
}
❌ "No keystroke data recorded"
// Solution: Ensure startRecording() is called
auth.startRecording(inputElement);
❌ "IndexedDB not supported"
// Solution: Check browser compatibility
if (!window.indexedDB) {
showUnsupportedBrowserMessage();
}
❌ "Low similarity scores"
// Solution: More training or lower threshold
if (result.similarity < 0.6) {
suggestMoreTraining();
// OR
auth.setThreshold('low');
}
❌ "Web Crypto API not available"
// Solution: Ensure HTTPS in production
if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
console.error('HTTPS required for production');
}
// Reduce sample size for faster processing
const maxSamples = 5; // Instead of 10
// Use web workers for heavy computation (advanced)
const worker = new Worker('crypto-worker.js');
// Track authentication metrics
const metrics = {
attempts: 0,
successes: 0,
averageSimilarity: 0,
trainingAccuracy: 0
};
// Log authentication attempts
auth.onVerify = (result) => {
metrics.attempts++;
if (result.isAuthentic) metrics.successes++;
// Send to analytics
analytics.track('biometric_auth', {
success: result.isAuthentic,
similarity: result.similarity,
threshold: result.threshold
});
};
<!-- CDN ready -->
<script src="https://cdn.jsdelivr.net/keystroke-dynamics@latest/keystroke-dynamics.min.js"></script>
# Extract and serve
unzip keystroke-dynamics.zip
cp *.js /var/www/html/js/
Contributions are welcome! Here's how to get started:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)git clone https://github.com/Omodaka9375/keystroke-dynamics.git
cd keystroke-dynamics
This project is licensed under the MIT License - see the LICENSE file for details.
If you find this project useful, please consider:
Made with ❤️ by developers, for developers
Securing the web, one keystroke at a time 🔐
FAQs
Passwordless authentication for the browser
We found that keystroke-dynamics demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.