Skip to content

Address code review feedback: authorization, error handling, and cleanup optimization#20

Merged
carnach merged 5 commits into
developmentfrom
copilot/sub-pr-19
Feb 4, 2026
Merged

Address code review feedback: authorization, error handling, and cleanup optimization#20
carnach merged 5 commits into
developmentfrom
copilot/sub-pr-19

Conversation

Copy link
Copy Markdown

Copilot AI commented Feb 4, 2026

Implements fixes for security, reliability, and performance issues identified in code review feedback.

Authorization & Security

  • Add authorization checks to update.procedure.ts to prevent unauthorized group modifications
  • Fix hardcoded URL in tRPC procedures—now derives host/protocol from request headers
  • Validate user existence before passkey auth session creation (return 404 vs creating incomplete records)

Error Handling

  • Wrap all fetch calls in try-catch with proper loading state cleanup
  • Add error logging for debugging network/application failures
  • Improve user-facing error messages

Code Quality

  • Extract session retrieval into reusable getSessionFromHeaders() helper
  • Remove duplicate credentialId index (unique constraint already provides indexing)
  • Remove unnecessary open dependency from useMemo
  • Add SSR guard for localStorage access

Cleanup Optimization

  • Fix race condition in passkey deletion using transaction:
await prisma.$transaction(async (tx) => {
  await tx.passkey.delete({ where: { id: passkeyId } })
  const remaining = await tx.passkey.count({ where: { anonymousUserId } })
  if (remaining === 0) {
    await tx.anonymousUser.update({ 
      where: { id: anonymousUserId },
      data: { passkeysEnabled: false }
    })
  }
})
  • Add batch cleanup for temporary users (replaces individual deletes with deleteMany)

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI mentioned this pull request Feb 4, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
my-spliit-instance Ready Ready Preview, Comment Feb 4, 2026 10:43pm

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>
Copy link
Copy Markdown
Author

Copilot AI commented Feb 4, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • checkpoint.prisma.io
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/spliit/spliit/node_modules/prisma/build/child {&#34;product&#34;:&#34;prisma&#34;,&#34;version&#34;:&#34;6.18.0&#34;,&#34;cli_install_type&#34;:&#34;local&#34;,&#34;information&#34;:&#34;&#34;,&#34;local_timestamp&#34;:&#34;2026-02-04T22:34:48Z&#34;,&#34;project_hash&#34;:&#34;fe730ba0&#34;,&#34;cli_path&#34;:&#34;/home/REDACTED/work/spliit/spliit/node_modules/.bin/prisma&#34;,&#34;cli_path_hash&#34;:&#34;86e8da49&#34;,&#34;endpoin (dns block)
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/spliit/spliit/node_modules/prisma/build/child {&#34;product&#34;:&#34;prisma&#34;,&#34;version&#34;:&#34;6.18.0&#34;,&#34;cli_install_type&#34;:&#34;local&#34;,&#34;information&#34;:&#34;&#34;,&#34;local_timestamp&#34;:&#34;2026-02-04T22:34:50Z&#34;,&#34;project_hash&#34;:&#34;fe730ba0&#34;,&#34;cli_path&#34;:&#34;/home/REDACTED/work/spliit/spliit/node_modules/.bin/prisma&#34;,&#34;cli_path_hash&#34;:&#34;86e8da49&#34;,&#34;endpoin (dns block)
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/spliit/spliit/node_modules/prisma/build/child {&#34;product&#34;:&#34;prisma&#34;,&#34;version&#34;:&#34;6.18.0&#34;,&#34;cli_install_type&#34;:&#34;local&#34;,&#34;information&#34;:&#34;&#34;,&#34;local_timestamp&#34;:&#34;2026-02-04T22:35:51Z&#34;,&#34;project_hash&#34;:&#34;fe730ba0&#34;,&#34;cli_path&#34;:&#34;/home/REDACTED/work/spliit/spliit/node_modules/.bin/prisma&#34;,&#34;cli_path_hash&#34;:&#34;86e8da49&#34;,&#34;endpoin (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title [WIP] Refactor anonymous user authentication system Address code review feedback: authorization, error handling, and cleanup optimization Feb 4, 2026
Copilot AI requested a review from carnach February 4, 2026 22:43
@carnach carnach marked this pull request as ready for review February 4, 2026 22:44
Copilot AI review requested due to automatic review settings February 4, 2026 22:44
@carnach carnach merged commit b593d62 into development Feb 4, 2026
2 checks passed
carnach added a commit that referenced this pull request Feb 4, 2026
* add anonymous account

* feat: implement database-backed sessions and improve group management

Add database-backed session storage to fix session expiration on server restart

- Create AnonymousSession table with token, userId, expiresAt, and challenge fields

- Replace in-memory Map with Prisma queries in SessionStore

- Sessions now persist across server restarts and deployments

Fix group recovery and sign-out behavior

- Fix naming conflict between setRecentGroups state setter and helper function

- Groups now properly save to localStorage when logging in

- Sign-out only clears localStorage, preserving groups in database for recovery

- Groups restore automatically when logging back in with same account

Improve UI for logged-in users

- Move Restore from backup and Import from JSON to account dropdown menu

- Hide restore/import options from groups page for logged-in users

- Better organization of account management features

Add server-side authorization to group deletion

- Validate user session before allowing group deletion

- Verify group ownership through anonymousUserGroup table

- Return appropriate error codes (401 UNAUTHORIZED, 403 FORBIDDEN)

- Prevents unauthorized deletion even if UI is bypassed

* update sigin UI

* feat: support multiple passkeys per account with names

Add ability to register multiple passkeys per anonymous account with custom names

- Create new Passkey table with name, credentialId, publicKey, counter, lastUsedAt fields

- Migrate from single passkey storage on AnonymousUser to separate Passkey table

- Add passkey name input dialog when registering new passkeys

- Display list of all registered passkeys with creation dates

- Add individual passkey deletion with confirmation dialog

- Update lastUsedAt timestamp when passkey is used for authentication

- Add /api/anonymous-users/passkey/list endpoint to fetch user's passkeys

- Maintain backward compatibility with deprecated passkey fields on AnonymousUser

UI improvements:

- Show passkey list with name and creation date in account dialog

- Add 'Add passkey' button that opens name input dialog

- Individual remove button for each passkey

- Better UX with 'New account' button highlighted when generating username

- Changed 'Recover account' to 'Existing account' for clarity

* fix: update auth-options to use new Passkey table

- Change auth-options endpoint to fetch credentials from Passkey table
- Support multiple passkeys by returning all credential IDs for allowCredentials
- Fixes 'No passkey registered' error when authenticating

* fix: ensure user exists before creating passkey auth session

- Add upsert to create AnonymousUser if not exists before session creation
- Prevents foreign key constraint violation in AnonymousSession table
- Handle both authenticated and discoverable credential flows

* feat: enforce passphrase security requirements and separate change/reset flows

- Always require current passphrase for normal passphrase change
- Add separate reset flow for passkey-authenticated users
- Reset with passkey only requires new passphrase (no current)
- Add hasPassphrase flag to groups API response
- Update UI to conditionally show options based on passphrase state
- Add passkeyResetMode state to differentiate between change and reset
- Server-side validation enforces security requirements

* Address code review feedback: authorization, error handling, and cleanup optimization (#20)

* Initial plan

* Address code review comments: fix security and reliability issues

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Refactor: extract session retrieval helper and add temp user cleanup

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Improve error messages and add error logging for debugging

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Polish error message for clarity

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>
Copy link
Copy Markdown

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 PR implements security hardening, error handling improvements, and performance optimizations based on code review feedback. The changes focus on authorization enforcement, proper error handling with loading state cleanup, and optimizations for database operations.

Changes:

  • Added authorization checks to group update and delete procedures to verify user ownership before allowing modifications
  • Extracted session retrieval logic into reusable getSessionFromHeaders() helper with proper URL derivation from request headers
  • Wrapped all fetch calls in try-catch-finally blocks with consistent error logging and user-friendly error messages
  • Fixed race conditions in passkey deletion using database transactions and optimized temporary user cleanup with batch operations

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/trpc/routers/groups/update.procedure.ts Adds session-based authorization to verify user has associated with group before allowing updates
src/trpc/routers/groups/delete.procedure.ts Refactors to use new getSessionFromHeaders() helper for cleaner session retrieval
src/lib/session.ts Adds getSessionFromHeaders() helper, fixes hardcoded localhost URL, implements batch cleanup for temporary users
src/components/anonymous-auth-menu.tsx Wraps fetch calls in try-catch-finally for proper error handling and loading state cleanup, removes unnecessary open dependency from useMemo
src/app/groups/recent-group-list.tsx Adds SSR guard for localStorage access to prevent hydration errors
src/app/api/anonymous-users/passkey/delete/route.ts Wraps passkey deletion and passkeysEnabled update in transaction to prevent race conditions
src/app/api/anonymous-users/passkey/auth-options/route.ts Validates user existence before creating auth session instead of creating incomplete user records
prisma/schema.prisma Removes redundant credentialId index (unique constraint already provides indexing)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +18 to +43
// Get session from request headers
const session = await getSessionFromHeaders()

if (!session) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: 'You must be logged in to update a group',
})
}

// Check if user has this group in their associated groups
const userGroup = await prisma.anonymousUserGroup.findUnique({
where: {
anonymousUserId_groupId: {
anonymousUserId: session.userId,
groupId: groupId,
},
},
})

if (!userGroup) {
throw new TRPCError({
code: 'FORBIDDEN',
message: 'You are not authorized to update this group',
})
}
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

Authorization checks have been added to update and delete group procedures, but similar mutating operations like creating groups and creating/updating/deleting expenses lack authorization checks. This creates an inconsistency in security enforcement across the API surface.

Consider adding similar authorization checks to:

  • createGroupProcedure in src/trpc/routers/groups/create.procedure.ts
  • createGroupExpenseProcedure in src/trpc/routers/groups/expenses/create.procedure.ts
  • updateGroupExpenseProcedure in src/trpc/routers/groups/expenses/update.procedure.ts
  • deleteGroupExpenseProcedure in src/trpc/routers/groups/expenses/delete.procedure.ts

These procedures should verify that the user has access to the group before allowing mutations.

Copilot uses AI. Check for mistakes.
carnach added a commit that referenced this pull request Feb 6, 2026
* add anonymous account

* feat: implement database-backed sessions and improve group management

Add database-backed session storage to fix session expiration on server restart

- Create AnonymousSession table with token, userId, expiresAt, and challenge fields

- Replace in-memory Map with Prisma queries in SessionStore

- Sessions now persist across server restarts and deployments

Fix group recovery and sign-out behavior

- Fix naming conflict between setRecentGroups state setter and helper function

- Groups now properly save to localStorage when logging in

- Sign-out only clears localStorage, preserving groups in database for recovery

- Groups restore automatically when logging back in with same account

Improve UI for logged-in users

- Move Restore from backup and Import from JSON to account dropdown menu

- Hide restore/import options from groups page for logged-in users

- Better organization of account management features

Add server-side authorization to group deletion

- Validate user session before allowing group deletion

- Verify group ownership through anonymousUserGroup table

- Return appropriate error codes (401 UNAUTHORIZED, 403 FORBIDDEN)

- Prevents unauthorized deletion even if UI is bypassed

* update sigin UI

* feat: support multiple passkeys per account with names

Add ability to register multiple passkeys per anonymous account with custom names

- Create new Passkey table with name, credentialId, publicKey, counter, lastUsedAt fields

- Migrate from single passkey storage on AnonymousUser to separate Passkey table

- Add passkey name input dialog when registering new passkeys

- Display list of all registered passkeys with creation dates

- Add individual passkey deletion with confirmation dialog

- Update lastUsedAt timestamp when passkey is used for authentication

- Add /api/anonymous-users/passkey/list endpoint to fetch user's passkeys

- Maintain backward compatibility with deprecated passkey fields on AnonymousUser

UI improvements:

- Show passkey list with name and creation date in account dialog

- Add 'Add passkey' button that opens name input dialog

- Individual remove button for each passkey

- Better UX with 'New account' button highlighted when generating username

- Changed 'Recover account' to 'Existing account' for clarity

* fix: update auth-options to use new Passkey table

- Change auth-options endpoint to fetch credentials from Passkey table
- Support multiple passkeys by returning all credential IDs for allowCredentials
- Fixes 'No passkey registered' error when authenticating

* fix: ensure user exists before creating passkey auth session

- Add upsert to create AnonymousUser if not exists before session creation
- Prevents foreign key constraint violation in AnonymousSession table
- Handle both authenticated and discoverable credential flows

* feat: enforce passphrase security requirements and separate change/reset flows

- Always require current passphrase for normal passphrase change
- Add separate reset flow for passkey-authenticated users
- Reset with passkey only requires new passphrase (no current)
- Add hasPassphrase flag to groups API response
- Update UI to conditionally show options based on passphrase state
- Add passkeyResetMode state to differentiate between change and reset
- Server-side validation enforces security requirements

* Address code review feedback: authorization, error handling, and cleanup optimization (#20)

* Initial plan

* Address code review comments: fix security and reliability issues

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Refactor: extract session retrieval helper and add temp user cleanup

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Improve error messages and add error logging for debugging

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Polish error message for clarity

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* test passkey fix

* try no username first

* test IOS

* update ux

* update UI remove import button

* finalise permissions

* additional permissoins

* Add remote JSON import flow with auto-association and iOS icon fixes

* Address security vulnerabilities and translations in remote JSON import (#22)

* Initial plan

* Add security improvements to remote JSON import route

- Add rate limiting to prevent SSRF abuse
- Add timeout (30s) to fetch operations
- Add groupId validation to prevent injection/path traversal
- Add SSRF protection (block private IPs, localhost, metadata endpoints)
- Add response size limit (10MB) and max expenses limit (10k)
- Add error handling to normalizeDate function
- Improve error logging in import-json-button.tsx
- Extract ASSOCIATED_GROUPS_KEY constant to shared file

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Translate import success messages to all supported languages

- pt-BR: Portuguese (Brazil)
- ru-RU: Russian
- ko: Korean
- id: Indonesian
- fr-FR: French
- zh-TW: Chinese Traditional
- ro: Romanian
- ja-JP: Japanese
- eu: Basque
- zh-CN: Chinese Simplified
- nl-NL: Dutch
- he: Hebrew
- cs-CZ: Czech
- uk-UA: Ukrainian
- tr-TR: Turkish
- pt: Portuguese
- pl-PL: Polish
- es: Spanish
- de-DE: German
- ca: Catalan
- fi: Finnish
- it-IT: Italian

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Address code review feedback on security improvements

- Fix IP validation to properly handle octets and validate ranges
- Remove redundant cloud metadata endpoint check (covered by link-local)
- Fix normalizeDate to throw error instead of using epoch as default
- Filter out expenses with invalid dates instead of setting to epoch
- Sanitize logging to avoid exposing sensitive data

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Fix log injection vulnerability in normalizeDate

Use template literal instead of string concatenation to prevent
potential log injection when logging the type of invalid date values.

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* fix json

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>
carnach added a commit that referenced this pull request Feb 7, 2026
* add anonymous account

* feat: implement database-backed sessions and improve group management

Add database-backed session storage to fix session expiration on server restart

- Create AnonymousSession table with token, userId, expiresAt, and challenge fields

- Replace in-memory Map with Prisma queries in SessionStore

- Sessions now persist across server restarts and deployments

Fix group recovery and sign-out behavior

- Fix naming conflict between setRecentGroups state setter and helper function

- Groups now properly save to localStorage when logging in

- Sign-out only clears localStorage, preserving groups in database for recovery

- Groups restore automatically when logging back in with same account

Improve UI for logged-in users

- Move Restore from backup and Import from JSON to account dropdown menu

- Hide restore/import options from groups page for logged-in users

- Better organization of account management features

Add server-side authorization to group deletion

- Validate user session before allowing group deletion

- Verify group ownership through anonymousUserGroup table

- Return appropriate error codes (401 UNAUTHORIZED, 403 FORBIDDEN)

- Prevents unauthorized deletion even if UI is bypassed

* update sigin UI

* feat: support multiple passkeys per account with names

Add ability to register multiple passkeys per anonymous account with custom names

- Create new Passkey table with name, credentialId, publicKey, counter, lastUsedAt fields

- Migrate from single passkey storage on AnonymousUser to separate Passkey table

- Add passkey name input dialog when registering new passkeys

- Display list of all registered passkeys with creation dates

- Add individual passkey deletion with confirmation dialog

- Update lastUsedAt timestamp when passkey is used for authentication

- Add /api/anonymous-users/passkey/list endpoint to fetch user's passkeys

- Maintain backward compatibility with deprecated passkey fields on AnonymousUser

UI improvements:

- Show passkey list with name and creation date in account dialog

- Add 'Add passkey' button that opens name input dialog

- Individual remove button for each passkey

- Better UX with 'New account' button highlighted when generating username

- Changed 'Recover account' to 'Existing account' for clarity

* fix: update auth-options to use new Passkey table

- Change auth-options endpoint to fetch credentials from Passkey table
- Support multiple passkeys by returning all credential IDs for allowCredentials
- Fixes 'No passkey registered' error when authenticating

* fix: ensure user exists before creating passkey auth session

- Add upsert to create AnonymousUser if not exists before session creation
- Prevents foreign key constraint violation in AnonymousSession table
- Handle both authenticated and discoverable credential flows

* feat: enforce passphrase security requirements and separate change/reset flows

- Always require current passphrase for normal passphrase change
- Add separate reset flow for passkey-authenticated users
- Reset with passkey only requires new passphrase (no current)
- Add hasPassphrase flag to groups API response
- Update UI to conditionally show options based on passphrase state
- Add passkeyResetMode state to differentiate between change and reset
- Server-side validation enforces security requirements

* Address code review feedback: authorization, error handling, and cleanup optimization (#20)

* Initial plan

* Address code review comments: fix security and reliability issues

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Refactor: extract session retrieval helper and add temp user cleanup

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Improve error messages and add error logging for debugging

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Polish error message for clarity

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* test passkey fix

* try no username first

* test IOS

* update ux

* update UI remove import button

* finalise permissions

* additional permissoins

* Add remote JSON import flow with auto-association and iOS icon fixes

* Address security vulnerabilities and translations in remote JSON import (#22)

* Initial plan

* Add security improvements to remote JSON import route

- Add rate limiting to prevent SSRF abuse
- Add timeout (30s) to fetch operations
- Add groupId validation to prevent injection/path traversal
- Add SSRF protection (block private IPs, localhost, metadata endpoints)
- Add response size limit (10MB) and max expenses limit (10k)
- Add error handling to normalizeDate function
- Improve error logging in import-json-button.tsx
- Extract ASSOCIATED_GROUPS_KEY constant to shared file

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Translate import success messages to all supported languages

- pt-BR: Portuguese (Brazil)
- ru-RU: Russian
- ko: Korean
- id: Indonesian
- fr-FR: French
- zh-TW: Chinese Traditional
- ro: Romanian
- ja-JP: Japanese
- eu: Basque
- zh-CN: Chinese Simplified
- nl-NL: Dutch
- he: Hebrew
- cs-CZ: Czech
- uk-UA: Ukrainian
- tr-TR: Turkish
- pt: Portuguese
- pl-PL: Polish
- es: Spanish
- de-DE: German
- ca: Catalan
- fi: Finnish
- it-IT: Italian

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Address code review feedback on security improvements

- Fix IP validation to properly handle octets and validate ranges
- Remove redundant cloud metadata endpoint check (covered by link-local)
- Fix normalizeDate to throw error instead of using epoch as default
- Filter out expenses with invalid dates instead of setting to epoch
- Sanitize logging to avoid exposing sensitive data

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Fix log injection vulnerability in normalizeDate

Use template literal instead of string concatenation to prevent
potential log injection when logging the type of invalid date values.

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* fix json

* empty username when accessing the login screen

* Address security vulnerabilities and localization gaps in remote JSON import (#24)

* Initial plan

* Address code review feedback: SSRF protection, translations, and error handling

- Add protocol validation (http/https only) to validateRemoteUrl
- Enforce hard byte limit while reading response in fetchTrpcJson
- Prevent redirect-based SSRF by setting redirect: 'manual'
- Add all missing translation keys for hardcoded UI strings
- Change placeholder URL from spliit.app to neutral example.com
- Wrap syncAssociatedGroups in try/catch for best-effort handling
- Improve passkey error handling to differentiate error types (rate limit, not found, user cancel)
- Run prettier to fix inconsistent indentation

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Fix duplicate imports causing build failure in import-json-button.tsx (#25)

* Initial plan

* Fix duplicate imports in import-json-button.tsx

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>
carnach added a commit that referenced this pull request Feb 7, 2026
* add anonymous account

* feat: implement database-backed sessions and improve group management

Add database-backed session storage to fix session expiration on server restart

- Create AnonymousSession table with token, userId, expiresAt, and challenge fields

- Replace in-memory Map with Prisma queries in SessionStore

- Sessions now persist across server restarts and deployments

Fix group recovery and sign-out behavior

- Fix naming conflict between setRecentGroups state setter and helper function

- Groups now properly save to localStorage when logging in

- Sign-out only clears localStorage, preserving groups in database for recovery

- Groups restore automatically when logging back in with same account

Improve UI for logged-in users

- Move Restore from backup and Import from JSON to account dropdown menu

- Hide restore/import options from groups page for logged-in users

- Better organization of account management features

Add server-side authorization to group deletion

- Validate user session before allowing group deletion

- Verify group ownership through anonymousUserGroup table

- Return appropriate error codes (401 UNAUTHORIZED, 403 FORBIDDEN)

- Prevents unauthorized deletion even if UI is bypassed

* update sigin UI

* feat: support multiple passkeys per account with names

Add ability to register multiple passkeys per anonymous account with custom names

- Create new Passkey table with name, credentialId, publicKey, counter, lastUsedAt fields

- Migrate from single passkey storage on AnonymousUser to separate Passkey table

- Add passkey name input dialog when registering new passkeys

- Display list of all registered passkeys with creation dates

- Add individual passkey deletion with confirmation dialog

- Update lastUsedAt timestamp when passkey is used for authentication

- Add /api/anonymous-users/passkey/list endpoint to fetch user's passkeys

- Maintain backward compatibility with deprecated passkey fields on AnonymousUser

UI improvements:

- Show passkey list with name and creation date in account dialog

- Add 'Add passkey' button that opens name input dialog

- Individual remove button for each passkey

- Better UX with 'New account' button highlighted when generating username

- Changed 'Recover account' to 'Existing account' for clarity

* fix: update auth-options to use new Passkey table

- Change auth-options endpoint to fetch credentials from Passkey table
- Support multiple passkeys by returning all credential IDs for allowCredentials
- Fixes 'No passkey registered' error when authenticating

* fix: ensure user exists before creating passkey auth session

- Add upsert to create AnonymousUser if not exists before session creation
- Prevents foreign key constraint violation in AnonymousSession table
- Handle both authenticated and discoverable credential flows

* feat: enforce passphrase security requirements and separate change/reset flows

- Always require current passphrase for normal passphrase change
- Add separate reset flow for passkey-authenticated users
- Reset with passkey only requires new passphrase (no current)
- Add hasPassphrase flag to groups API response
- Update UI to conditionally show options based on passphrase state
- Add passkeyResetMode state to differentiate between change and reset
- Server-side validation enforces security requirements

* Address code review feedback: authorization, error handling, and cleanup optimization (#20)

* Initial plan

* Address code review comments: fix security and reliability issues

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Refactor: extract session retrieval helper and add temp user cleanup

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Improve error messages and add error logging for debugging

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Polish error message for clarity

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* test passkey fix

* try no username first

* test IOS

* update ux

* update UI remove import button

* finalise permissions

* additional permissoins

* Add remote JSON import flow with auto-association and iOS icon fixes

* Address security vulnerabilities and translations in remote JSON import (#22)

* Initial plan

* Add security improvements to remote JSON import route

- Add rate limiting to prevent SSRF abuse
- Add timeout (30s) to fetch operations
- Add groupId validation to prevent injection/path traversal
- Add SSRF protection (block private IPs, localhost, metadata endpoints)
- Add response size limit (10MB) and max expenses limit (10k)
- Add error handling to normalizeDate function
- Improve error logging in import-json-button.tsx
- Extract ASSOCIATED_GROUPS_KEY constant to shared file

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Translate import success messages to all supported languages

- pt-BR: Portuguese (Brazil)
- ru-RU: Russian
- ko: Korean
- id: Indonesian
- fr-FR: French
- zh-TW: Chinese Traditional
- ro: Romanian
- ja-JP: Japanese
- eu: Basque
- zh-CN: Chinese Simplified
- nl-NL: Dutch
- he: Hebrew
- cs-CZ: Czech
- uk-UA: Ukrainian
- tr-TR: Turkish
- pt: Portuguese
- pl-PL: Polish
- es: Spanish
- de-DE: German
- ca: Catalan
- fi: Finnish
- it-IT: Italian

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Address code review feedback on security improvements

- Fix IP validation to properly handle octets and validate ranges
- Remove redundant cloud metadata endpoint check (covered by link-local)
- Fix normalizeDate to throw error instead of using epoch as default
- Filter out expenses with invalid dates instead of setting to epoch
- Sanitize logging to avoid exposing sensitive data

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Fix log injection vulnerability in normalizeDate

Use template literal instead of string concatenation to prevent
potential log injection when logging the type of invalid date values.

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* fix json

* empty username when accessing the login screen

* Address security vulnerabilities and localization gaps in remote JSON import (#24)

* Initial plan

* Address code review feedback: SSRF protection, translations, and error handling

- Add protocol validation (http/https only) to validateRemoteUrl
- Enforce hard byte limit while reading response in fetchTrpcJson
- Prevent redirect-based SSRF by setting redirect: 'manual'
- Add all missing translation keys for hardcoded UI strings
- Change placeholder URL from spliit.app to neutral example.com
- Wrap syncAssociatedGroups in try/catch for best-effort handling
- Improve passkey error handling to differentiate error types (rate limit, not found, user cancel)
- Run prettier to fix inconsistent indentation

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Fix duplicate imports causing build failure in import-json-button.tsx (#25)

* Initial plan

* Fix duplicate imports in import-json-button.tsx

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* add whats new

* add whats new

* [WIP] Add end-user documentation and UX improvements (#27)

* Initial plan

* Address PR review comments: fix unused imports, update links, improve security and error handling

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Improve icon choice and clarify HTML stripping behavior

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>
carnach added a commit that referenced this pull request Feb 9, 2026
* add anonymous account

* feat: implement database-backed sessions and improve group management

Add database-backed session storage to fix session expiration on server restart

- Create AnonymousSession table with token, userId, expiresAt, and challenge fields

- Replace in-memory Map with Prisma queries in SessionStore

- Sessions now persist across server restarts and deployments

Fix group recovery and sign-out behavior

- Fix naming conflict between setRecentGroups state setter and helper function

- Groups now properly save to localStorage when logging in

- Sign-out only clears localStorage, preserving groups in database for recovery

- Groups restore automatically when logging back in with same account

Improve UI for logged-in users

- Move Restore from backup and Import from JSON to account dropdown menu

- Hide restore/import options from groups page for logged-in users

- Better organization of account management features

Add server-side authorization to group deletion

- Validate user session before allowing group deletion

- Verify group ownership through anonymousUserGroup table

- Return appropriate error codes (401 UNAUTHORIZED, 403 FORBIDDEN)

- Prevents unauthorized deletion even if UI is bypassed

* update sigin UI

* feat: support multiple passkeys per account with names

Add ability to register multiple passkeys per anonymous account with custom names

- Create new Passkey table with name, credentialId, publicKey, counter, lastUsedAt fields

- Migrate from single passkey storage on AnonymousUser to separate Passkey table

- Add passkey name input dialog when registering new passkeys

- Display list of all registered passkeys with creation dates

- Add individual passkey deletion with confirmation dialog

- Update lastUsedAt timestamp when passkey is used for authentication

- Add /api/anonymous-users/passkey/list endpoint to fetch user's passkeys

- Maintain backward compatibility with deprecated passkey fields on AnonymousUser

UI improvements:

- Show passkey list with name and creation date in account dialog

- Add 'Add passkey' button that opens name input dialog

- Individual remove button for each passkey

- Better UX with 'New account' button highlighted when generating username

- Changed 'Recover account' to 'Existing account' for clarity

* fix: update auth-options to use new Passkey table

- Change auth-options endpoint to fetch credentials from Passkey table
- Support multiple passkeys by returning all credential IDs for allowCredentials
- Fixes 'No passkey registered' error when authenticating

* fix: ensure user exists before creating passkey auth session

- Add upsert to create AnonymousUser if not exists before session creation
- Prevents foreign key constraint violation in AnonymousSession table
- Handle both authenticated and discoverable credential flows

* feat: enforce passphrase security requirements and separate change/reset flows

- Always require current passphrase for normal passphrase change
- Add separate reset flow for passkey-authenticated users
- Reset with passkey only requires new passphrase (no current)
- Add hasPassphrase flag to groups API response
- Update UI to conditionally show options based on passphrase state
- Add passkeyResetMode state to differentiate between change and reset
- Server-side validation enforces security requirements

* Address code review feedback: authorization, error handling, and cleanup optimization (#20)

* Initial plan

* Address code review comments: fix security and reliability issues

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Refactor: extract session retrieval helper and add temp user cleanup

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Improve error messages and add error logging for debugging

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Polish error message for clarity

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* test passkey fix

* try no username first

* test IOS

* update ux

* update UI remove import button

* finalise permissions

* additional permissoins

* Add remote JSON import flow with auto-association and iOS icon fixes

* Address security vulnerabilities and translations in remote JSON import (#22)

* Initial plan

* Add security improvements to remote JSON import route

- Add rate limiting to prevent SSRF abuse
- Add timeout (30s) to fetch operations
- Add groupId validation to prevent injection/path traversal
- Add SSRF protection (block private IPs, localhost, metadata endpoints)
- Add response size limit (10MB) and max expenses limit (10k)
- Add error handling to normalizeDate function
- Improve error logging in import-json-button.tsx
- Extract ASSOCIATED_GROUPS_KEY constant to shared file

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Translate import success messages to all supported languages

- pt-BR: Portuguese (Brazil)
- ru-RU: Russian
- ko: Korean
- id: Indonesian
- fr-FR: French
- zh-TW: Chinese Traditional
- ro: Romanian
- ja-JP: Japanese
- eu: Basque
- zh-CN: Chinese Simplified
- nl-NL: Dutch
- he: Hebrew
- cs-CZ: Czech
- uk-UA: Ukrainian
- tr-TR: Turkish
- pt: Portuguese
- pl-PL: Polish
- es: Spanish
- de-DE: German
- ca: Catalan
- fi: Finnish
- it-IT: Italian

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Address code review feedback on security improvements

- Fix IP validation to properly handle octets and validate ranges
- Remove redundant cloud metadata endpoint check (covered by link-local)
- Fix normalizeDate to throw error instead of using epoch as default
- Filter out expenses with invalid dates instead of setting to epoch
- Sanitize logging to avoid exposing sensitive data

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Fix log injection vulnerability in normalizeDate

Use template literal instead of string concatenation to prevent
potential log injection when logging the type of invalid date values.

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* fix json

* empty username when accessing the login screen

* Address security vulnerabilities and localization gaps in remote JSON import (#24)

* Initial plan

* Address code review feedback: SSRF protection, translations, and error handling

- Add protocol validation (http/https only) to validateRemoteUrl
- Enforce hard byte limit while reading response in fetchTrpcJson
- Prevent redirect-based SSRF by setting redirect: 'manual'
- Add all missing translation keys for hardcoded UI strings
- Change placeholder URL from spliit.app to neutral example.com
- Wrap syncAssociatedGroups in try/catch for best-effort handling
- Improve passkey error handling to differentiate error types (rate limit, not found, user cancel)
- Run prettier to fix inconsistent indentation

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Fix duplicate imports causing build failure in import-json-button.tsx (#25)

* Initial plan

* Fix duplicate imports in import-json-button.tsx

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* add whats new

* add whats new

* [WIP] Add end-user documentation and UX improvements (#27)

* Initial plan

* Address PR review comments: fix unused imports, update links, improve security and error handling

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* Improve icon choice and clarify HTML stripping behavior

Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>

* change icon

* update update with links to forked instance

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: carnach <26198260+carnach@users.noreply.github.com>
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.

3 participants