Skip to content

Comments

feat: add passwordless passkey-only account UI#56

Open
devondragon wants to merge 7 commits intomainfrom
feature/passwordless-passkey-accounts
Open

feat: add passwordless passkey-only account UI#56
devondragon wants to merge 7 commits intomainfrom
feature/passwordless-passkey-accounts

Conversation

@devondragon
Copy link
Owner

Summary

  • Add passwordless registration option on the registration page (register with passkey only, no password)
  • Update password page detects passwordless accounts and switches to "Set a Password" mode
  • Update user profile page to show auth methods (password, passkeys) and allow removing password if passkeys exist
  • Add auth-methods.js utility for fetching the user's configured auth methods
  • Update library dependency to 4.2.1-SNAPSHOT for passwordless support
  • Fix TestDataController to delete DemoUserProfile before user to avoid FK constraint violations
  • Add Playwright tests for passwordless registration, auth methods display, and change-password reliability
  • Fix change-password test to use response interception instead of unreliable waitForLoadState('networkidle')

Depends on: devondragon/SpringUserFramework PR for passwordless passkey-only account support.

Test plan

  • Manual test: register a new account using passkey only (no password)
  • Manual test: set a password on a passwordless account via the update-password page
  • Manual test: remove password from an account that has passkeys
  • Manual test: verify profile page shows correct auth method indicators
  • Manual test: verify existing password-based registration still works
  • Run full Playwright suite: npx playwright test --project=chromium

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
Copilot AI review requested due to automatic review settings February 24, 2026 13:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>`;
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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>`;

Copilot uses AI. Check for mistakes.
- 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant