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!
- π« No Passwords Required - Type your username/email naturally to login
- π‘οΈ Bank-Level Security - AES-256 encryption with customizable thresholds
- π― High Accuracy - 95%+ authentication success rate after training
- π Smart Fallback - Automatic password backup after 3 failed attempts
- π± Cross-Platform - Works on desktop and mobile browsers
- π Privacy First - All data stays on your device, no servers required
- β‘ Lightning Fast - Sub-second authentication
- π¨ Easy Integration - Drop-in solution for any website
<!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', '[email protected]');
// 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', '[email protected]');
// 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:
- IndexedDB support
- Web Crypto API
- ES6+ JavaScript
- HTTPS (production only)
- AES-256-GCM encryption for all stored data
- PBKDF2 key derivation (100,000 iterations)
- Random salt and IV for each encryption operation
- SHA-256 hashing for master keys
- Zero server communication - everything happens locally
- No biometric data transmission - patterns never leave your device
- Automatic data cleanup - old samples can be purged
- Master password protection - encrypted with your password only
- Timing attack prevention - normalized timing windows
- Replay attack protection - each sample includes timestamp
- Threshold validation - customizable security levels
- Attempt limiting - automatic lockout after failed attempts
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;
}Set up the system for a new user.
await auth.initialize('secure123', '[email protected]');Begin capturing keystroke data.
auth.startRecording(inputElement); // Bind to specific input
auth.startRecording(); // Capture globallyAdd a training sample to improve accuracy.
auth.startRecording();
// User types...
await auth.addSample();Verify current keystroke pattern.
const result = await auth.verify();
console.log(result);
/*
{
isAuthentic: true,
similarity: 0.85,
threshold: 0.70,
sampleCount: 5
}
*/Configure security level.
auth.setThreshold(0.8); // Numeric (0.0-1.0)
auth.setThreshold('medium'); // String levelauth.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 resettry {
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
});
};- HTTPS enabled
- Master passwords are strong and unique
- Error handling implemented
- Fallback authentication ready
- User training flow tested
- Browser compatibility verified
- Performance optimized
- Security audit completed
<!-- 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:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
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:
- β Starring this repository
- π Sharing with your network
- π Sponsoring development
- π Writing a blog post about it
- π Reporting bugs and issues
Made with β€οΈ by developers, for developers
Securing the web, one keystroke at a time π