Merge pull request #1519 from Lokowitz/webauth

Fix upgrade @simplewebauthn/server from 9.0.3 to 13.2.1
This commit is contained in:
Owen Schwartz
2025-09-28 10:47:06 -07:00
committed by GitHub
8 changed files with 249 additions and 118 deletions

View File

@@ -2,7 +2,7 @@ import path from "path";
import { fileURLToPath } from "url";
// This is a placeholder value replaced by the build process
export const APP_VERSION = "1.10.2";
export const APP_VERSION = "1.10.4";
export const __FILENAME = fileURLToPath(import.meta.url);
export const __DIRNAME = path.dirname(__FILENAME);

View File

@@ -16,18 +16,12 @@ import {
} from "@simplewebauthn/server";
import type {
GenerateRegistrationOptionsOpts,
VerifyRegistrationResponseOpts,
GenerateAuthenticationOptionsOpts,
VerifyAuthenticationResponseOpts,
VerifiedRegistrationResponse,
VerifiedAuthenticationResponse
AuthenticatorTransportFuture
} from "@simplewebauthn/server";
import type {
AuthenticatorTransport,
AuthenticatorTransportFuture,
PublicKeyCredentialDescriptorJSON,
PublicKeyCredentialDescriptorFuture
} from "@simplewebauthn/types";
import {
isoBase64URL
} from '@simplewebauthn/server/helpers';
import config from "@server/lib/config";
import { UserType } from "@server/types/UserTypes";
import { verifyPassword } from "@server/auth/password";
@@ -204,15 +198,14 @@ export async function startRegistration(
.where(eq(securityKeys.userId, user.userId));
const excludeCredentials = existingSecurityKeys.map(key => ({
id: new Uint8Array(Buffer.from(key.credentialId, 'base64')),
type: 'public-key' as const,
id: key.credentialId,
transports: key.transports ? JSON.parse(key.transports) as AuthenticatorTransportFuture[] : undefined
}));
const options: GenerateRegistrationOptionsOpts = {
rpName,
rpID,
userID: user.userId,
userID: isoBase64URL.toBuffer(user.userId),
userName: user.email || user.username,
attestationType: 'none',
excludeCredentials,
@@ -308,11 +301,11 @@ export async function verifyRegistration(
// Store the security key in the database
await db.insert(securityKeys).values({
credentialId: Buffer.from(registrationInfo.credentialID).toString('base64'),
credentialId: registrationInfo.credential.id,
userId: user.userId,
publicKey: Buffer.from(registrationInfo.credentialPublicKey).toString('base64'),
signCount: registrationInfo.counter || 0,
transports: credential.response.transports ? JSON.stringify(credential.response.transports) : null,
publicKey: isoBase64URL.fromBuffer(registrationInfo.credential.publicKey),
signCount: registrationInfo.credential.counter || 0,
transports: registrationInfo.credential.transports ? JSON.stringify(registrationInfo.credential.transports) : null,
name: challengeData.securityKeyName,
lastUsed: new Date().toISOString(),
dateCreated: new Date().toISOString()
@@ -496,7 +489,7 @@ export async function startAuthentication(
const { email } = parsedBody.data;
try {
let allowCredentials: PublicKeyCredentialDescriptorFuture[] = [];
let allowCredentials;
let userId;
// If email is provided, get security keys for that specific user
@@ -533,13 +526,9 @@ export async function startAuthentication(
}
allowCredentials = userSecurityKeys.map(key => ({
id: new Uint8Array(Buffer.from(key.credentialId, 'base64')),
type: 'public-key' as const,
id: key.credentialId,
transports: key.transports ? JSON.parse(key.transports) as AuthenticatorTransportFuture[] : undefined
}));
} else {
// If no email provided, allow any security key (for resident key authentication)
allowCredentials = [];
}
const options: GenerateAuthenticationOptionsOpts = {
@@ -616,7 +605,7 @@ export async function verifyAuthentication(
}
// Find the security key in database
const credentialId = Buffer.from(credential.id, 'base64').toString('base64');
const credentialId = credential.id;
const [securityKey] = await db
.select()
.from(securityKeys)
@@ -653,9 +642,9 @@ export async function verifyAuthentication(
expectedChallenge: challengeData.challenge,
expectedOrigin: origin,
expectedRPID: rpID,
authenticator: {
credentialID: Buffer.from(securityKey.credentialId, 'base64'),
credentialPublicKey: Buffer.from(securityKey.publicKey, 'base64'),
credential: {
id: securityKey.credentialId,
publicKey: isoBase64URL.toBuffer(securityKey.publicKey),
counter: securityKey.signCount,
transports: securityKey.transports ? JSON.parse(securityKey.transports) as AuthenticatorTransportFuture[] : undefined
},
@@ -714,4 +703,4 @@ export async function verifyAuthentication(
)
);
}
}
}

View File

@@ -11,6 +11,7 @@ import m3 from "./scriptsPg/1.8.0";
import m4 from "./scriptsPg/1.9.0";
import m5 from "./scriptsPg/1.10.0";
import m6 from "./scriptsPg/1.10.2";
import m7 from "./scriptsPg/1.10.4";
// THIS CANNOT IMPORT ANYTHING FROM THE SERVER
// EXCEPT FOR THE DATABASE AND THE SCHEMA
@@ -23,6 +24,7 @@ const migrations = [
{ version: "1.9.0", run: m4 },
{ version: "1.10.0", run: m5 },
{ version: "1.10.2", run: m6 },
{ version: "1.10.4", run: m7 },
// Add new migrations here as they are created
] as {
version: string;

View File

@@ -29,6 +29,7 @@ import m24 from "./scriptsSqlite/1.9.0";
import m25 from "./scriptsSqlite/1.10.0";
import m26 from "./scriptsSqlite/1.10.1";
import m27 from "./scriptsSqlite/1.10.2";
import m28 from "./scriptsSqlite/1.10.4";
// THIS CANNOT IMPORT ANYTHING FROM THE SERVER
// EXCEPT FOR THE DATABASE AND THE SCHEMA
@@ -57,6 +58,7 @@ const migrations = [
{ version: "1.10.0", run: m25 },
{ version: "1.10.1", run: m26 },
{ version: "1.10.2", run: m27 },
{ version: "1.10.4", run: m28 },
// Add new migrations here as they are created
] as const;

View File

@@ -0,0 +1,41 @@
import { db } from "@server/db/pg/driver";
import { sql } from "drizzle-orm";
import { isoBase64URL } from "@simplewebauthn/server/helpers";
const version = "1.10.4";
export default async function migration() {
console.log(`Running setup script ${version}...`);
try {
await db.execute(sql`BEGIN`);
const webauthnCredentialsQuery = await db.execute(sql`SELECT "credentialId", "publicKey" FROM "webauthnCredentials"`);
const webauthnCredentials = webauthnCredentialsQuery.rows as { credentialId: string; publicKey: string }[];
for (const webauthnCredential of webauthnCredentials) {
const credentialId = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.credentialId, 'base64')));
await db.execute(sql`
UPDATE "webauthnCredentials" SET "credentialId" = ${credentialId}
WHERE "credentialId" = ${webauthnCredential.credentialId}
`);
const publicKey = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.publicKey, 'base64')));
await db.execute(sql`
UPDATE "webauthnCredentials" SET "publicKey" = ${publicKey}
WHERE "credentialId" = ${webauthnCredential.credentialId}
`);
}
await db.execute(sql`COMMIT`);
console.log(`Updated credentialId and publicKey`);
} catch (e) {
await db.execute(sql`ROLLBACK`);
console.log("Unable to update credentialId and publicKey");
console.log(e);
throw e;
}
console.log(`${version} migration complete`);
}

View File

@@ -0,0 +1,34 @@
import { APP_PATH } from "@server/lib/consts";
import Database from "better-sqlite3";
import path from "path";
import { isoBase64URL } from "@simplewebauthn/server/helpers";
const version = "1.10.4";
export default async function migration() {
console.log(`Running setup script ${version}...`);
const location = path.join(APP_PATH, "db", "db.sqlite");
const db = new Database(location);
db.transaction(() => {
const webauthnCredentials = db.prepare(`SELECT credentialId, publicKey FROM 'webauthnCredentials'`).all() as {
credentialId: string; publicKey: string
}[];
for (const webauthnCredential of webauthnCredentials) {
const credentialId = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.credentialId, 'base64')));
db.prepare(
`UPDATE 'webauthnCredentials' SET 'credentialId' = ? WHERE 'credentialId' = ?`
).run(credentialId, webauthnCredential.credentialId);
const publicKey = isoBase64URL.fromBuffer(new Uint8Array(Buffer.from(webauthnCredential.publicKey, 'base64')));
db.prepare(
`UPDATE 'webauthnCredentials' SET 'publicKey' = ? WHERE 'credentialId' = ?`
).run(publicKey, webauthnCredential.credentialId);
}
})();
console.log(`${version} migration complete`);
}