refactor: tighten read-only access token scope enforcement

This commit is contained in:
Shivam Mishra
2026-05-20 13:38:33 +05:30
parent d2de7bed6c
commit fe944720a8
2 changed files with 23 additions and 3 deletions
+19 -2
View File
@@ -8,6 +8,13 @@ class Api::BaseController < ApplicationController
api/v1/accounts/contacts#filter
].freeze
# GET-shaped endpoints that mutate state and must be blocked for read-only
# tokens despite the verb being "safe". Add any new write-on-GET endpoint
# here when it ships.
READ_ONLY_BLOCKED_GET_ACTIONS = %w[
api/v1/accounts/callbacks#register_facebook_page
].freeze
respond_to :json
before_action :authenticate_access_token!, if: :authenticate_by_access_token?
before_action :validate_bot_access_token!, if: :authenticate_by_access_token?
@@ -22,13 +29,23 @@ class Api::BaseController < ApplicationController
def enforce_read_only_token_scope
return unless @access_token&.scope == 'read_only'
return if request.get? || request.head? || request.options?
return if READ_ONLY_POST_ALLOWLIST.include?("#{params[:controller]}##{params[:action]}")
return if read_only_token_action_allowed?
render json: { error: 'This access token is read-only and cannot perform write operations.' },
status: :forbidden
end
def read_only_token_action_allowed?
action_key = "#{params[:controller]}##{params[:action]}"
return READ_ONLY_POST_ALLOWLIST.include?(action_key) unless safe_http_method?
READ_ONLY_BLOCKED_GET_ACTIONS.exclude?(action_key)
end
def safe_http_method?
request.get? || request.head? || request.options?
end
def check_authorization(model = nil)
model ||= controller_name.classify.constantize
+4 -1
View File
@@ -1,4 +1,7 @@
json.access_token resource.access_token&.token
# Withhold the full-scope token from callers who authenticated with a
# read-only token — otherwise GET /api/v1/profile would let a read-only
# holder lift the full token and bypass the scope gate entirely.
json.access_token resource.access_token&.token unless @access_token&.scope == 'read_only'
json.read_only_access_token resource.read_only_access_token&.token
json.account_id resource.active_account_user&.account_id
json.available_name resource.available_name