Files
sure/Gemfile
Max Barbare 51c93649da feat(snaptrade): replace device-flow OAuth with authorization-code + PKCE flow (#2747)
* feat(snaptrade): replace device-flow OAuth with authorization-code + PKCE flow

Squashed from 16 commits on snaptrade-oauth-apps for a clean rebase onto
current upstream/main ahead of opening a PR.

* fix(snaptrade): address PR #2747 review feedback on OAuth PKCE flow

- Remove unreachable dead-code guard in import_latest_snaptrade_data
- Guard apply_oauth_tokens! against a malformed payload missing access_token
- Wrap token endpoint network errors in ApiError and retry like data calls
- Remove unused Provider::Snaptrade#revoke_token! instance method
- Preserve return_to/accountable_type through the SnapTrade portal callback
  so the account-linking flow no longer drops users back to accounts_path
- Show the real absolute OAuth callback URL in self-hosted setup instructions
- Refresh brakeman.ignore fingerprint for the connect redirect after the
  return_to/accountable_type params were added

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y8SCCmKX6RphB5E73WSUQQ

* fix(snaptrade): don't retry non-idempotent OAuth/API requests

CodeRabbit flagged that Provider::Snaptrade retried OAuth token
exchanges/refreshes and all API POST/DELETE calls (get_connection_url,
delete_connection) after timeouts/connection failures. If the response
is lost after SnapTrade already consumed a single-use auth code,
rotated the refresh token, or applied a POST/DELETE, replaying the
request either fails with invalid_grant on a token that actually
succeeded, or risks duplicate side effects. Retries are now limited to
GET requests; OAuth token requests and non-GET API calls translate a
network failure straight into an ApiError without replay.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrrGkgSBEqhjjBmmH1fcXL

* fix(snaptrade): stop querying non-deterministically encrypted token via empty-string compare

CodeRabbit flagged that the syncable scope's where.not(oauth_access_token:
[nil, ""]) re-encrypts "" with a random IV on every query, so the ""
comparison can never match a stored ciphertext and is a silent no-op.
No code path ever persists oauth_access_token as "" (only nil or a real
token via apply_oauth_tokens!), so the exclusion is unnecessary --
narrowed the scope to a plain NULL check, which encryption handles
transparently since nil is never encrypted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NrrGkgSBEqhjjBmmH1fcXL

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 22:45:44 +02:00

150 lines
2.9 KiB
Ruby

source "https://rubygems.org"
ruby file: ".ruby-version"
# Rails
gem "rails", "~> 8.1.0"
# Drivers
gem "pg", "~> 1.5"
gem "redis", "~> 5.4"
# Deployment
gem "puma", ">= 7.2.1"
gem "bootsnap", require: false
# Assets
gem "importmap-rails"
gem "propshaft"
gem "tailwindcss-rails"
gem "lucide-rails"
# Hotwire + UI
gem "stimulus-rails"
gem "turbo-rails"
gem "view_component"
# https://github.com/lookbook-hq/lookbook/issues/712
# TODO: Remove max version constraint when fixed
gem "lookbook", "2.3.11"
gem "hotwire_combobox"
# Background Jobs
gem "connection_pool", "~> 2.5" # pin to 2.x; 3.0 breaks sidekiq 8.x
gem "sidekiq"
gem "sidekiq-cron"
gem "sidekiq-unique-jobs"
# Monitoring
gem "vernier"
gem "rack-mini-profiler"
gem "sentry-ruby"
gem "sentry-rails"
gem "sentry-sidekiq"
gem "posthog-ruby"
gem "logtail-rails"
if ENV["SKYLIGHT_ENABLED"] == "true"
gem "skylight", group: :development, require: false
else
gem "skylight", group: :production
end
# Active Storage
gem "aws-sdk-s3", "~> 1.208.0", require: false
gem "google-cloud-storage", "~> 1.59", require: false
gem "image_processing", ">= 1.2"
# Other
gem "ostruct"
gem "bcrypt", "~> 3.1"
gem "jwt"
gem "jbuilder"
gem "countries"
# OAuth & API Security
gem "doorkeeper"
gem "rack-attack", "~> 6.6"
gem "rack-cors"
gem "pundit"
gem "faraday"
gem "faraday-retry"
gem "faraday-multipart"
gem "inline_svg"
gem "octokit"
gem "pagy"
gem "rails-i18n"
gem "rails-settings-cached"
gem "tzinfo-data", platforms: %i[windows jruby]
gem "csv"
gem "rchardet" # Character encoding detection
gem "redcarpet"
gem "stripe"
gem "plaid"
gem "httparty"
gem "rotp", "~> 6.3"
gem "rqrcode", "~> 3.0"
gem "webauthn", "~> 3.4"
gem "activerecord-import"
gem "rubyzip", "~> 2.3"
gem "pdf-reader", "~> 2.12"
# OpenID Connect, OAuth & SAML authentication
gem "omniauth", "~> 2.1"
gem "omniauth-rails_csrf_protection", ">= 2.0"
gem "omniauth_openid_connect"
gem "omniauth-google-oauth2"
gem "omniauth-github"
gem "omniauth-saml", "~> 2.1"
# State machines
gem "aasm"
gem "after_commit_everywhere", "~> 1.0"
# AI
gem "ruby-openai"
gem "anthropic", "~> 1.0"
gem "langfuse-ruby", "~> 0.1.4", require: "langfuse"
group :development, :test do
gem "debug", platforms: %i[mri windows]
gem "brakeman", require: false
gem "rubocop-rails-omakase", require: false
gem "i18n-tasks"
gem "erb_lint"
gem "dotenv-rails"
end
if ENV["BENCHMARKING_ENABLED"]
gem "dotenv-rails", groups: [ :production ]
end
group :development do
gem "hotwire-livereload"
gem "letter_opener"
gem "ruby-lsp-rails"
gem "web-console"
gem "faker"
gem "benchmark-ips"
gem "stackprof"
gem "derailed_benchmarks"
gem "foreman"
end
group :development, :test do
gem "rspec-rails"
gem "rswag-api"
gem "rswag-specs"
gem "rswag-ui"
end
group :test do
gem "capybara"
gem "selenium-webdriver"
gem "mocha"
gem "vcr"
gem "webmock"
gem "climate_control"
gem "simplecov", require: false
end