# CC Soccer D11 - Session Handoff
**Date:** March 21, 2026 (evening)
**Session:** Passkey (WebAuthn) implementation
**Branch:** `feature/passkey-webauthn` (pushed to GitHub)

---

## Current State

### Passkey authentication is WORKING locally

Installed `drupal/wa` 2.0.0-rc7 on the DDEV local environment. Full passkey flow tested and verified:
- Registration via Touch ID on Mac (Chrome)
- Login via "Sign in with Passkey" button (Touch ID, usernameless)
- Redirect to `/my-account` after passkey login (existing `ccsoccer_user_login` hook works)
- Password login and password reset still work normally
- Passkeys tab visible on user profile (`/user/{uid}/passkeys`)

### Branch status
- `feature/passkey-webauthn` — committed and pushed to GitHub
- Ready to merge to `main` and deploy to test.ccsoccer.com

---

## What Changed This Session

### Composer — drupal/wa added
- `drupal/wa:^2.0@RC` added to `composer.json`
- 18 new packages in `composer.lock` including `web-auth/webauthn-lib` 5.2.4, `brick/math`, `spomky-labs/cbor-php`, `spomky-labs/pki-framework`

### Configuration — 3 files added/updated
- `config/sync/wa.settings.yml` — Passkey settings (new)
- `config/sync/views.view.wa_passkeys.yml` — Admin passkey overview view (new)
- `config/sync/core.extension.yml` — `wa` module added to enabled modules

### wa module settings (in wa.settings.yml)
- **Enable Passkey Login:** true
- **Allowed Roles:** All (authenticated, content_editor, administrator, board_member, tournament_director)
- **Allowed Authenticators:** Generic/Software, Chrome on Mac, iCloud Keychain (Apple Passkeys), Microsoft Authenticator for Android, Google Password Manager, Windows Hello (TPM-backed)
- **User Verification:** preferred
- **Resident Key:** required (enables usernameless login via "Sign in with Passkey" button)
- **Enforce User Handle:** false
- **Notification:** Email sent when a new passkey is added to an account

---

## Deploying to test.ccsoccer.com

After merging `feature/passkey-webauthn` to `main`, SSH in and run the standard deploy sequence plus `composer install` (new packages) and `drush cim` (new config):

```bash
ssh ccsoccer
cd ~/public_html/test_ccsoccer_site
git pull

# Install new composer packages (drupal/wa + 17 dependencies)
PATH=/opt/cpanel/ea-php83/root/usr/bin:$PATH /opt/cpanel/ea-php83/root/usr/bin/php /opt/cpanel/composer/bin/composer install --ignore-platform-req=ext-intl

# Run database updates (creates wa module tables)
PATH=/opt/cpanel/ea-php83/root/usr/bin:$PATH /opt/cpanel/ea-php83/root/usr/bin/php vendor/drush/drush/drush.php -r web updb -y

# Import config (enables wa module, applies passkey settings)
PATH=/opt/cpanel/ea-php83/root/usr/bin:$PATH /opt/cpanel/ea-php83/root/usr/bin/php vendor/drush/drush/drush.php -r web cim -y

# Clear caches
PATH=/opt/cpanel/ea-php83/root/usr/bin:$PATH /opt/cpanel/ea-php83/root/usr/bin/php vendor/drush/drush/drush.php -r web cr
```

### Prerequisites for test.ccsoccer.com
- HTTPS with valid SSL certificate (should already be in place)
- PHP 8.2+ with `ext-openssl` (already PHP 8.3 on InMotion)
- `trusted_host_patterns` should include `^test\.ccsoccer\.com$` in `settings.local.php`

### Important notes
- Passkeys registered on DDEV will NOT work on test.ccsoccer.com (domain-bound by design)
- Users must register new passkeys on each domain
- The RP ID will automatically be `test.ccsoccer.com` — passkeys registered there will NOT carry over to `ccsoccer.com` either (consider setting RP ID to `ccsoccer.com` if wa module supports it, so passkeys are portable to production)

---

## Remaining Work

### UI Polish (future session)
- **Login form spacing:** Need CSS to add margin between the "Log in" button and "Other sign in options" fieldset
- **Passkeys tab discovery:** The tab shows on the user profile page but isn't surfaced from My Account dashboard — consider adding a link/card

### Testing Still Needed
- [ ] iPhone testing (Face ID) — requires mkcert CA on iPhone or `ddev share` for local, or test on test.ccsoccer.com directly
- [ ] Multiple passkeys per user (e.g., Mac + iPhone)
- [ ] Delete passkey and verify graceful fallback
- [ ] Commerce checkout with passkey-authenticated user
- [ ] Flood control (repeated failed attempts)
- [ ] Test with non-admin user roles

### Future Decisions (from implementation plan)
- Whether to set RP ID to `ccsoccer.com` on test.ccsoccer.com for passkey portability
- Whether to require passkeys for board member accounts
- User-facing help text about passkeys on My Account page

---

## Architecture Reference

### Decision 6 (ARCHITECTURE_DECISIONS.md)
Passkeys are a **post-launch optional enhancement**. Password login remains for all users. Passkeys sit alongside, not replacing, existing auth. The `drupal/wa` module is not yet under Drupal's security advisory policy — risk is mitigated by the fact that it's purely additive (disable toggle available at `/admin/config/people/wa`).

### Key routes added by wa module
- `/admin/config/people/wa` — Admin settings
- `/admin/people/passkeys` — Admin overview of all registered passkeys
- `/user/{user}/passkeys` — User's passkey management page
- `/wa/register/options`, `/wa/register/verify` — Registration API endpoints
- `/wa/login/options`, `/wa/login/verify` — Login API endpoints

### Rollback
Quick disable (no code change):
```bash
drush config:set wa.settings enable_passkey_login 0 -y
drush cr
```
Full removal:
```bash
drush pm:uninstall wa -y
composer remove drupal/wa
drush cr
```

---

## Server Quick Reference
```bash
# SSH in
ssh ccsoccer

# Deploy to test
cd ~/public_html/test_ccsoccer_site
git pull
PATH=/opt/cpanel/ea-php83/root/usr/bin:$PATH /opt/cpanel/ea-php83/root/usr/bin/php /opt/cpanel/composer/bin/composer install --ignore-platform-req=ext-intl
PATH=/opt/cpanel/ea-php83/root/usr/bin:$PATH /opt/cpanel/ea-php83/root/usr/bin/php vendor/drush/drush/drush.php -r web updb -y
PATH=/opt/cpanel/ea-php83/root/usr/bin:$PATH /opt/cpanel/ea-php83/root/usr/bin/php vendor/drush/drush/drush.php -r web cim -y
PATH=/opt/cpanel/ea-php83/root/usr/bin:$PATH /opt/cpanel/ea-php83/root/usr/bin/php vendor/drush/drush/drush.php -r web cr
```

## Test Server .htaccess (IP Whitelist)
Not in git — protected via `skip-worktree`. If ever lost:
```apache
Require ip 68.249.41.9 35.151.50.130 99.8.107.54 97.84.70.141
<IfModule mod_headers.c>
  Header set X-Robots-Tag "noindex, nofollow, noarchive"
</IfModule>
```

## Git Workflow
- Always `git pull` before `git push` — Andrew may have pushed changes
- `main` is the primary branch
- `feature/passkey-webauthn` branch contains passkey work — merge to main before deploying
- `settings.local.php` is NOT in git — never commit it
- `*.sql.gz` files are gitignored — never commit DB dumps
