* fix(sync): prevent magic link prefetch failures and improve auth resilience
- Make /magic-login prefetch-immune: GET now renders a confirmation page
instead of consuming the single-use token. A "Log In" button triggers
POST /api/login/magic-link/verify which does the actual verification.
This prevents email client link prefetchers (Outlook SafeLinks, Gmail)
from consuming magic link tokens before users click them.
- Increase rate limits on auth endpoints for shared-IP scenarios (e.g.
university NAT). Global: 100→500, register: 10→50, login: 5→30,
verify: 10→50, magic-login page: 10→50.
- Add trustProxy to Fastify so req.ip uses X-Forwarded-For behind
reverse proxies instead of collapsing all clients to one rate bucket.
- Clear stale SuperSync auth tokens after 3 consecutive AuthFailSPErrors
instead of never clearing them. Prevents persistent failure loops where
an invalid token keeps retrying indefinitely until app reinstall.
- Improve registration-to-login UX messaging to emphasize email
verification is required before login links will work.
https://claude.ai/code/session_01WkbLzvhguQwRDtk7ht38aY
* chore: update package-lock.json from npm install
https://claude.ai/code/session_01WkbLzvhguQwRDtk7ht38aY
* fix(sync): address review feedback for auth resilience
- Remove conflicting style.css link from magic-login page
- Change trustProxy from true to 1 (trust single hop only)
- Increase /login/magic-link rate limit from 30 to 50 per 15min
- Delete dead magic-login-redirect.js (replaced by magic-login-confirm.js)
- Reset auth failure counter on non-auth errors (truly consecutive)
- Add test for counter reset on non-auth error between auth errors
https://claude.ai/code/session_01WkbLzvhguQwRDtk7ht38aY
* fix(sync): increase passkey rate limits and fix CSP inline script violations
- Increase passkey endpoint rate limits to match magic-link equivalents (50/15min)
- Extract inline scripts from /reset-password and /recover-passkey pages
into external JS files to comply with CSP scriptSrc: ["'self'"]
- Remove dead safeJsonForScript helper (no longer needed)
- Increase /recover-passkey page rate limit to 50/15min for consistency
https://claude.ai/code/session_01WkbLzvhguQwRDtk7ht38aY
* fix(sync): update security tests for escapeHtml and fix recover/passkey rate limit
- Update server-security.spec.ts: tests now check for escapeHtml output
(<, ") instead of safeJsonForScript output (\u003c), matching
the new data-token attribute approach
- Increase /recover/passkey rate limit from 30 to 50 per 15min to match
/login/magic-link (both send email with a secret link)
https://claude.ai/code/session_01WkbLzvhguQwRDtk7ht38aY
* fix(sync): reset auth failure counter after successful download
Move the counter reset from the final InSync return to right after
downloadRemoteOps() succeeds. This ensures early returns (cancelled,
LWW pending, payload rejected) also break the consecutive-failure
chain, since a successful download confirms auth is working.
https://claude.ai/code/session_01WkbLzvhguQwRDtk7ht38aY
---------
Co-authored-by: Claude <noreply@anthropic.com>
Add email-only registration flow (no passkey required) with verification
email. For existing unverified users, email is sent before DB token
update to preserve the old verification link on failure.
- POST /api/register/magic-link endpoint with Zod validation and rate limiting
- registerWithMagicLink function in auth.ts with P2002 race condition handling
- Frontend "Register with Email" button and JS handler
- Export shared VERIFICATION_TOKEN_EXPIRY_MS constant (eliminates duplication)
- 8 unit tests covering all paths including email failure cleanup
* fix(build): fix tag resolution and add error handling in bump-android-version
The previous two-tag approach (`currentTag` → `prevTag`) would resolve the
wrong changelog range during `npm version` since the new tag doesn't exist
yet. Simplified to a single `lastTag...HEAD` range which correctly captures
all commits since the last release.
Added try-catch with fallback to last 20 commits when no tags exist, and
a fallback message for empty changelogs.
* fix(sync): differentiate auth error messages and clarify token revocation
- Rename server dashboard "Refresh Token" to "Revoke & Replace Token"
with explicit warning that ALL devices will be disconnected
- Return rejection reason in 401 responses (revoked, expired, etc.)
via discriminated union TokenVerificationResult type
- Show different client error messages for server-side token rejection
vs missing local credentials to aid user diagnosis
- Extract server error reason from JSON response body in AuthFailSPError
Closes#6597
* refactor(sync): use generic auth reason and improve test readability
- Replace 'User not found' and 'Account not verified' with generic
'Account unavailable' to avoid leaking account state in API responses
- Extract long reason strings to constants in middleware.spec.ts
Add a "BETA" badge inline with the SuperSync heading to indicate
the service is in beta testing. Badge includes:
- Orange/amber styling for visibility in light mode
- Lighter orange with dark background for dark mode
- Compact, pill-shaped design consistent with existing badges
Add a trust badge displaying EU flag stars and "Data hosted in EU" text to the SuperSync server login/register page. The badge uses official EU flag colors (blue #039 and gold #fc0) with the circle of 12 stars from public domain SVG.
- Remove placeholder address text from privacy policies (DE/EN)
- Expand HTML privacy policy with full GDPR disclosures:
- Legal bases (Art. 6), data subject rights (Art. 15-22)
- Supervisory authority, retention periods, DPA info
- Cookies/tracking and automated decision-making sections
- Align HTML terms with German ToS:
- Add proper termination notice periods (2 weeks/good cause)
- Add 6-week notice for ToS amendments
- Add consumer withdrawal rights (14 days)
- Add ODR platform link and jurisdiction info
Add automatic dark mode support using prefers-color-scheme media query:
- Dark backgrounds: #1a1a1a (page), #2d2d2d (cards)
- Light text: #e5e5e5 (primary), #a3a3a3 (secondary)
- Adjusted primary color for dark: #4db8e8 (7.74:1 contrast)
- Dark variants for warning/success/error message boxes
- Button backgrounds kept darker for white text contrast
Also add consistent link styling for Terms/Privacy links:
- Normal, visited, and hover states use primary color
- Works in both light and dark modes
All colors verified to meet WCAG AA 4.5:1 minimum contrast.
Update CSS color variables to meet WCAG AA 4.5:1 contrast ratio:
- --primary: #0c96e2 → #0077b6 (4.87:1 on white)
- --text-light: #666666 → #595959 (7.00:1 on white)
- --success: #4caf50 → #2e7d32 (5.13:1 white on bg)
- --error: #f44336 → #c62828 (5.62:1 white on bg)
- .warning-box: #856404 → #6d5200 (6.63:1 on #fff3cd)
All text elements now meet WCAG AA accessibility requirements.
Add a dedicated Account Settings page to the SuperSync server web interface:
- Move Reset Account and Delete Account buttons from token display to
separate Account Settings page for better UX
- Reset Account clears all synced data but keeps account active
- Delete Account permanently removes account and all data
- Add E2E tests for account reset functionality
Closes#5848
- Email is now shared between login, register, and lost passkey forms
- Email persists to localStorage across page reloads
- Typing in any email field updates all others in real-time
The server was updated to @simplewebauthn/server v13.2.2 but the
browser library was still at pre-v13. The v13 API expects options
wrapped as { optionsJSON: options }, causing allowCredentials to be
lost during login with the old library.
This caused "no passkeys found" errors when users tried to login
after registering.
The deleteAccount fetch request sent Content-Type: application/json
but no body, causing Fastify's JSON parser to fail with 400 Bad Request.
Add empty JSON body to match the pattern used by refreshToken.
Authentication changes:
- Add passkey (WebAuthn) as primary login method
- Add email magic link as fallback for devices without passkey support
- Remove password-based authentication entirely
New features:
- Passkey registration and login via @simplewebauthn/server
- Magic link login with 15-minute expiry tokens
- Passkey recovery via email link
- Self-hosted simplewebauthn-browser.min.js for reliability
Database changes:
- Add Passkey model for WebAuthn credentials
- Add PasskeyChallenge model for registration/auth challenges
- Add loginToken and loginTokenExpiresAt fields for magic links
- Add passkeyRecoveryToken fields for passkey recovery
UI changes:
- Login form: email + "Login with Passkey" + "Send Login Link"
- Register form: email + terms checkbox + "Register with Passkey"
- Consistent token display UI for both passkey and magic link login
- Remove password fields and forgot password flow
Security:
- CSP-compliant magic link redirect using external script
- Rate limiting on all auth endpoints
- Single-use magic link tokens
Add ability for users to delete their SuperSync account from the
web frontend. This addresses GitHub issue #5848.
Server changes:
- Add DELETE /api/account endpoint in api.ts
- Requires JWT authentication
- Uses Prisma cascade delete for user and related data
- Rate limited to 3 requests per 15 minutes
- Logs USER_ACCOUNT_DELETED audit event
Web frontend changes:
- Add "Delete Account" button with danger styling
- Confirmation dialog warns about permanent deletion
- Clears state and returns to login on success
Add "Forgot Password?" link and form to the SuperSync server login page.
The backend endpoints already existed but had no UI entry point.
- Add forgot password button under login form
- Add email input form with "Send Reset Link" button
- Show generic success message (prevents email enumeration)
- Auto-return to login form after submission
Replace inline onclick handlers with JavaScript event listeners to
comply with Content Security Policy (CSP). The helmet middleware's
scriptSrc directive blocks inline event handlers, preventing the
register tab and other buttons from working.
For German legal compliance (Impressum), the privacy policy needs
to display the operator's address. This change:
- Rename privacy.html to privacy.template.html with placeholders
- Add PRIVACY_* env vars to .env.example
- Generate privacy.html at server startup from template
- Add PrivacyConfig to ServerConfig
If env vars are not set, placeholder text is shown.
- Update start script to use docker compose
- Add logs script for viewing container logs
- Expand privacy policy and terms of service content
- Format app.js and registration-api.spec.ts
Client-side (Super Productivity app):
- Add contextual help text explaining what SuperSync does
- Add clear instructions before "Get Token" button
- Rename button to "Open Server & Get Token" for clarity
- Update field labels and descriptions for better guidance
- Add translation keys for all new text
Server-side (SuperSync server):
- Add collapsible step-by-step instructions on token page
- Make "Copy Token" button the primary action
- Make "Refresh Token" a subtle text link instead of prominent button
- Add confirmation dialog before refreshing token with warning
Add button to token display page that calls /api/replace-token endpoint,
allowing users to invalidate their current token and get a new one
if the token was accidentally shared or compromised.