Skip to content

Commit 9ea04e4

Browse files
florida117claude
andcommitted
Add WebAuthn/Passkey authentication support
Implements passwordless passkey login alongside the existing email/password flow. Passkeys provide phishing-resistant, multi-factor authentication (device + biometric) and bypass TOTP 2FA when used. Closes #3363 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c2fddee commit 9ea04e4

File tree

27 files changed

+2453
-1016
lines changed

27 files changed

+2453
-1016
lines changed

backend/internal/token.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import authModel from "../models/auth.js";
55
import TokenModel from "../models/token.js";
66
import userModel from "../models/user.js";
77
import twoFactor from "./2fa.js";
8+
import webauthn from "./webauthn.js";
89

910
const ERROR_MESSAGE_INVALID_AUTH = "Invalid email or password";
1011
const ERROR_MESSAGE_INVALID_AUTH_I18N = "error.invalid-auth";
@@ -210,6 +211,39 @@ export default {
210211
};
211212
},
212213

214+
/**
215+
* Verify passkey authentication and return full token
216+
* @param {string} challengeToken
217+
* @param {Object} credential
218+
* @param {string} [expiry]
219+
* @returns {Promise}
220+
*/
221+
getTokenFromPasskey: async (challengeToken, credential, expiry) => {
222+
const Token = TokenModel();
223+
const tokenExpiry = expiry || "1d";
224+
225+
const userId = await webauthn.verifyAuthentication(challengeToken, credential);
226+
227+
const expiryDate = parseDatePeriod(tokenExpiry);
228+
if (expiryDate === null) {
229+
throw new errs.AuthError(`Invalid expiry time: ${tokenExpiry}`);
230+
}
231+
232+
const signed = await Token.create({
233+
iss: "api",
234+
attrs: {
235+
id: userId,
236+
},
237+
scope: ["user"],
238+
expiresIn: tokenExpiry,
239+
});
240+
241+
return {
242+
token: signed.token,
243+
expires: expiryDate.toISOString(),
244+
};
245+
},
246+
213247
/**
214248
* @param {Object} user
215249
* @returns {Promise}

0 commit comments

Comments
 (0)