Код: Выделить всё
const btn = document.getElementById('test');
btn.addEventListener("click", () => {
registerUser("--USER--");
})
const { startRegistration } = SimpleWebAuthnBrowser;
async function registerUser(username) {
console.log('[REGISTER] started');
const resp = await fetch('http://localhost:3000/generate-registration-options', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ username }),
});
console.log('[REGISTER] options response status', resp.status);
const data = await resp.json();
console.log('[REGISTER] options payload', data);
const { options, userId } = data;
console.log('[REGISTER] calling startRegistration');
let credential;
try {
credential = await startRegistration({ optionsJSON: options }); //stops here -> no output, doesnt add keys to database.json
console.log('[REGISTER] credential returned', credential);
} catch (err) {
console.error('[REGISTER] startRegistration ERROR', err);
return;
}
console.log('[REGISTER] verifying credential');
const verifyResp = await fetch('http://localhost:3000/verify-registration', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ userId, credential, username }),
});
const result = await verifyResp.json();
console.log('[REGISTER] verification result', result);
}
Код: Выделить всё
app.post('/generate-registration-options', async (req, res) => {
try {
const { username } = req.body;
if (!username) {
return res.status(400).json({ error: 'Username required' });
}
let user = await getUserByUsername(username);
if (!user) {
user = await createUser(username);
}
const options = await webAuthN.generateRegistrationOptions({
rpName,
rpID,
userID: isoUint8Array.isoUint8Array.fromUTF8String(user.id),
userName: user.username,
timeout: 60000,
attestationType: 'none',
authenticatorSelection: {
authenticatorAttachment: 'cross-platform',
userVerification: 'preferred',
},
excludeCredentials: user.passkeys.map(pk => ({
id: pk.id,
type: 'public-key',
transports: pk.transports,
})),
});
await setChallenge(user.id, options.challenge);
res.json({ options, userId: user.id });
} catch (err) {
console.error('Registration options error:', err);
res.status(500).json({ error: err.message });
}
});
app.post('/verify-registration', async (req, res) => {
try {
const { userId, credential } = req.body;
if (!userId || !credential) {
return res.status(400).json({ error: 'Missing data' });
}
const user = await getUserById(userId);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
const expectedChallenge = await getChallenge(userId);
if (!expectedChallenge) {
return res.status(400).json({ error: 'Challenge expired' });
}
let verification;
try {
verification = await webAuthN.verifyRegistrationResponse({
response: credential,
expectedChallenge,
expectedOrigin: origin,
expectedRPID: rpID,
});
} catch (err) {
console.error(err);
return res.status(400).json({ error: err.message });
}
const { verified, registrationInfo } = verification;
if (!verified) {
return res.status(400).json({ verified: false });
}
const {
credentialPublicKey,
credentialID,
counter,
transports,
} = registrationInfo;
await addPasskeyToUser(user.id, {
id: credentialID,
publicKey: Array.from(credentialPublicKey),
counter,
transports,
});
await clearChallenge(user.id);
res.json({
verified: true,
message: 'Passkey registered',
});
} catch (err) {
console.error('Registration verify error:', err);
res.status(500).json({ error: err.message });
}
});
Подробнее здесь: https://stackoverflow.com/questions/798 ... -finishing