WebAuthn Passkey Nedir? 30 Dakikada Şifresiz Giriş
WebAuthn passkey ile şifre derdini bitirin: phishing’e dayanıklı, hızlı ve modern giriş akışını adım adım kurun.
WebAuthn Passkey Nedir? 30 Dakikada Şifresiz Giriş
Meta Description
WebAuthn passkey ile şifresiz ve phishing’e dayanıklı giriş kurun. Node.js örneği, adımlar, ipuçları ve canlıya çıkış kontrol listesi.
Giriş (Introduction)
Şifreler hem kullanıcıyı yorar hem de işinizi büyüdükçe güvenlik ekibinin kabusu olur: unutulan şifreler, parola sıfırlama e-postaları, credential stuffing saldırıları ve phishing… Üstelik “güçlü şifre” dayatması dönüşümü düşürür.
WebAuthn passkey (geçiş anahtarı) ile kullanıcıya şifre yazdırmadan giriş yaptırabilir, aynı zamanda klasik 2FA akışlarından bile daha güvenli bir temel kurabilirsiniz. Çünkü passkey, cihazın güvenli donanımında saklanan anahtarla çalışır ve siteye özel kriptografi kullanır.
Bu yazıda WebAuthn passkey’in ne olduğunu, neden önemli olduğunu ve bir web uygulamasına nasıl entegre edeceğinizi adım adım, gerçekçi bir akışla anlatıyorum.
WebAuthn Passkey Nedir?
WebAuthn passkey, FIDO2 standardının tarayıcı tarafındaki API’si olan WebAuthn üzerinden çalışan, kullanıcıların şifresiz kimlik doğrulamasını sağlayan yöntemdir.
Temel fikir:
- Sunucu, kullanıcı için bir public key saklar.
- Kullanıcının cihazında (telefon, bilgisayar, güvenlik anahtarı) private key güvenli şekilde saklanır.
- Girişte sunucu bir “challenge” üretir, cihaz bu challenge’ı imzalar.
- Sunucu imzayı doğrular ve kullanıcıyı içeri alır.
Önemli: Passkey’ler phishing’e dayanıklıdır çünkü imzalama işlemi domain’e bağlıdır. Sahte bir sitede aynı passkey çalışmaz.
Neden WebAuthn Passkey Kullanmalıyım?
“Bunu neden yapmalıyım?” sorusunun net yanıtı: güvenlik + kullanıcı deneyimi + maliyet.
Avantajlar
- Phishing ve credential stuffing’e karşı güçlü
- Parola reset yükünü düşürür (support maliyeti azalır)
- Mobilde Face ID / Touch ID ile tek dokunuş
- 2FA’ya göre daha akıcı bir deneyim (çoğu senaryoda ekstra kod yok)
Dezavantajlar / dikkat edilmesi gerekenler
- Eski tarayıcı/cihaz desteği sınırlı olabilir
- İlk kurulum (registration) akışı doğru tasarlanmalı
- “Cihaz değiştirdim” senaryosu için fallback şart (e-posta linki, OTP, vs.)
WebAuthn Passkey Nasıl Çalışır? (Kısa Teknik Özet)
Aşağıdaki kavramlar entegrasyonda işinizi kolaylaştırır:
| Terim | Ne işe yarar? |
|---|---|
| RP (Relying Party) | Kimlik doğrulayan servis (sizin domain’iniz) |
| Challenge | Tek kullanımlık rastgele veri; replay attack önler |
| Credential ID | Cihazın oluşturduğu kimlik; sunucuda saklanır |
| Public Key | Sunucuda saklanır, doğrulamada kullanılır |
| Attestation | Credential’ın nasıl oluşturulduğuna dair kanıt (opsiyonel) |
Akış iki parçadır:
- Kayıt (Registration): Kullanıcı cihazında passkey oluşturur.
- Giriş (Authentication): Sunucu challenge verir, cihaz imzalar.
Adım Adım WebAuthn Passkey Entegrasyonu (Node.js + Frontend)
Bu bölümde hedef: Çalışan bir WebAuthn passkey kayıt ve giriş akışını kurmak.
Not: Üretimde sağlam ve hızlı ilerlemek için genelde
@simplewebauthnailesi tercih edilir.
1) Gereksinimler ve Ön Koşullar
- HTTPS zorunlu (localhost hariç)
- Domain net olmalı:
example.comgibi - Session veya temporary store: challenge saklamak için
Kurulum:
npm i express express-session @simplewebauthn/server
2) Backend: Registration (Kayıt) Options Üret
Sunucu, tarayıcının passkey oluşturması için gerekli “options”u üretir.
import express from 'express';
import session from 'express-session';
import {
generateRegistrationOptions,
verifyRegistrationResponse,
generateAuthenticationOptions,
verifyAuthenticationResponse,
} from '@simplewebauthn/server';
const app = express();
app.use(express.json());
app.use(session({
secret: 'replace-me',
resave: false,
saveUninitialized: true,
cookie: { secure: false }, // prod'da true + HTTPS
}));
// Demo user store (prod'da DB)
const users = new Map();
// users.get(userId) => { id, email, credentials: [{ credentialID, publicKey, counter }] }
const rpName = 'Acme App';
const rpID = 'localhost'; // prod: example.com
const origin = 'http://localhost:3000'; // prod: https://example.com
app.post('/webauthn/register/options', (req, res) => {
const { userId, email } = req.body;
const user = users.get(userId) ?? { id: userId, email, credentials: [] };
const options = generateRegistrationOptions({
rpName,
rpID,
userID: user.id,
userName: user.email,
timeout: 60000,
attestationType: 'none',
excludeCredentials: user.credentials.map(c => ({
id: c.credentialID,
type: 'public-key',
})),
authenticatorSelection: {
userVerification: 'preferred',
},
});
req.session.currentChallenge = options.challenge;
users.set(userId, user);
res.json(options);
});
3) Frontend: Passkey Oluştur (navigator.credentials.create)
Tarayıcı tarafı, sunucudan options alır ve passkey’i oluşturur.
import { startRegistration } from '@simplewebauthn/browser';
export async function createPasskey(userId, email) {
const optRes = await fetch('/webauthn/register/options', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, email }),
});
const options = await optRes.json();
const attResp = await startRegistration(options);
const verRes = await fetch('/webauthn/register/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, attResp }),
});
return verRes.json();
}
Kurulum:
npm i @simplewebauthn/browser
4) Backend: Registration Verify (Kayıt Doğrulama)
Sunucu, tarayıcının döndürdüğü yanıtı doğrular ve public key + credentialID’yi saklar.
app.post('/webauthn/register/verify', async (req, res) => {
const { userId, attResp } = req.body;
const user = users.get(userId);
try {
const verification = await verifyRegistrationResponse({
response: attResp,
expectedChallenge: req.session.currentChallenge,
expectedOrigin: origin,
expectedRPID: rpID,
});
const { verified, registrationInfo } = verification;
if (verified && registrationInfo) {
const { credential, credentialPublicKey, counter } = registrationInfo;
user.credentials.push({
credentialID: credential.id,
publicKey: credentialPublicKey,
counter,
});
users.set(userId, user);
}
res.json({ verified });
} catch (e) {
res.status(400).json({ verified: false, error: String(e) });
}
});
5) Login: Authentication Options Üret
Kullanıcı girişe bastığında sunucu challenge üretir.
app.post('/webauthn/login/options', (req, res) => {
const { userId } = req.body;
const user = users.get(userId);
if (!user) return res.status(404).json({ error: 'User not found' });
const options = generateAuthenticationOptions({
rpID,
timeout: 60000,
userVerification: 'preferred',
allowCredentials: user.credentials.map(c => ({
id: c.credentialID,
type: 'public-key',
})),
});
req.session.currentChallenge = options.challenge;
res.json(options);
});
Frontend:
import { startAuthentication } from '@simplewebauthn/browser';
export async function loginWithPasskey(userId) {
const optRes = await fetch('/webauthn/login/options', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId }),
});
const options = await optRes.json();
const asseResp = await startAuthentication(options);
const verRes = await fetch('/webauthn/login/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, asseResp }),
});
return verRes.json();
}
6) Backend: Login Verify (Giriş Doğrulama)
Burada imza doğrulanır, sayaç (counter) güncellenir.
app.post('/webauthn/login/verify', async (req, res) => {
const { userId, asseResp } = req.body;
const user = users.get(userId);
if (!user) return res.status(404).json({ verified: false });
const cred = user.credentials.find(c => c.credentialID === asseResp.id);
if (!cred) return res.status(400).json({ verified: false, error: 'Unknown credential' });
try {
const verification = await verifyAuthenticationResponse({
response: asseResp,
expectedChallenge: req.session.currentChallenge,
expectedOrigin: origin,
expectedRPID: rpID,
authenticator: {
credentialID: cred.credentialID,
credentialPublicKey: cred.publicKey,
counter: cred.counter,
},
});
const { verified, authenticationInfo } = verification;
if (verified && authenticationInfo) {
// Replay/clone riskini azaltmak için counter güncelle
cred.counter = authenticationInfo.newCounter;
users.set(userId, user);
// Burada session/JWT üretip login edebilirsiniz
req.session.userId = userId;
}
res.json({ verified });
} catch (e) {
res.status(400).json({ verified: false, error: String(e) });
}
});
Gerçek Hayat Senaryosu: SaaS Panelinde Passkey
Bir B2B SaaS yönetim paneli düşünün:
- Müşteri şirket çalışanları sık sık giriş yapıyor.
- Parola unutma talepleri artıyor.
- Phishing e-postalarıyla hesaplar hedefleniyor.
WebAuthn passkey eklediğinizde:
- Çalışanlar Face ID/Windows Hello ile 1-2 saniyede giriş yapar.
- Support ekibinin parola sıfırlama yükü azalır.
- Phishing kaynaklı hesap ele geçirmeler ciddi oranda düşer.
Bu noktada iyi bir ürün kararı:
- İlk etapta “Passkey ile giriş (önerilen)” + “E-posta ile giriş linki (yedek)”
- Daha sonra şifreyi tamamen opsiyonel hâle getirmek
Üretime Çıkmadan Önce Kontrol Listesi
Aşağıdakiler entegrasyonun “gerçekten güvenli” olmasını sağlar:
- HTTPS zorunlu (prod)
-
rpIDdomain ile birebir uyumlu (subdomain planınızı netleştirin) - Challenge’ı session/redis gibi yerde kısa süreli saklayın
- Kullanıcının birden fazla credential’ını destekleyin (telefon + laptop)
- Fallback akışı ekleyin (cihaz kaybı/erişilemeyen passkey)
-
countergüncellemesi ve doğrulaması aktif - Hata mesajlarında fazla detay sızdırmayın
Sık Sorulan Sorular (FAQ)
1) WebAuthn passkey ile 2FA aynı şey mi?
Hayır. Passkey, kriptografik anahtar tabanlı kimlik doğrulama yöntemidir. Çoğu senaryoda 2FA gibi ek adım gerektirmeden güçlü güvenlik sağlar.
2) WebAuthn passkey tüm tarayıcılarda çalışır mı?
Modern tarayıcılarda büyük ölçüde desteklenir. Yine de hedef kitlenize göre destek matrisi kontrol edip fallback eklemelisiniz.
3) Kullanıcı telefonunu değiştirirse ne olur?
Bu yüzden hesap kurtarma (e-posta linki, OTP, destek akışı) şarttır. Ayrıca kullanıcıya birden fazla cihazda passkey ekletmek iyi pratiktir.
4) Passkey’ler sunucuda saklanıyor mu?
Sunucuda private key değil, sadece public key ve credential meta bilgisi saklanır. Bu da veri sızıntısında riski azaltır.
5) WebAuthn passkey girişini şifreyle birlikte kullanabilir miyim?
Evet. En yaygın geçiş stratejisi: Şifre + Passkey (opsiyonel) başlayıp zamanla passkey’i birincil yöntem yapmak.
Sonuç
WebAuthn passkey, şifrelerden kaynaklanan güvenlik ve kullanıcı deneyimi problemlerini aynı anda çözen modern bir yaklaşım. Doğru kurgulanmış bir kayıt/giriş akışıyla phishing’e dayanıklı, hızlı ve destek maliyeti düşük bir kimlik doğrulama sistemi kurabilirsiniz.
Bir sonraki adım: Kendi uygulamanızda önce passkey kayıt ekranını ekleyin, ardından login + fallback akışını tamamlayın. Takıldığınız yerde yorumda hedef stack’inizi (Node, .NET, Java, Go vb.) yazın; aynı akışı o teknolojiyle de örneklendireyim.