feat(oauth): validate callback URL origin / code / state in OAuth browser flow#13
Closed
NeritonDias wants to merge 74 commits intoEvolutionAPI:mainfrom
Closed
feat(oauth): validate callback URL origin / code / state in OAuth browser flow#13NeritonDias wants to merge 74 commits intoEvolutionAPI:mainfrom
NeritonDias wants to merge 74 commits intoEvolutionAPI:mainfrom
Conversation
- Added a new Setup page for creating the admin account. - Integrated setup service to handle account creation. - Updated routing to include the new setup page. - Removed the old CreateAccount onboarding page. - Updated various components to use a unified logo. - Enhanced global configuration context to manage setup status. - Added internationalization support for setup page in English and Portuguese. - Updated RouterGuard to handle redirection based on setup status.
- Removed accountId references from ReconnectService and ActionCableService, simplifying connection logic. - Updated PermissionsService to cache account permissions without accountId. - Cleaned up various services (macros, notifications, pipelines, reports, scheduledActions, teams, users) by removing accountId comments and ensuring accountId is only used in headers. - Introduced AppDataStore for managing application data, including account, agents, inboxes, labels, and teams with caching. - Modified AuthStore to eliminate currentAccountId, relying on AppDataStore for account data management. - Adjusted types across the application to remove accountId where unnecessary, ensuring a cleaner and more consistent codebase. - Updated createCallbackPage utility to remove accountId from integration service callbacks.
…stem status fetch
… for improved fallback handling
Build and push multi-arch images (amd64 + arm64) in parallel to Docker Hub on push to main and version tags. Uses SHA-pinned actions, provenance attestation, and env-based variable passing to prevent script injection.
Story 2.3: Adds Test Connection button to SmtpConfig page with i18n support (6 locales). Fixes adminConfigService to extract .configs from API response wrapper. Tightens AdminConfigData type and removes as any casts.
- Implement TourFab button in the app header that auto-detects the current route and shows a tour trigger when a tour is registered - Add WelcomeTourModal for onboarding new users on first access - Create tourRegistry (pub/sub store) and matchTourRoute for route-based tour discovery - Add useJoyride hook and JoyrideTooltip custom component - Add i18n tour translations for pt-BR, pt, en, es, fr and it Tours created (route → tourKey): /dashboard → DashboardTour /chat → ChatTour /contacts → ContactsTour /contacts/scheduled-actions → ScheduledActionsTour /pipelines → PipelinesTour /agents/list → AgentsTour /agents/custom-tools → AgentsCustomToolsTour /agents/custom-mcp-servers → AgentsCustomMCPsTour /settings/account → SettingsTour /settings/users → SettingsAgentsTour /settings/teams → SettingsTeamsTour /settings/labels → SettingsLabelsTour /settings/attributes → SettingsCustomAttributesTour /settings/canned-responses → SettingsCannedResponsesTour /settings/macros → SettingsMacrosTour /settings/integrations → SettingsIntegrationsTour /settings/access-tokens → SettingsAccessTokensTour /channels → ChannelsTour /campaigns → CampaignsTour /journeys → JourneyTour - Add data-tour attributes to all targeted page components - Add searchDataTour prop to BaseHeader for granular search targeting - Mount each tour component inside its own page following the existing AccountSettings pattern
Add Storage Configuration page to Admin Settings with provider selector (Local/S3/S3-Compatible), conditional cloud fields, Zod validation, masked secret handling, and Test Connection button. Includes route, sidebar navigation, i18n translations (6 locales), and 12 unit tests.
- PaymentsConfig.tsx: form with Zod validation, masked secrets, toast feedback - Route /settings/admin/payments with lazy loading - Payments sidebar entry with CreditCard icon - i18n translations for all 6 locales (en, pt-BR, pt, es, fr, it) - 9 unit tests covering rendering, save, secret masking - Review fixes: autoComplete="off", aria-label, empty→null for cleared secrets, deduplicated title
…App, Instagram) Add admin settings page with tabbed UI for configuring Facebook, WhatsApp, and Instagram channel credentials. Each tab saves independently via its respective config type. Includes masked secret fields, boolean toggles, read-only locked fields, Zod validation, i18n in 6 locales, and 19 tests.
Extend ChannelConfig with 3 new tabs for Evolution API, Evolution Go, and Twitter credentials. Extract ChannelFormCard and TextField components to reduce boilerplate. Add i18n translations for all 6 locales and 14 new tests.
…rompt templates Admin panel page for configuring OpenAI API connection (URL, key, model, audio transcription toggle) and 9 AI prompt templates (reply, summary, rephrase, grammar, shorten, expand, friendly, formal, simplify). Includes i18n for all 6 locales, route, sidebar nav, and unit tests.
…Slack) Add admin settings page for configuring OAuth credentials for Linear, HubSpot, Shopify, and Slack integrations. Each integration has independent form sections with client ID and client secret fields, masked secret handling, and independent save. Includes route, sidebar navigation with Puzzle icon, i18n translations for all 6 locales, and 17 unit tests.
Add InboundEmailConfig page with provider selector (Relay/Mailgun/Mandrill/SendGrid), conditional credential fields, Zod validation, and masked secrets. Add route, MailOpen nav icon, and translations in all 6 locales. Include 15 tests covering provider switching, validation, error handling, and secrets.
Create PushNotificationsConfig page with Firebase credentials (textarea), project ID, iOS App ID, and Android Bundle ID fields. Add route, Bell icon navigation, and translations in all 6 locales.
feat: Add guided tours system with Joyride across all main sections
Replace shared privacy options with field-specific options to match Evolution API and Evolution Go enum constraints (readreceipts, profile, status, online, last, groupadd each have different allowed values).
WelcomeTourModal: Start Tour now saves onboarding:preference as guided and Skip saves it as solo, both persisted via the /user_tours API alongside the existing welcome dismissal key useJoyride: tours no longer auto-start based solely on welcome dismissal; they now require onboarding:preference to be completed (guided choice) — users who chose explore alone will never see automatic tours on any section TourFab: added data-tour attribute nav-tour-icon to the help button for future targeting i18n: added soloHint keys (title + content) across all 6 supported languages (en, pt-BR, pt, es, fr, it)
…cards - Create TooltipInfo component (HelpCircle icon + hover popover) - Add optional tooltip prop to DashboardMetricCard, AreaChartCard, BarChartCard, DonutChartCard and OperationHeatmapCard - Wire tour i18n copy (steps 2–20) as hover tooltips across DashboardMetricsSection, DashboardTrendsSection and DashboardPerformanceSection — reuses existing translations in all 6 languages with no new keys required - Fix TourFab: Ver tour desta página label now only appears after the page tour has been completed or skipped
feat(dashboard): add inline tooltips with tour copy to all dashboard …
setupService already sent form.crmExperience in the pre-login survey payload, but the field was missing from the OnboardingFormData type, from the form state, and from the UI. This caused a TypeScript build error and meant that post-login submissions via surveyService were silently dropping the same data even if the user had filled it in. Adds the field to the type and initial state, renders a new SelectField between the "biggest pain" and "main goal" questions using the existing survey.crm i18n keys (already translated in all six locales), and bumps the progress bar total from 6 to 7. Also teaches surveyService.toSnakeCase to include crm_experience so both pre-login and post-login paths stay consistent.
…d is disabled On the new-channel flow, Facebook and Instagram cards are disabled when canFB is false (FB app id / version not configured in admin settings), and the WhatsApp Cloud provider card is disabled when canWpCloud is false. Before this change, the cards were only visually dimmed with no hint about why the user could not click them. ChannelCard and ProviderGrid now accept an optional disabledTooltip (a string for ChannelCard, a resolver function for ProviderGrid) and wrap the disabled card in a Radix Tooltip that surfaces it on hover. The wrapper is only rendered when both disabled and a tooltip are present, so enabled cards keep their current behavior. ChannelGrid passes a single i18n key for the Facebook/Instagram case, and NewChannel passes the same key for whatsapp_cloud. The string is added to all six locale files under channelGrid.notConfiguredTooltip and points the user at Admin Settings > Channels (matching the real menu labels in adminSettings.json).
…k-config-and-channel-tooltips feat(channels): tooltip on disabled channel cards + onboarding crmExperience field
ModelSelector now calls the new core endpoint GET /agents/apikeys/:id/models whenever the user picks an API key, and uses the returned list instead of the hardcoded availableModels catalog. If the provider is one the backend doesn't support dynamically (e.g. Perplexity, Bedrock, Vertex), or the call fails, the component falls back to the existing hardcoded list so nothing regresses. Adds a loading state on the trigger button while the models are being fetched and a new ApiKeyModelsResponse type to the agent types barrel.
The 'Test agent in chat' card on the wizard success step navigated to the global /conversations inbox, where there is no way to test the newly-created agent. Now it routes to the agent edit page with a ?test=1 query param, which auto-opens the AgentTestChat dialog — the same drawer reachable from the edit header's Test button.
The agent name Badge in the AgentChatArea header sat at the top-right of the content, overlapping the Radix dialog's close button. Add right padding and a small gap so the badge sits to the left of the X.
Adds a 'Copy ID' entry (with toast feedback) to: - Agents list actions dropdown - Pipelines list table and card menus - Pipeline kanban header, stage and item menus Switches the pipeline Duplicate icon to CopyPlus so it stays visually distinct from the Copy icon now used for Copy ID. Translation keys added for all six locales.
PermissionsContext was flipping isReady true as soon as the user loaded but before the account/user permissions fetch actually ran, so route guards briefly evaluated can() against empty arrays and redirected to /unauthorized — the user saw an 'Access Denied' page flash and disappear right after logging in, especially after a hard reload. Track whether both fetches have completed for the current user with dedicated flags that reset on user change, and require them in isReady.
AppInitializer fetched /user_tours only inside the main init effect, which short-circuits once isInitialized is true. On the first page load of a logged-out user the effect marks itself initialized, so when the user subsequently logs in the tours store (not persisted) stays empty and the Welcome modal reappears even after being skipped. Move the tour fetch into its own effect keyed on user.id so every login (including after a hard reload) repopulates the store before WelcomeTourModal and useJoyride evaluate their dismissed flags.
- ChannelConfig schemas enforce required essentials per integration with
mode:'onChange'; Save disabled while invalid; inline errors only render
after the first submit attempt (formState.submitCount>0).
- SecretField gains an error prop and a sentinel so secrets already stored
in the DB satisfy required validation without exposing or retransmitting
the value; buildPayload maps the sentinel back to null on save.
- Replace save-error toast with a persistent, selectable inline banner in
ChannelFormCard so the admin can copy the backend error text.
- Invalidate the GlobalConfig cache after each save and broadcast to
listeners so the channel creation flow reflects hasXxxConfig changes
without a manual reload.
- useChannelForm reads hasFacebook/hasWhatsapp/hasInstagram/hasTwitter
from the backend booleans; email/google and email/microsoft stay on
their existing string gates.
- CloudWhatsappForm:
- SDK effect now depends on wpAppId/wpApiVersion so it waits for the
GlobalConfig response before calling FB.init.
- Drop script.crossOrigin='anonymous' on the SDK script tag here, in
FacebookChannelForm and in AuthorizationBanners; Facebook's CDN does
not emit the CORS headers it requires and the script failed to load.
- The FB embedded-signup button on NewChannel is now gated by the
WhatsApp config (canWpCloud), because the SDK initializes with
wpAppId/wpApiVersion and logs in with wpWhatsappConfigId.
- Add generic i18n keys: common:validation.required (6 locales),
channels.dismissError for the banner close button, and
cloudWhatsappForm.facebookIntegration.loadingSDK.
- WhatsAppForm under components/integrations/forms keeps two fields
marked required as documentation for when that orphan directory is
adopted; no page renders it today.
- restore DarkModeProvider with localStorage + prefers-color-scheme and re-render ThemeToggle in the header - replace EVO_CRM.png with theme-aware AppLogo (dark/light SVG variants) across all 13 usages - fix contrast on dashboard tone badges, BaseStatusBadge and channel icon wrapper in light mode - rename "Operators" to "Team Members" in English locales to avoid collision with AI "Agents"
…mismatch) - OAuthDeviceCodeFlow.tsx: prefix unused keyId with underscore - integrations/main.ts: make auth_method optional in OpenAIFormData to match OpenAIConfig
OAuthBrowserFlow.tsx:
- Parse the pasted callback URL with the native URL constructor instead
of a bare substring match on 'code='. Reject malformed URLs with a
clear user-facing message.
- Check url.origin + url.pathname against the expected redirect target
(http://localhost:1455/auth/callback) so a URL from the wrong origin
cannot be forwarded to auth-complete.
- Surface OAuth provider errors (error, error_description query params)
before attempting the token exchange.
- Extract 'state' from the authorize URL returned by auth-start, store
it in component state and compare it against the 'state' query param
in the pasted callback URL — defense in depth on top of the
server-side check. A mismatch aborts the flow locally.
- console.error / console.warn every failure path so production
issues are diagnosable from the browser devtools.
- Preserve the upstream error message when available rather than
swallowing it with a generic string.
ApiKeysModal.tsx:
- OAuthStatusBadge was being rendered with clientId={apiKey.id}, which
confused the 'account / tenant id' namespace with the API key id.
Pull the real account id from useAppDataStore (same pattern as
OpenAIModal.tsx) and pass it as clientId.
There was a problem hiding this comment.
Sorry @NeritonDias, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
c70ad83 to
76fc920
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Hardens the OAuth Codex browser paste-URL flow introduced alongside the upcoming processor change (evo-ai-processor-community#2).
OAuthBrowserFlow.tsxURLconstructor instead of a substring match on'code='. Malformed URLs are rejected with a clear user-facing message.origin + pathnameis nothttp://localhost:1455/auth/callback— the only redirect target the backend will exchange.error,error_descriptionquery params) before attempting the token exchange.statefrom the authorize URL returned byauth-start, store it in component state, and compare against thestatequery param in the pasted callback URL. Defense in depth on top of the server-side validation added in the processor PR.console.error/console.warnevery failure path so production issues are diagnosable from the browser devtools, and preserve the upstream error message rather than swallowing it with a generic string.ApiKeysModal.tsxOAuthStatusBadgewas being rendered withclientId={apiKey.id}, conflating the account / tenant id namespace with the API key id. Pull the real account id fromuseAppDataStore(same pattern already used inOpenAIModal.tsx) and pass it asclientId.Test plan
?error=access_denied— UI shows the provider's error descriptionstatedoes not match the one fromauth-start— UI rejects before calling the backendauth-completeis called, status badge flips to 'Connected', and the rendered API key in the list shows the account's status badgeRelated PRs