Commit Graph

977 Commits

Author SHA1 Message Date
jubnl eff3fcfe10 test: update expected event_types count after adding synology_session_cleared 2026-04-11 18:44:40 +02:00
jubnl 0257e0d842 feat: route Synology session-cleared notification through unified send()
Replace direct createNotification() call with notificationService.send()
so the notification respects user preferences and reaches all enabled
channels (in-app, email, webhook) instead of only WebSocket.

Registers synology_session_cleared as a proper NotifEventType (inapp-only)
and adds localized text for all 14 supported languages.
2026-04-11 18:36:50 +02:00
jubnl 7871c06059 feat: enhance Synology Photos integration with OTP, SSL skip, and better UX
- Fix endpoint path: users now provide full base URL (e.g. https://nas:5001/photo)
- Add OTP/2FA field for Synology login
- Add skip SSL verification option (DB column + checkbox UI)
- Add device ID (synology_did) column for session tracking
- Trigger in-app notification when Synology session is cleared
- Show disconnection banner in MemoriesPanel
- Add URL hint in provider settings
- Map Synology API error codes to human-readable messages
- Update i18n for all locales
2026-04-11 18:25:42 +02:00
Julien G. bcc37d6b7d Merge pull request #562 from mauriceboe/main
Align dev
2026-04-11 15:41:34 +02:00
jubnl c96044f4f7 docs: document hosted Helm repository 2026-04-11 15:40:02 +02:00
github-actions[bot] 0f6be35870 chore: bump version to 2.9.13 [skip ci] v2.9.13 2026-04-11 13:26:44 +00:00
jubnl f47852d689 docs: improve FORCE_HTTPS, COOKIE_SECURE, TRUST_PROXY documentation
FORCE_HTTPS now documents all four effects (redirect, HSTS, CSP
upgrade-insecure-requests, secure cookie flag) and is clearly marked
optional. COOKIE_SECURE default updated to "auto" with explanation of
auto-derivation logic. TRUST_PROXY clarifies it's off in dev unless
set and is required for FORCE_HTTPS. charts/README.md gains FORCE_HTTPS
and TRUST_PROXY entries. README prose expanded to explain all three
vars and their interaction.
2026-04-11 15:26:19 +02:00
jubnl 4e683e92ec chore: merge main into dev to align environments
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 14:50:44 +02:00
Julien G. 3b080ac116 Merge pull request #544 from mauriceboe/feat/mcp-oauth2-addon-gating
Implement OAuth 2.1 authentication for MCP, enforce addon gating
2026-04-11 14:39:50 +02:00
jubnl 0efa316004 docs(mcp): update MCP.md and README for OAuth 2.1
- Restructure MCP.md setup section: OAuth 2.1 as primary auth path
  (auto-flow via DCR/consent screen), static tokens demoted to
  deprecated Option B with callout
- Add Authentication section documenting three-tier auth model
  (trekoa_, trek_, JWT) with prefixes, TTLs, and deprecation status
- Add OAuth Scopes section: all 24 scopes across 13 groups, scope
  inheritance rules, always-available tools note
- Fix outdated Limitations values: 60→300 req/min, 5→20 sessions
- Add new limitation rows: OAuth scope enforcement, per-client rate
  limiting, addon toggle invalidation
- Add token_auth_notice prompt to Prompts table
- README: mention OAuth 2.1 in MCP features, add Granular Scopes
  bullet, update tech stack auth line
2026-04-11 14:35:05 +02:00
jubnl 7a22d742ab test: add comprehensive coverage for OAuth scopes, MCP, and core services
Adds new and expanded test suites across client and server to cover the
OAuth 2.1 scope system, MCP session manager, collab service, unified
memories helpers, OIDC service, budget slice, and OAuth authorize page.
Also extends SonarQube coverage exclusions to include bootstrapping files
(migrations, scheduler, main.tsx, types.ts) that are not meaningfully
testable.
2026-04-11 14:08:09 +02:00
xenocent a4727c4c53 docs: add Indonesian to supported languages 2026-04-11 15:35:08 +07:00
xenocent 577f2b05ca feat(i18n): add Indonesian translation 2026-04-11 15:26:16 +07:00
jubnl 1585c472c2 fix(test): bumb default limit to reflect implementation 2026-04-11 02:32:17 +02:00
jubnl dd8d2ae54a chore(mcp): raise default session and rate-limit caps
Higher defaults reduce config friction for self-hosters while
staying within reasonable server limits.

- MCP_MAX_SESSION_PER_USER: 5 → 20
- MCP_RATE_LIMIT: 60 → 300 req/min
2026-04-11 02:29:11 +02:00
jubnl e3a5bc0f77 fix(tests): mock FormData uploads at API boundary to fix CI timeouts
jsdom's FormData is incompatible with undici's ReadableStream serialisation
used by MSW 2.x — requests hang under CI resource constraints but pass locally.
Replace server.use() + implicit HTTP roundtrip with vi.spyOn().mockResolvedValueOnce()
for all five FormData POST tests (uploadAvatar, uploadRestore, addFile, importGpx).
2026-04-11 02:29:11 +02:00
jubnl 535c06bb3f feat(mcp): granular OAuth scopes and per-client rate limiting
- Split `media:read` into `geo:read` and `weather:read` scopes
- Add dedicated `atlas:read/write` scopes (previously under `places`)
- Add dedicated `todos:read/write` scopes (previously under `collab`)
- Rate limiting now keyed by userId+clientId instead of userId alone
- Bind MCP sessions to the OAuth client that created them
- Log MCP tool calls to audit log with clientId
- Invalidate all MCP sessions on addon state change
- Reduce session sweep interval from 10min to 1min
- Update all translations with new scope labels
2026-04-11 02:06:32 +02:00
Marco Sadowski 6a632137ed refactor(trip): Naver List Import as Addon 2026-04-10 15:37:38 +02:00
Marco Sadowski f82f00216b Merge remote-tracking branch 'origin/dev' into naver-list-import 2026-04-10 15:35:16 +02:00
Maurice be248e1ad4 Update Discord link in README.md v2.9.12 2026-04-10 14:13:01 +02:00
Marco Pasquali abc5ee2aa7 add autofocus to place search 2026-04-10 11:05:10 +02:00
github-actions[bot] e290c7c522 chore: bump version to 2.9.12 [skip ci] 2026-04-10 05:51:22 +00:00
jubnl f20eb6639f chore(workflow): remove delete tag workflow 2026-04-10 07:50:51 +02:00
github-actions[bot] d0176d7ed6 chore: bump version to 2.9.12 [skip ci] 2026-04-10 05:44:33 +00:00
jubnl 8402f3bcfd chore: add workflow to delete Docker tags 2026-04-10 07:44:10 +02:00
github-actions[bot] 6caa966a52 chore: bump version to 2.10.0 [skip ci] 2026-04-10 05:36:13 +00:00
Julien G. 098918b416 Merge pull request #514 from gravitysc/chart-releaser
Chart releaser
2026-04-10 07:36:00 +02:00
jubnl 4670d4914c fix(admin): collapse long scope lists with toggle in MCP Access panel
Show first 6 scope badges per session with a clickable "+N more" pill
that expands to all scopes; a "show less" pill collapses them again.
Also fix column alignment to items-start so Owner/Created stay at the
top of tall rows.
2026-04-10 06:59:40 +02:00
jubnl 3ce9962b32 fix(admin): improve OAuth sessions layout in MCP Access panel
Replace overflowing scopes column with inline wrapping badges under the
client name, and drop the redundant client_id UUID row.
2026-04-10 06:53:22 +02:00
jubnl 4b1286d53c feat(admin): add OAuth sessions to MCP Access panel
Show active OAuth sessions (first) and static API tokens (second) in
the admin MCP Access tab. Admins can revoke any OAuth session, which
immediately terminates the live MCP transport for that client.

- Add admin-level listOAuthSessions / revokeOAuthSession in adminService
- Add GET /admin/oauth-sessions and DELETE /admin/oauth-sessions/:id routes
- Restructure AdminMcpTokensPanel into two sections; rename tab to MCP Access
- Fix stale writeAudit call in rotate-jwt-secret route (user_id → userId)
- Add admin.oauthSessions.* i18n keys across all 14 locale files
2026-04-10 06:47:35 +02:00
jubnl cc2a2ddca3 remove(oauth): drop browser-initiated DCR registration flow
OAuthRegisterPage and its server routes (GET /api/oauth/register/validate,
POST /api/oauth/register) are superseded by the RFC 7591 machine-to-machine
DCR endpoint (POST /oauth/register). Claude.ai and compliant MCP clients
register via RFC 7591, then go through the standard /oauth/authorize consent
screen for scope selection.
2026-04-10 06:23:07 +02:00
jubnl 4ad1ccf5dd fix(oauth): gate scope selection UI to DCR clients only
Settings-created clients have fixed scopes chosen at creation time and
should show a read-only scope list on the consent screen. Only DCR-registered
clients expose the interactive checkbox UI for user-controlled scope selection.
2026-04-10 06:03:52 +02:00
jubnl ac9c5784ee feat(oauth): user scope selection on authorization consent screen
When an MCP client registers via DCR and redirects the user to authorize,
the consent screen now shows checkboxes instead of a read-only scope list.
The user can grant any subset of the scopes the client requested — the same
level of control as when creating a client manually from user settings.

- selectedScopes state initialized from validation.scopes (all pre-checked)
- Group-level indeterminate checkbox to select/deselect an entire category
- Approve button reflects selection count and is disabled when nothing selected
- Auto-approve path (consent already on record) bypasses selection and passes
  the existing granted scopes directly
2026-04-10 06:03:44 +02:00
jubnl cb3aeda8e0 fix(oauth): add public RFC 7591 DCR endpoint at POST /oauth/register
Claude.ai's start-auth flow POSTs to the registration_endpoint advertised
in the discovery document, but no public handler existed at /oauth/register
(only /api/oauth/register with browser cookie auth). This caused a
start_error redirect immediately on every connect attempt.

- Add POST /oauth/register to oauthPublicRouter following RFC 7591
- Make oauth_clients.user_id nullable via a raw (no-transaction) migration
  so anonymous DCR clients can be created without a user context
- Update migration runner to support { raw: () => void } migrations for
  DDL that requires PRAGMA foreign_keys = OFF outside a transaction
- Update createOAuthClient to accept userId: number | null with a global
  cap (500) for anonymous DCR clients in place of the per-user limit
2026-04-10 05:42:18 +02:00
jubnl 9b1baaf7b8 feat(oauth): browser-initiated dynamic client registration (DCR)
Adds an OAuth 2.1 public client registration flow so MCP clients can
self-register via a user-facing consent page instead of requiring manual
setup in Settings.

Server:
- DB migration adds `is_public` and `created_via` columns to oauth_clients
- New GET /api/oauth/register/validate — validates DCR params, returns
  requested scopes; unauthenticated callers get loginRequired flag
- New POST /api/oauth/register — creates a public client, saves consent,
  and redirects with client_id (cookie auth required)
- `authenticateClient` / `refreshTokens` skip secret check for public
  clients (PKCE provides the security guarantee)
- `createOAuthClient` accepts options for isPublic/createdVia; public
  clients store an opaque secret hash instead of a usable secret
- `rotateOAuthClientSecret` blocked on public clients
- `isValidRedirectUri` extracted as a shared helper
- Discovery metadata now advertises registration_endpoint and auth method
  `none`; token/revoke endpoints no longer require client_secret for
  public clients

Client:
- New OAuthRegisterPage (/oauth/register) — loading → optional
  login-required gate → scope selection → done states
- New ScopeGroupPicker component — collapsible groups, indeterminate
  checkboxes, select-all per group or globally
- oauthApi.register.{validate,submit} added to api/client.ts
- apiClient exported so it can be reused outside api/client.ts
- IntegrationsTab tests fixed for new collapsible section structure
- collab_notes fallback changed from undefined to [] in MCP trip tools
2026-04-10 05:20:54 +02:00
jubnl 81a360f9a7 fix(mcp): bundle data with deprecation error and add verbatim instruction
Claude retried the tool silently and answered without mentioning the
notice. Two fixes:

1. Include actual trip data in the same isError response so no retry
   is needed and Claude has both the warning and the answer in one shot.

2. Reword the notice to instruct Claude to include the warning verbatim
   in its response before answering the user's question.
2026-04-10 03:15:18 +02:00
jubnl a74a6313dd fix(mcp): instruct Claude to retry tool call after deprecation notice
Claude stopped after surfacing the error rather than retrying.
Append an explicit instruction to retry the tool call so the user
gets both the deprecation warning and their actual answer.
2026-04-10 03:10:02 +02:00
jubnl 89a109560e fix(mcp): return deprecation notice as isError tool result
isError: true is the one MCP mechanism Claude.ai cannot ignore —
it is obligated to surface tool errors to the user.

On the first tool call of a static-token session, return only the
deprecation notice with isError: true (no data). The per-session
_noticeEmitted flag is set before returning, so the immediate retry
(or any subsequent call) goes through normally and returns real data.
2026-04-10 03:04:05 +02:00
jubnl ce36b550c3 fix(mcp): embed deprecation notice as JSON field instead of separate content item
Claude.ai filters out prepended content items as metadata but must
process top-level JSON fields as response data, making it far more
likely to surface the notice to the user.
2026-04-10 02:54:32 +02:00
jubnl 1187883c6b feat(mcp): always register list_trips & get_trip_summary; inject deprecation notice into tool results
Navigation tools:
- list_trips and get_trip_summary are now always registered for any
  OAuth session regardless of granted scopes — they are required for
  trip ID discovery before any scoped tool can be used
- get_trip_summary filters optional sections (budget, packing, collab,
  reservations) by the client's OAuth scopes when called without trips:read

Deprecation notice:
- Inject static token deprecation warning into the first tool result
  (list_trips or get_trip_summary) via a per-session closure so Claude
  is forced to surface it — the instructions field alone is only
  background context and is not proactively shown to the user

UI:
- OAuth client creation modal: add hint explaining the always-available
  tools, remove the "must select at least one scope" submit guard
- OAuth consent screen: add "Always included" section showing list_trips
  and get_trip_summary; handles zero-scope clients gracefully (empty
  permissions section is hidden)
2026-04-10 02:45:16 +02:00
jubnl cef86cbcd9 feat(mcp): add base server instructions for all MCP sessions
Injects a structured BASE_MCP_INSTRUCTIONS string into every session's
initialize response so Claude has data model, workflow, and behavioral
context without needing to infer it from tool names alone.

Covers: data model hierarchy (trip→day→place→assignment), key discovery
workflow (list_trips → get_trip_summary), correct place-to-itinerary
flow (search_place → create_place → assign_place_to_day), accommodation
creation order, access rules, date/time format, add-on feature list,
and common pitfalls (e.g. don't skip search_place, confirm before bulk
deletes).

Static token deprecation notice is appended on top when applicable.
2026-04-10 02:23:32 +02:00
jubnl bf23b2d2f2 fix(mcp): surface static token deprecation via server instructions
The deprecation warning was registered as an MCP prompt that clients
must explicitly fetch — it never fired automatically. Move it to the
ServerOptions.instructions field, which is returned in the initialize
response and automatically read by Claude and other MCP clients as
system context.
2026-04-10 02:18:06 +02:00
Ben Haas 4a16442db0 Replace Google Maps URL regex with a safer utility function 2026-04-09 17:06:41 -07:00
jubnl 7c0a0d5f39 security(oauth): harden OAuth 2.1/MCP implementation (Critical + High + Medium findings)
Address 14 security findings from internal review of the OAuth 2.1 + MCP layer:

Critical:
- C1: Scope-gate all MCP resources (trips, budget, packing, collab, atlas, vacay, etc.)
- C2: Wire token/session revocation into active MCP session lifecycle per (user, client_id)
- C3: Refresh-token replay detection via parent_token_id chain + cascade revoke on replay

High:
- H1: Validate PKCE code_challenge (43-char base64url) and code_verifier (43–128 chars) format
- H2: Rate-limit /oauth/token (30/min), /authorize/validate (30/min), /oauth/revoke (10/min)
- H3: Strip client metadata from unauthenticated /authorize/validate responses (oracle prevention)
- H4: Constant-time secret comparison via crypto.timingSafeEqual (prevents timing attacks)
- H5: Collapse all invalid_grant cases to a single generic message; log specifics server-side

Medium:
- M1: Set Cache-Control: no-store + Pragma: no-cache on token endpoint responses
- M2: Return 404 (not 200/403) on discovery + revoke endpoints when MCP addon is disabled
- M4: Audit-log all OAuth lifecycle events (create, consent, issue, refresh, revoke, replay)
- M5: Union consent scopes on re-authorization instead of replacing existing grants
- M7: Require httpOnly cookie auth (not Bearer JWT) on all state-mutating OAuth endpoints
- M8: Strict Bearer scheme check in MCP token verification

Refactoring:
- Extract MCP session management (sessions Map, revokeUserSessions, revokeUserSessionsForClient)
  into mcp/sessionManager.ts to break the circular dependency between oauthService and mcp/index
- Extract verifyJwtAndLoadUser helper in auth middleware, shared by authenticate and new
  requireCookieAuth middleware

Tests:
- Fix all existing integration tests broken by the security hardening (OAUTH-019 to OAUTH-032)
- Add 13 new integration tests covering M1, M2, H1, H3, H5, M5, M7, C3
- Add 14 new unit tests covering C2, C3, H1, H3, M5 behaviors in oauthService
2026-04-10 02:03:27 +02:00
Ben Haas 8f1445e6df Fix too permissive regex for Google Maps 2026-04-09 16:11:05 -07:00
jubnl e91ee04d93 fix(csp): disable Vite module preload polyfill to prevent inline script violation
The polyfill was injected as an inline script at build time, causing a hard
CSP block under script-src 'self'. All browsers that support ES modules also
support modulepreload natively, so the polyfill is unnecessary.
2026-04-10 01:10:32 +02:00
Ben Haas 583ac6d4d9 Add tests for mapsApi.autocomplete and autocompletePlaces service interactions 2026-04-09 16:02:10 -07:00
jubnl 8212f3c023 feat(oauth): add trips:share scope and redesign consent screen
Introduce trips:share as a dedicated OAuth scope for managing public
share links, decoupled from trips:read and trips:write. Share link
tools (get/create/delete_share_link) now gate on canShareTrips()
instead of the generic read/write scopes. Scope added to both client
and server definitions with full test coverage.

Redesign the consent screen from a narrow single-column card
(max-w-sm) to a two-panel layout (max-w-2xl): app identity and
action buttons on the left, scrollable scope list on the right.
Responsive — stacks vertically on mobile.
2026-04-10 00:55:12 +02:00
Ben Haas 35d676e76e Add real-time autocomplete suggestions when typing in the place search
field, with Google Places Autocomplete API and Nominatim fallback.

  - Add POST /api/maps/autocomplete route and autocompletePlaces service
  - Add mapsApi.autocomplete client method
  - Add debounced autocomplete dropdown to PlaceFormModal with keyboard
    navigation (arrow keys, enter, escape) and mouse selection
  - Use place details API to populate form fields on suggestion selection
  - Derive location bias from existing trip places for better results
  - Extract Google Maps URL regex to shared constant
2026-04-09 15:19:49 -07:00
jubnl 41f1dd9ce5 fix(oauth): select ot.user_id instead of u.id in getUserByAccessToken
u.id was returned by SQLite as `id` but the code read `row.user_id`,
which was undefined. This caused all MCP calls to resolve userId as
undefined, making list_trips return empty and canAccessTrip deny all
access when authenticated via OAuth 2.1.
2026-04-09 23:59:11 +02:00