feat: add passwordless passkey-only account UI#56
Conversation
Add UI support for passwordless registration, password removal, set-password mode, and auth methods display. New auth-methods.js utility, passwordless toggle on registration page, set-password mode on change-password page, and auth methods card with remove password flow on profile page.
The deleteTestUser endpoint was failing with FK constraint violation because demo_user_profile rows weren't cleaned up before deleting the user_account. This caused all Playwright tests that create users to fail during cleanup.
- Registration page: toggle visibility, password field hiding, required attribute toggling, correct endpoint targeting, active state switching - Profile page: auth methods card display, password badge, remove password button visibility, set password link visibility, passkey management section, empty passkeys message - Change password page: current password field for password users - Navigation: change password link text verification
Replace unreliable waitForLoadState('networkidle') with explicit
waitForResponse() in the "should reject change with wrong current
password" test. Since the form uses fetch() (AJAX), networkidle can
resolve before the DOM is updated from the response, causing the
#globalMessage visibility check to fail intermittently.
The new approach:
- Intercepts the server response to /user/updatePassword
- Asserts the server correctly returns success=false
- Waits for the #globalMessage div to become visible
There was a problem hiding this comment.
Pull request overview
This pull request adds comprehensive support for passwordless passkey-only account registration and management, enabling users to create and use accounts without traditional passwords.
Changes:
- Introduces passwordless registration flow with passkey-only option on registration page
- Updates password management pages to detect and adapt to passwordless accounts (showing "Set a Password" vs "Change Password")
- Adds authentication methods UI card to user profile showing configured auth methods and allowing password removal when passkeys exist
- Improves test reliability by replacing flaky
waitForLoadState('networkidle')with response interception - Fixes test data cleanup FK constraint violations by deleting DemoUserProfile before User
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/main/resources/templates/user/update-user.html | Adds authentication methods display card and password removal modal with confirmation |
| src/main/resources/templates/user/update-password.html | Adds UI elements for "Set a Password" mode (title, info alert, conditionally hidden current password field) |
| src/main/resources/templates/user/register.html | Adds passwordless registration mode toggle and conditional password field visibility |
| src/main/resources/static/js/user/webauthn-manage.js | Implements auth methods UI updates, password removal functionality, and cache invalidation |
| src/main/resources/static/js/user/update-password.js | Detects passwordless accounts and switches to set password mode with separate endpoint |
| src/main/resources/static/js/user/register.js | Adds passwordless registration mode toggle logic and separate registration endpoint |
| src/main/resources/static/js/user/auth-methods.js | New utility module for fetching and caching user authentication methods |
| src/main/resources/application.yml | Adds /user/registration/passwordless to unprotected URIs for public access |
| src/main/java/com/digitalsanctuary/spring/demo/test/api/TestDataController.java | Fixes FK constraint violations by deleting DemoUserProfile before User during test cleanup |
| playwright/tests/profile/change-password.spec.ts | Improves test reliability by using response interception instead of networkidle |
| playwright/tests/profile/auth-methods.spec.ts | New comprehensive tests for authentication methods display and functionality |
| playwright/tests/auth/passwordless-registration.spec.ts | New comprehensive tests for passwordless registration flow |
| build.gradle | Updates dependency to 4.2.1-SNAPSHOT for passwordless support from library |
Comments suppressed due to low confidence (2)
src/main/resources/static/js/user/webauthn-manage.js:309
- Consider displaying a user-facing error message when auth methods cannot be loaded, rather than just logging to the console. Users should be informed if this critical information fails to load. You could show a message in the auth-methods-section or use the globalMessage element to notify the user of the failure.
} catch (error) {
console.error('Failed to update auth methods UI:', error);
}
src/main/resources/templates/user/update-user.html:81
- The confirmation input field should have an aria-label attribute for better accessibility. Screen reader users need to know what to type in this field. Consider adding aria-label="Confirmation input" or aria-describedby pointing to the instruction text.
<input type="text" id="removePasswordConfirmInput" class="form-control" placeholder="Type REMOVE">
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| badges += `<span class="badge bg-success me-2"><i class="bi bi-key me-1"></i>Passkeys (${auth.passkeysCount})</span>`; | ||
| } | ||
| if (auth.provider && auth.provider !== 'LOCAL') { | ||
| badges += `<span class="badge bg-info me-2"><i class="bi bi-cloud me-1"></i>${auth.provider}</span>`; |
There was a problem hiding this comment.
The auth.provider value should be escaped using escapeHtml() before being inserted into the HTML to prevent potential XSS attacks. While provider values typically come from trusted sources (OAuth providers), it's safer to sanitize all user-facing data, especially when using innerHTML.
| badges += `<span class="badge bg-info me-2"><i class="bi bi-cloud me-1"></i>${auth.provider}</span>`; | |
| badges += `<span class="badge bg-info me-2"><i class="bi bi-cloud me-1"></i>${escapeHtml(auth.provider)}</span>`; |
- Escape auth.provider in HTML to prevent XSS (A1) - Add aria-label to remove-password confirm input (A2) - Show user-facing error when auth methods fail to load (A3)
Two issues causing intermittent failures under parallel load:
1. Race between dialog.accept() and location.reload(): After accepting
the alert dialog, waitForLoadState('networkidle') could resolve in
the gap before the reload navigation started. Fixed by listening for
the page 'load' event before accepting the dialog.
2. Server-side stale reads: The registration API returns 200 but the
subsequent server-rendered page occasionally shows stale state under
concurrent load. Fixed by retrying the page reload once if the API
confirmed success but the page hasn't caught up.
Also made register()/unregister() return success boolean based on the
dialog message, so tests can distinguish server failures from UI bugs.
Summary
auth-methods.jsutility for fetching the user's configured auth methodsTestDataControllerto deleteDemoUserProfilebefore user to avoid FK constraint violationswaitForLoadState('networkidle')Depends on: devondragon/SpringUserFramework PR for passwordless passkey-only account support.
Test plan
npx playwright test --project=chromium