Commit graph

27 commits

Author SHA1 Message Date
Johannes Millan
70414145a3
fix(sync): prevent magic link prefetch failures and improve auth resilience (#7175)
* 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>
2026-04-10 14:46:57 +02:00
Johannes Millan
3ba5841421 feat(sync): add magic link registration to SuperSync server
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
2026-03-12 13:36:05 +01:00
Johannes Millan
7070fde51e
Pr 6741 (#6743)
* 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
2026-03-05 21:17:59 +01:00
Johannes Millan
67527373c1 feat(supersync): add beta badge to login/register screens
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
2026-01-23 15:23:16 +01:00
Johannes Millan
b05b0400ed feat: add EU data hosting badge with SVG image to login page 2026-01-22 16:40:34 +01:00
Johannes Millan
5be9aea701 feat(supersync): add EU data hosting badge to login page
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.
2026-01-22 15:36:57 +01:00
Johannes Millan
2f7a00371a fix(supersync): improve GDPR compliance in legal documents
- 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
2026-01-15 12:48:18 +01:00
Johannes Millan
db41055feb feat(supersync): add dark mode with WCAG AA compliant colors
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.
2026-01-06 13:49:50 +01:00
Johannes Millan
e80850fb1e fix(supersync): improve color contrast for WCAG 2.2 compliance #5903
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.
2026-01-06 13:08:46 +01:00
Johannes Millan
f9635423d6 feat(server): add Account Settings page with reset and delete options
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
2026-01-05 12:54:53 +01:00
Johannes Millan
9ddced5648 feat(sync-server): sync email input across all auth forms
- 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
2026-01-03 15:50:15 +01:00
Johannes Millan
253e55cd2c fix(sync-server): use properly minified SimpleWebAuthn browser v13.2.2
The previous version was reformatted by prettier into 362 lines.
This version is the original minified bundle with version header.
2026-01-03 12:32:50 +01:00
Johannes Millan
1fad43d198 fix(sync-server): update SimpleWebAuthn browser library to v13.2.2
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.
2026-01-03 12:08:04 +01:00
Johannes Millan
4843d2afb7 fix(sync-server): add body to DELETE account request
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.
2026-01-02 21:02:34 +01:00
Johannes Millan
9c0a728ef4 feat(sync-server): replace password auth with passkey + magic link
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
2026-01-02 16:52:48 +01:00
Johannes Millan
b367c9595b feat(sync): add account deletion to SuperSync web frontend
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
2026-01-02 13:25:40 +01:00
Johannes Millan
1b2f5ff22c feat(supersync): add forgot password UI to login page
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
2026-01-01 12:30:52 +01:00
Johannes Millan
1775060b9d fix(supersync): use event listeners instead of inline onclick handlers
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.
2025-12-31 14:35:33 +01:00
Johannes Millan
4d5c79f129 feat(supersync): add privacy policy address via env vars
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.
2025-12-28 15:38:40 +01:00
Johannes Millan
d46dab3d2e chore(sync-server): update npm scripts and expand legal docs
- 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
2025-12-15 13:47:42 +01:00
Johannes Millan
c1adbc522c feat: Implement Terms of Service and Privacy Policy acceptance for Super Sync registration 2025-12-12 20:48:13 +01:00
Johannes Millan
67d668dd7d feat: Add prominent warning for Super Sync future costs 2025-12-12 20:48:13 +01:00
Johannes Millan
2531b2730a feat(sync): improve SuperSync first-time setup experience
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
2025-12-12 20:47:47 +01:00
Johannes Millan
7be1fc19d9 fix(sync-server): send empty body for replace-token request
Fastify rejects requests with Content-Type: application/json but no body.
Add empty JSON object as body to fix "Bad Request" error.
2025-12-12 20:47:47 +01:00
Johannes Millan
87d300b847 feat(sync-server): add refresh token button to web UI
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.
2025-12-12 20:47:47 +01:00
Johannes Millan
ef95a601aa feat(superSync): make web token stuff work 2025-12-12 20:46:10 +01:00
Johannes Millan
6308e33a56 feat(syncServer): add SMTP email verification for user registration 2025-12-12 20:46:04 +01:00