Skip to content
This repository was archived by the owner on Jul 28, 2026. It is now read-only.

Commit 9a48179

Browse files
committed
fix: look up passkey by credential_id in login/complete (no userId needed)
- Query passkeys table by credential_id instead of user_id, so the login flow works regardless of whether the passkey is discoverable - Omit allowCredentials entirely (not empty array) for discoverable flow, fixing conditional mediation - Remove userId requirement from login/complete request body - Separate autocomplete attribute (webauthn instead of email webauthn)
1 parent 369d750 commit 9a48179

2 files changed

Lines changed: 10 additions & 23 deletions

File tree

public/js/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ function renderLoginPage() {
518518
<div class="auth-error"></div>
519519
<div class="form-group">
520520
<label for="login-email">Email</label>
521-
<input type="email" id="login-email" placeholder="your@email.com" required autocomplete="email webauthn" />
521+
<input type="email" id="login-email" placeholder="your@email.com" required autocomplete="webauthn" />
522522
</div>
523523
<div class="form-group">
524524
<label for="login-password">Password</label>

routes/passkeys.js

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ router.post('/passkeys/login/begin', async (req, res) => {
141141
try {
142142
const { email } = req.body;
143143
let userId = null;
144-
let allowCredentials = [];
144+
let allowCredentials;
145145

146146
if (email && typeof email === 'string') {
147147
const users = await query('SELECT id, email, username, auth_restricted FROM users WHERE email = ?', [email]);
@@ -174,7 +174,7 @@ router.post('/passkeys/login/begin', async (req, res) => {
174174
const { rpID } = getWebAuthnConfig(req);
175175
const options = await generateAuthenticationOptions({
176176
rpID,
177-
allowCredentials,
177+
...(allowCredentials ? { allowCredentials } : {}),
178178
userVerification: 'preferred',
179179
});
180180

@@ -193,7 +193,7 @@ router.post('/passkeys/login/begin', async (req, res) => {
193193

194194
router.post('/passkeys/login/complete', async (req, res) => {
195195
try {
196-
const { response, userId: bodyUserId } = req.body;
196+
const { response } = req.body;
197197
if (!response) {
198198
return res.status(400).json({ error: 'Response is required' });
199199
}
@@ -206,30 +206,17 @@ router.post('/passkeys/login/complete', async (req, res) => {
206206

207207
challengeMap.delete(challengeFromResponse);
208208

209-
let userId = bodyUserId || expectedChallenge.userId;
210-
if (!userId && response.response.userHandle) {
211-
const userHandleBytes = isoBase64URL.toBuffer(response.response.userHandle);
212-
userId = parseInt(new TextDecoder().decode(userHandleBytes), 10);
213-
}
214-
215-
if (!userId) {
216-
return res.status(400).json({ error: 'Could not identify user. Try logging in with email.' });
217-
}
218-
219209
const passkeys = await query(
220-
'SELECT id, credential_id, public_key, counter, transports FROM passkeys WHERE user_id = ?',
221-
[userId]
222-
);
223-
224-
const credentialId = response.id;
225-
const passkey = passkeys.find(
226-
k => isoBase64URL.fromBuffer(isoBase64URL.toBuffer(k.credential_id)) === credentialId
210+
'SELECT id, credential_id, public_key, counter, transports, user_id FROM passkeys WHERE credential_id = ?',
211+
[response.id]
227212
);
228213

229-
if (!passkey) {
214+
if (!passkeys.length) {
230215
return res.status(400).json({ error: 'Passkey not found' });
231216
}
232217

218+
const passkey = passkeys[0];
219+
233220
const { rpID, origin } = getWebAuthnConfig(req);
234221
const verification = await verifyAuthenticationResponse({
235222
response,
@@ -253,7 +240,7 @@ router.post('/passkeys/login/complete', async (req, res) => {
253240
passkey.id,
254241
]);
255242

256-
const users = await query('SELECT id, email, username, ptero_user_id, is_admin, restricted, token_version FROM users WHERE id = ?', [userId]);
243+
const users = await query('SELECT id, email, username, ptero_user_id, is_admin, restricted, token_version FROM users WHERE id = ?', [passkey.user_id]);
257244
if (!users.length) {
258245
return res.status(404).json({ error: 'User not found' });
259246
}

0 commit comments

Comments
 (0)