Skip to content

Commit a8293cd

Browse files
committed
feat: add password, hash, and basic auth generators
1 parent c9b5498 commit a8293cd

8 files changed

Lines changed: 619 additions & 2 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ A sleek collection of developer tools built with React + Vite. Runs entirely in-
1111
| **Epoch Converter** | Convert Unix timestamps with timezone support |
1212
| **Base64 / URL** | Encode/decode Base64 and URL strings |
1313
| **Word Counter** | Count characters, words, lines, and paragraphs |
14+
| **Password Gen** | Generate secure random passwords with configurable options |
15+
| **Hash Generator** | Generate MD5 and bcrypt hashes |
16+
| **Basic Auth** | Generate HTTP headers and Nginx/Apache htpasswd entries |
1417

1518
## Quick Start
1619

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"preview": "vite preview"
1010
},
1111
"dependencies": {
12+
"bcryptjs": "^3.0.3",
1213
"lucide-react": "^0.460.0",
1314
"react": "^18.3.1",
1415
"react-dom": "^18.3.1"

src/App.jsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import {
44
ShieldCheck,
55
Clock,
66
Type,
7-
AlignLeft
7+
AlignLeft,
8+
KeyRound,
9+
Hash,
10+
Lock
811
} from 'lucide-react';
912

1013
// Layout components
@@ -16,7 +19,10 @@ import {
1619
JwtDebugger,
1720
EpochConverter,
1821
StringTools,
19-
CharacterCount
22+
CharacterCount,
23+
PasswordGenerator,
24+
HashGenerator,
25+
BasicAuthGenerator
2026
} from './features';
2127

2228
// UI components
@@ -31,6 +37,9 @@ const NAV_ITEMS = [
3137
{ id: 'epoch', label: 'Epoch Converter', icon: Clock },
3238
{ id: 'string', label: 'Base64 / URL', icon: Type },
3339
{ id: 'charcount', label: 'Word Counter', icon: AlignLeft },
40+
{ id: 'password', label: 'Password Gen', icon: Lock },
41+
{ id: 'hash', label: 'Hash Generator', icon: Hash },
42+
{ id: 'basicauth', label: 'Basic Auth', icon: KeyRound },
3443
];
3544

3645
/**
@@ -42,6 +51,9 @@ const FEATURE_COMPONENTS = {
4251
epoch: EpochConverter,
4352
string: StringTools,
4453
charcount: CharacterCount,
54+
password: PasswordGenerator,
55+
hash: HashGenerator,
56+
basicauth: BasicAuthGenerator,
4557
};
4658

4759
/**
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import { useState, useEffect } from 'react';
2+
import { Copy, Check, KeyRound, Server } from 'lucide-react';
3+
import bcrypt from 'bcryptjs';
4+
import { Button, Card } from '../components/ui';
5+
6+
/**
7+
* Basic Auth Generator - Generate HTTP Basic Authentication headers
8+
* and Nginx/Apache htpasswd entries
9+
*/
10+
const BasicAuthGenerator = () => {
11+
const [username, setUsername] = useState('');
12+
const [password, setPassword] = useState('');
13+
14+
// HTTP Header State
15+
const [encoded, setEncoded] = useState('');
16+
const [header, setHeader] = useState('');
17+
18+
// Nginx State
19+
const [htpasswd, setHtpasswd] = useState('');
20+
const [loading, setLoading] = useState(false);
21+
22+
const [copied, setCopied] = useState({ encoded: false, header: false, htpasswd: false });
23+
24+
// Auto-update Basic Auth Header (fast)
25+
useEffect(() => {
26+
if (username || password) {
27+
const credentials = `${username}:${password}`;
28+
const base64 = btoa(credentials);
29+
setEncoded(base64);
30+
setHeader(`Authorization: Basic ${base64}`);
31+
} else {
32+
setEncoded('');
33+
setHeader('');
34+
}
35+
}, [username, password]);
36+
37+
// Generate bcrypt hash for Nginx (slow, manual trigger)
38+
const generateHtpasswd = async () => {
39+
if (!username || !password) return;
40+
setLoading(true);
41+
try {
42+
const salt = await bcrypt.genSalt(10);
43+
const hash = await bcrypt.hash(password, salt);
44+
setHtpasswd(`${username}:${hash}`);
45+
} catch (e) {
46+
setHtpasswd('Error generating hash');
47+
}
48+
setLoading(false);
49+
};
50+
51+
const copyToClipboard = async (text, type) => {
52+
await navigator.clipboard.writeText(text);
53+
setCopied(prev => ({ ...prev, [type]: true }));
54+
setTimeout(() => setCopied(prev => ({ ...prev, [type]: false })), 2000);
55+
};
56+
57+
return (
58+
<div className="flex flex-col gap-6 max-w-2xl mx-auto mt-8">
59+
<Card title="Basic Auth Generator">
60+
<div className="flex flex-col gap-6">
61+
{/* Credentials Input */}
62+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
63+
<div className="flex flex-col gap-2">
64+
<label className="text-sm text-slate-400">Username</label>
65+
<input
66+
type="text"
67+
value={username}
68+
onChange={(e) => setUsername(e.target.value)}
69+
className="bg-slate-900 border border-slate-700 rounded-lg px-4 py-3 text-slate-200 focus:border-blue-500 outline-none"
70+
placeholder="admin"
71+
/>
72+
</div>
73+
<div className="flex flex-col gap-2">
74+
<label className="text-sm text-slate-400">Password</label>
75+
<input
76+
type="text"
77+
value={password}
78+
onChange={(e) => setPassword(e.target.value)}
79+
className="bg-slate-900 border border-slate-700 rounded-lg px-4 py-3 text-slate-200 focus:border-blue-500 outline-none"
80+
placeholder="secret123"
81+
/>
82+
</div>
83+
</div>
84+
85+
{/* Preview */}
86+
{(username || password) && (
87+
<div className="bg-slate-900/50 p-4 rounded-lg border border-slate-700/50">
88+
<div className="text-xs text-slate-500 mb-2">Credentials Preview</div>
89+
<code className="text-sm text-purple-400 font-mono">{username}:{password}</code>
90+
</div>
91+
)}
92+
93+
<div className="w-full h-px bg-slate-800 my-2"></div>
94+
95+
{/* Base64 Encoded */}
96+
<div className="flex flex-col gap-2">
97+
<label className="text-sm text-slate-400 flex items-center gap-2">
98+
<KeyRound size={14} />
99+
HTTP Header (Base64)
100+
</label>
101+
<div className="flex gap-2">
102+
<input
103+
readOnly
104+
value={header}
105+
className="flex-1 bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 font-mono text-sm text-green-400 focus:outline-none"
106+
placeholder="Authorization: Basic ..."
107+
/>
108+
<Button
109+
variant="secondary"
110+
onClick={() => copyToClipboard(header, 'header')}
111+
disabled={!header}
112+
icon={copied.header ? Check : Copy}
113+
/>
114+
</div>
115+
<div className="text-xs text-slate-500 font-mono pl-1">
116+
Base64: {encoded || '...'}
117+
</div>
118+
</div>
119+
120+
<div className="w-full h-px bg-slate-800 my-2"></div>
121+
122+
{/* Nginx / Apache htpasswd */}
123+
<div className="flex flex-col gap-2">
124+
<label className="text-sm text-slate-400 flex items-center gap-2">
125+
<Server size={14} />
126+
Nginx / Apache htpasswd (bcrypt)
127+
</label>
128+
<div className="flex gap-2">
129+
<input
130+
readOnly
131+
value={htpasswd}
132+
className="flex-1 bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 font-mono text-sm text-orange-400 focus:outline-none"
133+
placeholder="username:$2y$10$..."
134+
/>
135+
<Button
136+
variant="secondary"
137+
onClick={() => copyToClipboard(htpasswd, 'htpasswd')}
138+
disabled={!htpasswd}
139+
icon={copied.htpasswd ? Check : Copy}
140+
/>
141+
</div>
142+
<Button
143+
onClick={generateHtpasswd}
144+
disabled={!username || !password || loading}
145+
className="w-full justify-center"
146+
>
147+
{loading ? 'Generating...' : 'Generate htpasswd Entry'}
148+
</Button>
149+
<div className="bg-slate-900/50 p-3 rounded border border-slate-700/50 mt-2">
150+
<div className="text-xs text-slate-500 mb-1">Nginx Usage:</div>
151+
<code className="text-xs text-blue-300 font-mono block mb-2">
152+
echo "{htpasswd || 'user:pass'}" &gt; /etc/nginx/.htpasswd
153+
</code>
154+
<div className="text-xs text-slate-500 mb-1">Config:</div>
155+
<code className="text-xs text-slate-400 font-mono">
156+
auth_basic "Restricted";<br />
157+
auth_basic_user_file /etc/nginx/.htpasswd;
158+
</code>
159+
</div>
160+
</div>
161+
162+
</div>
163+
</Card>
164+
</div>
165+
);
166+
};
167+
168+
export default BasicAuthGenerator;

0 commit comments

Comments
 (0)