Secure Password Generator Component
Master machine coding by creating a secure password generator with customizable options, copy-to-clipboard functionality, and advanced security features.
Machine Coding Challenge: Password Generator Component
Create a production-ready password generator with customizable options, copy-to-clipboard functionality, and advanced security features using React and modern web APIs.
Problem Statement
Design and implement a secure password generator component with the following requirements:
✅ Core Features (Must Have)
- Generate secure passwords with customizable options
- Copy-to-clipboard functionality
- Password strength indicator
- Customizable password length
- Multiple character set options (uppercase, lowercase, numbers, symbols)
Advanced Features (Nice to Have)
- Password strength meter with visual feedback
- Password history/previous passwords
- Export passwords to file
- Keyboard shortcuts support
- Accessibility compliance
- Mobile-responsive design
UI/UX Requirements
- Clean, modern interface design
- Visual feedback for all actions
- Loading states and animations
- Error handling and validation
- Accessibility features (ARIA labels, keyboard navigation)
🧠 Clarifying Questions & Scope
Functional Requirements
- Password Length: Minimum and maximum length? (8-128 characters?)
- Character Sets: Which character sets to include?
- Strength Meter: How to calculate and display password strength?
- History: Should we store previous passwords?
- Export: File format for password export?
Technical Constraints
- Time Limit: 2-3 hours for implementation
- Framework: React with hooks
- Security: Use cryptographically secure random generation
- Browser Support: Modern browsers with Clipboard API
Success Criteria
- Generate cryptographically secure passwords
- Smooth user experience with immediate feedback
- Proper error handling and edge cases
- Clean, maintainable code structure
🏗️ High-Level Architecture
1. Component Structure
PasswordGenerator/
├── PasswordGeneratorContainer/ # Main container component
│ ├── PasswordDisplay/ # Password output with copy button
│ ├── OptionsPanel/ # Character set and length options
│ ├── StrengthMeter/ # Password strength indicator
│ ├── GenerateButton/ # Generate password button
│ └── PasswordHistory/ # Previous passwords list
├── usePasswordGenerator/ # Custom hook for password logic
├── useClipboard/ # Clipboard API hook
└── usePasswordStrength/ # Password strength calculation2. State Management Strategy
interface PasswordGeneratorState {
password: string;
options: PasswordOptions;
strength: PasswordStrength;
history: string[];
isCopied: boolean;
error: string | null;
}
interface PasswordOptions {
length: number;
uppercase: boolean;
lowercase: boolean;
numbers: boolean;
symbols: boolean;
excludeSimilar: boolean;
excludeAmbiguous: boolean;
}
interface PasswordStrength {
score: number; // 0-100
label: string; // 'Weak', 'Medium', 'Strong', 'Very Strong'
color: string; // CSS color for strength meter
}🛠️ Step-by-Step Implementation
Step 1: Basic Password Generator Setup
// components/PasswordGenerator.tsx
import React, { useState, useCallback } from 'react';
import { usePasswordGenerator } from '../hooks/usePasswordGenerator';
import { useClipboard } from '../hooks/useClipboard';
interface PasswordGeneratorProps {
defaultLength?: number;
maxLength?: number;
minLength?: number;
showStrength?: boolean;
showHistory?: boolean;
}
const PasswordGenerator: React.FC<PasswordGeneratorProps> = ({
defaultLength = 12,
maxLength = 128,
minLength = 8,
showStrength = true,
showHistory = false
}) => {
const [password, setPassword] = useState('');
const [options, setOptions] = useState({
length: defaultLength,
uppercase: true,
lowercase: true,
numbers: true,
symbols: true,
excludeSimilar: false,
excludeAmbiguous: false
});
const { generatePassword, strength } = usePasswordGenerator();
const { copyToClipboard, isCopied } = useClipboard();
const handleGeneratePassword = useCallback(() => {
const newPassword = generatePassword(options);
setPassword(newPassword);
}, [generatePassword, options]);
const handleCopyPassword = useCallback(() => {
if (password) {
copyToClipboard(password);
}
}, [copyToClipboard, password]);
return (
<div className="password-generator">
<div className="password-generator-header">
<h1>🔐 Secure Password Generator</h1>
<p>Generate strong, secure passwords with customizable options</p>
</div>
<div className="password-generator-content">
{/* Password Display */}
<PasswordDisplay
password={password}
onCopy={handleCopyPassword}
isCopied={isCopied}
/>
{/* Strength Meter */}
{showStrength && password && (
<StrengthMeter strength={strength} />
)}
{/* Options Panel */}
<OptionsPanel
options={options}
onOptionChange={handleOptionChange}
minLength={minLength}
maxLength={maxLength}
/>
{/* Generate Button */}
<GenerateButton
onGenerate={handleGeneratePassword}
disabled={!hasValidOptions(options)}
/>
</div>
</div>
);
};Step 2: Custom Hooks Implementation
// hooks/usePasswordGenerator.ts
import { useCallback, useMemo } from 'react';
export function usePasswordGenerator() {
const charSets = useMemo(() => ({
uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
lowercase: 'abcdefghijklmnopqrstuvwxyz',
numbers: '0123456789',
symbols: '!@#$%^&*()_+-=[]{}|;:,.<>?',
similar: 'il1Lo0O',
ambiguous: '{}[]()/\\\'"`~,;:.<>'
}), []);
const generatePassword = useCallback((options: PasswordOptions): string => {
const selectedSets = Object.entries(options)
.filter(([key, value]) =>
key !== 'length' &&
key !== 'excludeSimilar' &&
key !== 'excludeAmbiguous' &&
value
)
.map(([key]) => key as keyof typeof charSets);
if (selectedSets.length === 0) {
throw new Error('At least one character set must be selected');
}
let availableChars = selectedSets
.map(set => charSets[set])
.join('');
// Remove similar characters if requested
if (options.excludeSimilar) {
availableChars = availableChars
.split('')
.filter(char => !charSets.similar.includes(char))
.join('');
}
// Remove ambiguous characters if requested
if (options.excludeAmbiguous) {
availableChars = availableChars
.split('')
.filter(char => !charSets.ambiguous.includes(char))
.join('');
}
// Ensure at least one character from each selected set
let password = '';
const minCharsPerSet = Math.floor(options.length / selectedSets.length);
const remainingLength = options.length - (minCharsPerSet * selectedSets.length);
selectedSets.forEach(set => {
const setChars = charSets[set];
for (let i = 0; i < minCharsPerSet; i++) {
const randomIndex = Math.floor(Math.random() * setChars.length);
password += setChars[randomIndex];
}
});
// Fill remaining length with random characters
for (let i = 0; i < remainingLength; i++) {
const randomIndex = Math.floor(Math.random() * availableChars.length);
password += availableChars[randomIndex];
}
// Shuffle the password
return password
.split('')
.sort(() => Math.random() - 0.5)
.join('');
}, [charSets]);
const calculateStrength = useCallback((password: string): PasswordStrength => {
let score = 0;
// Length contribution
score += Math.min(password.length * 4, 40);
// Character set diversity
const hasUppercase = /[A-Z]/.test(password);
const hasLowercase = /[a-z]/.test(password);
const hasNumbers = /\d/.test(password);
const hasSymbols = /[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(password);
const uniqueSets = [hasUppercase, hasLowercase, hasNumbers, hasSymbols]
.filter(Boolean).length;
score += (uniqueSets - 1) * 10;
// Entropy calculation
const charsetSize = (hasUppercase ? 26 : 0) +
(hasLowercase ? 26 : 0) +
(hasNumbers ? 10 : 0) +
(hasSymbols ? 32 : 0);
const entropy = Math.log2(Math.pow(charsetSize, password.length));
score += Math.min(entropy / 2, 30);
// Determine strength level
let label: string;
let color: string;
if (score < 30) {
label = 'Very Weak';
color = '#ff4444';
} else if (score < 50) {
label = 'Weak';
color = '#ff8800';
} else if (score < 70) {
label = 'Medium';
color = '#ffaa00';
} else if (score < 90) {
label = 'Strong';
color = '#00aa00';
} else {
label = 'Very Strong';
color = '#008800';
}
return { score, label, color };
}, []);
return {
generatePassword,
calculateStrength
};
}
// hooks/useClipboard.ts
import { useState, useCallback } from 'react';
export function useClipboard() {
const [isCopied, setIsCopied] = useState(false);
const copyToClipboard = useCallback(async (text: string) => {
try {
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(text);
} else {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
document.execCommand('copy');
textArea.remove();
}
setIsCopied(true);
setTimeout(() => setIsCopied(false), 2000);
} catch (error) {
console.error('Failed to copy to clipboard:', error);
}
}, []);
return { copyToClipboard, isCopied };
}Step 3: Component Implementation
// components/PasswordDisplay.tsx
import React from 'react';
interface PasswordDisplayProps {
password: string;
onCopy: () => void;
isCopied: boolean;
}
const PasswordDisplay: React.FC<PasswordDisplayProps> = ({
password,
onCopy,
isCopied
}) => {
return (
<div className="password-display">
<div className="password-field">
<input
type="text"
value={password}
readOnly
placeholder="Generated password will appear here..."
className="password-input"
aria-label="Generated password"
/>
{password && (
<button
onClick={onCopy}
className={`copy-button ${isCopied ? 'copied' : ''}`}
title={isCopied ? 'Copied!' : 'Copy to clipboard'}
aria-label={isCopied ? 'Password copied' : 'Copy password to clipboard'}
>
{isCopied ? '✓' : '📋'}
</button>
)}
</div>
</div>
);
};
// components/OptionsPanel.tsx
import React from 'react';
interface OptionsPanelProps {
options: PasswordOptions;
onOptionChange: (name: string, value: boolean | number) => void;
minLength: number;
maxLength: number;
}
const OptionsPanel: React.FC<OptionsPanelProps> = ({
options,
onOptionChange,
minLength,
maxLength
}) => {
return (
<div className="options-panel">
<h3>Password Options</h3>
{/* Length Slider */}
<div className="option-group">
<label htmlFor="length-slider">
Password Length: {options.length}
</label>
<input
id="length-slider"
type="range"
min={minLength}
max={maxLength}
value={options.length}
onChange={(e) => onOptionChange('length', parseInt(e.target.value))}
className="length-slider"
/>
<div className="length-labels">
<span>{minLength}</span>
<span>{maxLength}</span>
</div>
</div>
{/* Character Set Options */}
<div className="option-group">
<h4>Character Sets</h4>
<div className="checkbox-group">
<label className="checkbox-label">
<input
type="checkbox"
checked={options.uppercase}
onChange={(e) => onOptionChange('uppercase', e.target.checked)}
/>
Uppercase Letters (A-Z)
</label>
<label className="checkbox-label">
<input
type="checkbox"
checked={options.lowercase}
onChange={(e) => onOptionChange('lowercase', e.target.checked)}
/>
Lowercase Letters (a-z)
</label>
<label className="checkbox-label">
<input
type="checkbox"
checked={options.numbers}
onChange={(e) => onOptionChange('numbers', e.target.checked)}
/>
Numbers (0-9)
</label>
<label className="checkbox-label">
<input
type="checkbox"
checked={options.symbols}
onChange={(e) => onOptionChange('symbols', e.target.checked)}
/>
Symbols (!@#$%^&*)
</label>
</div>
</div>
{/* Advanced Options */}
<div className="option-group">
<h4>Advanced Options</h4>
<div className="checkbox-group">
<label className="checkbox-label">
<input
type="checkbox"
checked={options.excludeSimilar}
onChange={(e) => onOptionChange('excludeSimilar', e.target.checked)}
/>
Exclude Similar Characters (i, l, 1, L, o, 0, O)
</label>
<label className="checkbox-label">
<input
type="checkbox"
checked={options.excludeAmbiguous}
onChange={(e) => onOptionChange('excludeAmbiguous', e.target.checked)}
/>
Exclude Ambiguous Characters ({ } [ ] ( ) / \ ' " ` ~ , ; : . < >)
</label>
</div>
</div>
</div>
);
};
// components/StrengthMeter.tsx
import React from 'react';
interface StrengthMeterProps {
strength: PasswordStrength;
}
const StrengthMeter: React.FC<StrengthMeterProps> = ({ strength }) => {
return (
<div className="strength-meter">
<div className="strength-header">
<span>Password Strength:</span>
<span className="strength-label" style={{ color: strength.color }}>
{strength.label}
</span>
</div>
<div className="strength-bar">
<div
className="strength-fill"
style={{
width: `${strength.score}%`,
backgroundColor: strength.color
}}
/>
</div>
<div className="strength-score">
Score: {strength.score}/100
</div>
</div>
);
};
// components/GenerateButton.tsx
import React from 'react';
interface GenerateButtonProps {
onGenerate: () => void;
disabled: boolean;
}
const GenerateButton: React.FC<GenerateButtonProps> = ({
onGenerate,
disabled
}) => {
return (
<button
onClick={onGenerate}
disabled={disabled}
className={`generate-button ${disabled ? 'disabled' : ''}`}
aria-label="Generate new password"
>
🔄 Generate Password
</button>
);
};UI/UX Enhancements
Modern CSS Styling
/* components/PasswordGenerator.css */
.password-generator {
max-width: 600px;
margin: 0 auto;
padding: 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
color: white;
}
.password-generator-header {
text-align: center;
margin-bottom: 2rem;
}
.password-generator-header h1 {
font-size: 2.5rem;
margin-bottom: 0.5rem;
font-weight: 700;
}
.password-generator-header p {
font-size: 1.1rem;
opacity: 0.9;
}
.password-display {
margin-bottom: 2rem;
}
.password-field {
position: relative;
display: flex;
align-items: center;
background: rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 0.5rem;
backdrop-filter: blur(10px);
}
.password-input {
flex: 1;
background: transparent;
border: none;
color: white;
font-size: 1.2rem;
font-family: 'Courier New', monospace;
padding: 1rem;
outline: none;
}
.password-input::placeholder {
color: rgba(255, 255, 255, 0.6);
}
.copy-button {
background: rgba(255, 255, 255, 0.2);
border: none;
border-radius: 8px;
padding: 0.75rem;
color: white;
cursor: pointer;
transition: all 0.3s ease;
margin-left: 0.5rem;
}
.copy-button:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
.copy-button.copied {
background: #10b981;
animation: pulse 0.6s ease-in-out;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
.strength-meter {
margin-bottom: 2rem;
padding: 1.5rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 12px;
backdrop-filter: blur(10px);
}
.strength-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.strength-label {
font-weight: 600;
font-size: 1.1rem;
}
.strength-bar {
height: 8px;
background: rgba(255, 255, 255, 0.2);
border-radius: 4px;
overflow: hidden;
margin-bottom: 0.5rem;
}
.strength-fill {
height: 100%;
transition: width 0.3s ease;
}
.options-panel {
margin-bottom: 2rem;
padding: 1.5rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 12px;
backdrop-filter: blur(10px);
}
.option-group {
margin-bottom: 1.5rem;
}
.option-group h4 {
margin-bottom: 1rem;
font-size: 1.1rem;
font-weight: 600;
}
.length-slider {
width: 100%;
height: 6px;
border-radius: 3px;
background: rgba(255, 255, 255, 0.2);
outline: none;
margin: 1rem 0;
}
.length-slider::-webkit-slider-thumb {
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: white;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}
.checkbox-group {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
}
.checkbox-label {
display: flex;
align-items: center;
cursor: pointer;
padding: 0.75rem;
border-radius: 8px;
transition: background-color 0.3s ease;
}
.checkbox-label:hover {
background: rgba(255, 255, 255, 0.1);
}
.checkbox-label input[type="checkbox"] {
margin-right: 0.75rem;
width: 18px;
height: 18px;
accent-color: #10b981;
}
.generate-button {
width: 100%;
padding: 1rem 2rem;
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
color: white;
border: none;
border-radius: 12px;
font-size: 1.2rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
}
.generate-button:hover:not(.disabled) {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4);
}
.generate-button.disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
/* Responsive Design */
@media (max-width: 768px) {
.password-generator {
padding: 1rem;
margin: 1rem;
}
.password-generator-header h1 {
font-size: 2rem;
}
.checkbox-group {
grid-template-columns: 1fr;
}
}Performance Optimizations
Security Enhancements
// Enhanced security with crypto API
const generateSecurePassword = (options: PasswordOptions): string => {
const array = new Uint8Array(options.length);
crypto.getRandomValues(array);
// Convert to secure password using selected character sets
// ... implementation
};
// Password validation
const validatePassword = (password: string): boolean => {
const minLength = 8;
const hasUppercase = /[A-Z]/.test(password);
const hasLowercase = /[a-z]/.test(password);
const hasNumbers = /\d/.test(password);
const hasSymbols = /[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(password);
return password.length >= minLength &&
(hasUppercase || hasLowercase) &&
(hasNumbers || hasSymbols);
};Key Takeaways
What You've Learned
- Security Best Practices: Cryptographically secure random generation
- User Experience: Intuitive interface with immediate feedback
- Accessibility: ARIA labels and keyboard navigation
- Performance: Optimized rendering and state management
- Modern APIs: Clipboard API and crypto API usage
Common Pitfalls to Avoid
- ❌ Weak Randomness: Don't use Math.random() for security
- ❌ Poor UX: Missing loading states and error handling
- ❌ Accessibility Issues: Missing ARIA labels and keyboard support
- ❌ Insecure Storage: Don't store passwords in localStorage
- ❌ Poor Validation: Missing input validation and edge cases
Next Steps
- Implement password strength visualization
- Add password export functionality
- Create password history management
- Add biometric authentication support
- Implement password sharing features
This password generator component demonstrates advanced React patterns, security best practices, and modern web APIs. The skills you've learned here are essential for building secure, user-friendly applications that handle sensitive data properly! 🔐
Pagination
Build a pagination component using React with page navigation, data chunking, and user-friendly controls
Dynamic Progress Bar Component
Master machine coding by creating a versatile progress bar component with smooth animations, customizable styling, and advanced features using React and CSS.