Google Auth gibi uygulamalar kurmak zor geliyorsa secret kodunuzu girerek anlık tek kullanımlık şifre üreten basit bir araç oluşturabilirsiniz.
Demo : https://2fa.grandkingbet.com/
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TOTP Kod Üretici</title>
<style>
:root {
--bg: #0b1020;
--panel: #131a2a;
--panel-2: #1a2338;
--text: #e8ecf3;
--muted: #9aa4b2;
--accent: #6ea8fe;
--accent-2: #8b5cf6;
--danger: #ef4444;
--ok: #22c55e;
--border: rgba(255,255,255,0.08);
--shadow: 0 20px 60px rgba(0,0,0,0.35);
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
background:
radial-gradient(circle at top right, rgba(110,168,254,0.16), transparent 28%),
radial-gradient(circle at bottom left, rgba(139,92,246,0.12), transparent 24%),
var(--bg);
color: var(--text);
display: grid;
place-items: center;
padding: 24px;
}
.card {
width: 100%;
max-width: 560px;
background: linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0.015));
border: 1px solid var(--border);
border-radius: 24px;
padding: 24px;
box-shadow: var(--shadow);
backdrop-filter: blur(8px);
}
h1 {
margin: 0 0 8px;
font-size: 28px;
line-height: 1.15;
}
p.desc {
margin: 0 0 20px;
color: var(--muted);
line-height: 1.55;
}
label {
display: block;
margin-bottom: 8px;
font-size: 14px;
color: var(--muted);
}
.row {
display: grid;
gap: 14px;
grid-template-columns: 1fr;
}
input, select, button {
width: 100%;
border: 1px solid var(--border);
background: var(--panel-2);
color: var(--text);
border-radius: 14px;
padding: 14px 16px;
font-size: 16px;
outline: none;
}
input:focus, select:focus {
border-color: rgba(110,168,254,0.55);
box-shadow: 0 0 0 4px rgba(110,168,254,0.12);
}
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
margin-top: 14px;
}
.actions {
display: grid;
grid-template-columns: 1fr auto;
gap: 12px;
margin-top: 14px;
}
button {
cursor: pointer;
background: linear-gradient(135deg, var(--accent), var(--accent-2));
border: 0;
font-weight: 600;
}
button.secondary {
background: var(--panel-2);
border: 1px solid var(--border);
min-width: 120px;
}
.result {
margin-top: 20px;
padding: 20px;
border-radius: 20px;
background: var(--panel);
border: 1px solid var(--border);
}
.code {
font-size: 52px;
letter-spacing: 6px;
font-weight: 800;
text-align: center;
margin: 8px 0 6px;
font-variant-numeric: tabular-nums;
}
.meta {
text-align: center;
color: var(--muted);
font-size: 14px;
}
.bar {
margin-top: 14px;
height: 10px;
background: rgba(255,255,255,0.06);
border-radius: 999px;
overflow: hidden;
}
.bar > div {
height: 100%;
width: 0%;
background: linear-gradient(90deg, var(--ok), #facc15, var(--danger));
transition: width 0.25s linear;
}
.error {
margin-top: 14px;
color: #fecaca;
background: rgba(239,68,68,0.12);
border: 1px solid rgba(239,68,68,0.24);
border-radius: 14px;
padding: 12px 14px;
display: none;
}
.footer {
margin-top: 16px;
color: var(--muted);
font-size: 13px;
line-height: 1.5;
}
.hint {
font-size: 13px;
color: var(--muted);
margin-top: 8px;
}
code.inline {
background: rgba(255,255,255,0.06);
padding: 2px 8px;
border-radius: 999px;
font-size: 12px;
}
@media (max-width: 560px) {
.grid-2, .actions {
grid-template-columns: 1fr;
}
.code {
font-size: 40px;
letter-spacing: 4px;
}
}
</style>
</head>
<body>
<main class="card">
<h1>TOTP Kod Üretici</h1>
<p class="desc">
Google Authenticator uyumlu <strong>secret key</strong> gir, kod anında tarayıcıda üretilsin.
Veri herhangi bir sunucuya gönderilmez.
</p>
<div class="row">
<div>
<label for="secret">Secret / Anahtar</label>
<input id="secret" type="text" placeholder="Örn: JBSWY3DPEHPK3PXP" autocomplete="off" spellcheck="false" />
<div class="hint">Base32 formatı desteklenir. Boşluk ve tireler otomatik temizlenir.</div>
</div>
</div>
<div class="grid-2">
<div>
<label for="digits">Hane</label>
<select id="digits">
<option value="6" selected>6</option>
</select>
</div>
<div>
<label for="period">Süre (sn)</label>
<select id="period">
<option value="30" selected>30</option>
<option value="60">60</option>
</select>
</div>
</div>
<div class="actions">
<button id="generateBtn">Kodu Üret</button>
<button id="copyBtn" class="secondary" type="button">Kopyala</button>
</div>
<div id="error" class="error"></div>
<section class="result">
<div class="meta">Aktif tek kullanımlık kod</div>
<div id="code" class="code">------</div>
<div id="meta" class="meta">Secret girildiğinde otomatik güncellenir.</div>
<div class="bar"><div id="progress"></div></div>
</section>
<div class="footer">
İstersen URL ile de kullanabilirsin: <code class="inline">?secret=JBSWY3DPEHPK3PXP</code>
</div>
</main>
<script>
const secretInput = document.getElementById('secret');
const digitsSelect = document.getElementById('digits');
const periodSelect = document.getElementById('period');
const codeEl = document.getElementById('code');
const metaEl = document.getElementById('meta');
const errorEl = document.getElementById('error');
const progressEl = document.getElementById('progress');
const generateBtn = document.getElementById('generateBtn');
const copyBtn = document.getElementById('copyBtn');
let timer = null;
let lastCode = '';
function showError(message = '') {
errorEl.textContent = message;
errorEl.style.display = message ? 'block' : 'none';
}
function normalizeSecret(secret) {
return (secret || '').toUpperCase().replace(/[\s-]+/g, '');
}
function base32ToBytes(base32) {
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
const cleaned = normalizeSecret(base32).replace(/=+$/g, '');
if (!cleaned) return new Uint8Array();
let bits = '';
for (const char of cleaned) {
const val = alphabet.indexOf(char);
if (val === -1) {
throw new Error('Secret geçersiz görünüyor. Base32 karakterleri bekleniyor.');
}
bits += val.toString(2).padStart(5, '0');
}
const bytes = [];
for (let i = 0; i + 8 <= bits.length; i += 8) {
bytes.push(parseInt(bits.slice(i, i + 8), 2));
}
return new Uint8Array(bytes);
}
async function hmacSha1(keyBytes, messageBytes) {
const cryptoKey = await crypto.subtle.importKey(
'raw',
keyBytes,
{ name: 'HMAC', hash: 'SHA-1' },
false,
['sign']
);
const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageBytes);
return new Uint8Array(signature);
}
function intToBytes(counter) {
const bytes = new Uint8Array(8);
let value = BigInt(counter);
for (let i = 7; i >= 0; i--) {
bytes[i] = Number(value & 0xffn);
value >>= 8n;
}
return bytes;
}
async function generateTOTP(secret, digits = 6, period = 30, timestamp = Date.now()) {
const key = base32ToBytes(secret);
if (!key.length) {
throw new Error('Lütfen bir secret key gir.');
}
const counter = Math.floor(timestamp / 1000 / period);
const counterBytes = intToBytes(counter);
const hash = await hmacSha1(key, counterBytes);
const offset = hash[hash.length - 1] & 0x0f;
const binary =
((hash[offset] & 0x7f) << 24) |
((hash[offset + 1] & 0xff) << 16) |
((hash[offset + 2] & 0xff) << 8) |
(hash[offset + 3] & 0xff);
const otp = (binary % (10 ** digits)).toString().padStart(digits, '0');
const secondsRemaining = period - (Math.floor(timestamp / 1000) % period);
return {
otp,
secondsRemaining,
counter,
};
}
async function refreshCode() {
const secret = secretInput.value;
const digits = Number(digitsSelect.value);
const period = Number(periodSelect.value);
const now = Date.now();
if (!normalizeSecret(secret)) {
codeEl.textContent = '-'.repeat(digits);
metaEl.textContent = 'Secret girildiğinde otomatik güncellenir.';
progressEl.style.width = '0%';
showError('');
lastCode = '';
return;
}
try {
const { otp, secondsRemaining } = await generateTOTP(secret, digits, period, now);
lastCode = otp;
codeEl.textContent = otp;
metaEl.textContent = `${secondsRemaining} saniye sonra yenilenecek`;
const elapsed = period - secondsRemaining;
progressEl.style.width = `${(elapsed / period) * 100}%`;
showError('');
} catch (err) {
codeEl.textContent = '!'.repeat(digits);
metaEl.textContent = 'Kod üretilemedi.';
progressEl.style.width = '0%';
showError(err.message || 'Beklenmeyen bir hata oluştu.');
lastCode = '';
}
}
function restartTimer() {
if (timer) clearInterval(timer);
refreshCode();
timer = setInterval(refreshCode, 250);
}
generateBtn.addEventListener('click', refreshCode);
secretInput.addEventListener('input', restartTimer);
digitsSelect.addEventListener('change', restartTimer);
periodSelect.addEventListener('change', restartTimer);
copyBtn.addEventListener('click', async () => {
if (!lastCode) return;
try {
await navigator.clipboard.writeText(lastCode);
const original = copyBtn.textContent;
copyBtn.textContent = 'Kopyalandı';
setTimeout(() => { copyBtn.textContent = original; }, 1200);
} catch {
showError('Panoya kopyalama başarısız oldu.');
}
});
function loadFromQuery() {
const params = new URLSearchParams(location.search);
const secret = params.get('secret') || '';
const digits = params.get('digits');
const period = params.get('period');
if (secret) secretInput.value = secret;
if (digits && ['6', '8'].includes(digits)) digitsSelect.value = digits;
if (period && ['30', '60'].includes(period)) periodSelect.value = period;
}
loadFromQuery();
restartTimer();
</script>
</body>
</html>
Demo : https://2fa.grandkingbet.com/
💬 SpyHackerz Telegram — Anlık tartışmalar ve duyurular için katıl