feat: upgrade WebAuthnKit to v3.0.0#24
Open
elukewalker wants to merge 23 commits into
Open
Conversation
- Upgrade webauthn-server-core 2.0.0 → 2.9.0 with custom Gson TypeAdapters
(ByteArray, Instant, Optional) for Java 17 module system compatibility
- Upgrade Lambda runtimes: java8.al2 → java17, nodejs16.x → nodejs20.x
- Migrate all Node.js Lambdas from AWS SDK v2 to v3
- Add passkey autofill (conditional mediation) via new /passkey route
- Add autoComplete="username webauthn" hint to login page
- Remove FIDO Metadata Service (MDS) integration
- Add parseResidentKey() for backward-compatible resident key handling
- Fix credentials === {} object comparison bug in HomePage
- Add BDD test suite: 24 Cucumber.js + Playwright scenarios
- Bump all versions to 3.0.0, add CHANGELOG.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace openkbs/jdk-mvn-py3 base image with ubuntu:22.04 - Install OpenJDK 17, Node.js 20, Maven 3.9, SAM CLI, AWS CLI v2 - Mount ~/.aws/sso in deploy script for SSO credential support Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- backend/template.yaml: replace deprecated EngineMode:serverless with EngineMode:provisioned + ServerlessV2ScalingConfiguration + db.serverless instance resource (Aurora Serverless v1 retired by AWS, new clusters unavailable) - scripts/Mac-Linux/deployStarterKit.sh: remove :ro from SSO cache volume mount so SAM CLI can write temporary token files during sam deploy Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
aws-sdk v2 is not bundled in nodejs20.x Lambda runtime, causing the CFN custom resource to fail with 'Cannot find module aws-sdk' and never respond to CloudFormation. Migrated to @aws-sdk/client-lambda (v3). Also increased Timeout from 20s to 60s to allow schema creation time. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cy requirement aws amplify start-deployment with s3:// source URL requires the Amplify service to have a bucket policy granting it access. Private buckets fail silently with UnauthorizedException. Generate a 1-hour presigned URL instead — no bucket policy needed, works with default private buckets. Also removed &> /dev/null suppression so deployment errors are visible. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FIDO2KitAPI/DatabaseController.js and CreateAuth/CreateAuthChallengeFIDO2.js both required a local './db-client' file that does not exist. Should use the 'data-api-client' npm package already listed in package.json deps. VerifyAuth and CreateDBSchema were already using the correct import. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All four Node Lambda functions (FIDO2KitAPI, CreateAuth, VerifyAuth, CreateDBSchema) use the data-api-client npm package for RDS Data API access but several were missing it from their declared dependencies, causing Runtime.ImportModuleError on Node 20. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
data-api-client v1 internally requires aws-sdk v2, which is not bundled in the Node 20 Lambda runtime. Replace with a local db-client.js shim in each Lambda function that wraps @aws-sdk/client-rds-data (bundled in Node 20) and exposes the identical .query(sql, params) interface. No call-site changes needed in DatabaseController.js or other callers — the shim is a drop-in replacement. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deploy script: - Remove > /dev/null 2>&1 from mvn, SAM, and npm build steps so errors surface instead of silently failing and deploying broken artifacts Registration flow: - Move defaultData inside IdentifierFirstSignUpFlow component so localStorage.getItem() runs at render time, not module load - InitUserStep navigates directly to '/' after auth tokens are set, skipping the now-unnecessary RegisterKeySuccessStep click - Guarantee a digit in the random Cognito password to satisfy RequireNumbers policy Credential display: - Guard credential.lastUsedTime null check in Credential.tsx - Use optional chaining + fallback for registrationRequest.requireResidentKey → residentKey rename in RegistrationRequest (v3.0.0 upgrade) BDD tests: - h3 → h5 selector for "Security Keys" heading (dashboard uses h5) - Update credential locator to match actual DOM (button, not ul/li/a) - I click Cancel step now falls back to span.btn-link "Log In" on /register Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- credentials.steps.js: replace ul/li/anchor credential selectors with
.card button:has-text("Edit") to match actual div-based credential rendering
- credentials.steps.js: fix h3->h5 heading and add recovery codes modal
dismissal in 'I am on the dashboard' step
- common.steps.js: fix sign-out selector (a->button, Logout->Sign Out)
and dashboard greeting (h1->h2, Hi->Welcome)
- recovery.steps.js: fix sign-out selector and close-button text ("Not now")
- recovery-exhaustion.steps.js: fix sign-out selector and close-button text
- server-verified-pin.steps.js: fix sign-out selector, modal title
("U2F Password" not "Enter Server-Verified PIN"), and save button
("Submit" not "OK")
- add-credential.steps.js: handle AddCredential modal with no nickname
input field; fix credential count check to use button selector
- helpers.js: fix signOutUser, captureRecoveryCodes, dismissRecoveryCodesModal
to use correct button text
… tests "Not now, ask me again later" only hides the modal in React state so it re-opens on the next getAll() reload, intercepting pointer events on other modals (Delete, Change PIN). "Ignore, and don't ask again" sets localStorage which prevents re-open for the browser session. Also adds Recovery Codes modal dismissal to the 'I am signed in' step in server-verified-pin.steps.js so the Change PIN modal is unobstructed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…condition Using isVisible() immediately after navigation misses the modal if React hasn't rendered it yet. Use Promise.race to wait for either the modal or the credential list before deciding whether to dismiss. Affects I am on the dashboard, I am signed in (svp), recovery-exhaustion loop, and captureRecoveryCodes (now waits for hidden state before returning). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…austion loop Two root-cause fixes: 1. Recovery Codes modal fires in a useEffect AFTER credentials load, so waitForDashboardReady must wait for creds first, then wait up to 4s for the modal to appear before checking/dismissing. This eliminates the race on account-deletion where the modal appeared after waitForDashboardReady already returned. 2. recovery-exhaustion loop must use "Not now" (not "Ignore") so localStorage is never set — the exhaustion warning modal fires only when allRecoveryCodesUsed === true AND ignoreModal === false. Using Ignore permanently suppresses the modal, breaking the exhaustion assertion. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…overyCodes
Two root-cause fixes:
1. account-deletion: DeleteUser.tsx renders the Delete button in Modal.Footer
(not Modal.Body). The selector .modal-body button:has-text("Delete") never
matched; changed to .modal-footer button:has-text("Delete").
2. recovery-exhaustion: captureRecoveryCodes was clicking Ignore which sets
localStorage recoveryCodesModal=true, making ignoreModal=true for the
rest of the browser session. RecoveryCodes useEffect gate is:
(!recoveryCodesViewed || allRecoveryCodesUsed) && !ignoreModal — so
the exhaustion modal never fired. Restored Not now (no localStorage write).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the AddCredential modal submits without a nickname field (the field was removed in v3.0.0), FIDO2KitAPI.js passed undefined to the Java Lambda which threw a NullPointerException (HTTP 500). Default to 'Security Key'. Also update package-lock.json version to 3.0.0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
JavaWebAuthnLib uses BuildMethod: makefile in the SAM template. The makefile build method invokes make to copy the pre-built JAR into SAM's ARTIFACTS_DIR. Without make in the container, sam build fails with 'Path resolution for runtime: provided of binary: make was not successful'. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sam deploy --s3-bucket handles artifact packaging inline. The standalone sam package step was missing --s3-bucket and --region, causing it to fail. It was also discarding its output to /dev/null so the packaged template was never used. Removing it entirely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Platform passkeys were being categorized as security keys because FIDO2KitAPI.js was dropping requireAuthenticatorAttachment before forwarding to the Java Lambda. App.java now reads this field and sets AuthenticatorSelectionCriteria.authenticatorAttachment so the backend stores PLATFORM in the credential, allowing HomePage.tsx to correctly categorize it as a trusted device.
HomePage.tsx checked authenticatorAttachment === "PLATFORM" but the Java library serializes it as "platform" (lowercase), so trusted devices always appeared as security keys. Changed to case-insensitive comparison. WebAuthnClient.ts called requestUV(challengeResponse) in the UV=false path, but AddTrustedDevice passes null for requestUV. Added a null guard with a descriptive error instead of a TypeError crash.
In webauthn-server-core 2.x, requireResidentKey (boolean) was replaced by residentKey (enum string). EditTrustedDevice was calling .toString() on the old field without optional chaining, crashing with TypeError after a trusted device was added. Applied the same safe pattern already used in EditCredential.
Commit 246f25b (Cody Salas) removed the nickname input from AddTrustedDevice but left no smart default, causing all credentials to fall back to "Security Key". Now that requireAuthenticatorAttachment is forwarded, use it to select the right default nickname.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR upgrades WebAuthnKit to v3.0.0, bringing the project up to date with modern AWS runtimes, the latest Yubico webauthn-server-core library, passkey autofill support, and a full BDD test suite.
Backend (Java Lambda)
ByteArrayTypeAdapter,InstantTypeAdapter,OptionalTypeAdapterFactory) for Java 17 module system compatibilityparseResidentKey()for backward-compatible resident key handlingAssertionRequestStorageandRDSRegistrationStoragefor 2.x API changesNode.js Lambdas
@aws-sdk/client-*)data-api-client→@aws-sdk/client-rds-data)React Frontend
/passkeyroute andPasskeyLoginPagecomponentautoComplete="username webauthn"hint to login input for browser passkey suggestionscredentials === {}object comparison bug inHomePageInfrastructure & Deployment
CreateDBSchemaFunctionCallerinline Lambda to AWS SDK v3Testing
Test plan
docker build --platform linux/amd64 -t starterkit:dev scripts/Mac-Linux/mvn testinbackend/lambda-functions/JavaWebAuthnLib/./deployStarterKit.shnpx cucumber-js(requires deployed stack + virtual authenticator browser)/passkeyroute in Chrome/SafariBreaking changes
🤖 Generated with Claude Code