rails: Rails v7: Error on direct upload (ActiveStorage::InvalidDirectUploadTokenError)
Steps to reproduce
Hello, I’m working on a new application with Rails 7 (I started with the release candidate). I’m having an issue with ActiveStorage in which I cannot perform a direct upload.
My configuration is a standard rails app (non-SPA), using the new importmap functionality:
importmap.rb
:
pin '@rails/activestorage', to: 'activestorage.esm.js'
I’m doing a POST
against http://localhost:3000/rails/active_storage/direct_uploads
with following params:
{"blob":{"filename":"IMG_0700.JPG","content_type":"image/jpeg","byte_size":1970730,"checksum":"il85E0GriR/6BKnCtaDmYg=="},"direct_upload_token":"6o-PbMbzQwHxI3pPpUWXXvSCIvjSsR3NEbZhqlowWiZPi5oNF3oBKc-1h-xkRM_yidedFVp22Xf0NvJ66Q7D3g"}
The token is coming from: meta[name="csrf-token"]
And the API is returning me an error 500:
ActiveStorage::InvalidDirectUploadTokenError in ActiveStorage::DirectUploadsController#create
ActiveStorage::InvalidDirectUploadTokenError
Extracted source (around line #35):
#33 ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, correct_token)
#34 rescue ArgumentError
*35 raise ActiveStorage::InvalidDirectUploadTokenError
#36 end
#37
#38 def direct_upload_token_hmac(session, identifier) # :doc:
Extracted source (around line #31):
#29 end
#30
*31 def valid_direct_upload_token?(token, attachment_name, session) # :doc:
#32 correct_token = direct_upload_token(session, attachment_name)
#33 ActiveSupport::SecurityUtils.fixed_length_secure_compare(token, correct_token)
#34 rescue ArgumentError
Extracted source (around line #21):
#19 decoded_token = token_components.join(SEPARATOR)
#20
*21 return service_name if valid_direct_upload_token?(decoded_token, attachment_name, session)
#22
#23 raise ActiveStorage::InvalidDirectUploadTokenError
#24 end
Rails.root: /home/xxx
Framework Trace
activestorage (7.0.0) lib/active_storage/direct_upload_token.rb:35:in `rescue in valid_direct_upload_token?'
activestorage (7.0.0) lib/active_storage/direct_upload_token.rb:31:in `valid_direct_upload_token?'
activestorage (7.0.0) lib/active_storage/direct_upload_token.rb:21:in `verify_direct_upload_token'
activestorage (7.0.0) app/controllers/active_storage/direct_uploads_controller.rb:20:in `verified_service_name'
activestorage (7.0.0) app/controllers/active_storage/direct_uploads_controller.rb:10:in `create'
actionpack (7.0.0) lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
actionpack (7.0.0) lib/abstract_controller/base.rb:214:in `process_action'
actionpack (7.0.0) lib/action_controller/metal/rendering.rb:53:in `process_action'
actionpack (7.0.0) lib/abstract_controller/callbacks.rb:234:in `block in process_action'
activesupport (7.0.0) lib/active_support/callbacks.rb:118:in `block in run_callbacks'
actiontext (7.0.0) lib/action_text/rendering.rb:20:in `with_renderer'
actiontext (7.0.0) lib/action_text/engine.rb:69:in `block (4 levels) in <class:Engine>'
activesupport (7.0.0) lib/active_support/callbacks.rb:127:in `instance_exec'
activesupport (7.0.0) lib/active_support/callbacks.rb:127:in `block in run_callbacks'
activesupport (7.0.0) lib/active_support/callbacks.rb:138:in `run_callbacks'
actionpack (7.0.0) lib/abstract_controller/callbacks.rb:233:in `process_action'
actionpack (7.0.0) lib/action_controller/metal/rescue.rb:22:in `process_action'
actionpack (7.0.0) lib/action_controller/metal/instrumentation.rb:67:in `block in process_action'
activesupport (7.0.0) lib/active_support/notifications.rb:206:in `block in instrument'
activesupport (7.0.0) lib/active_support/notifications/instrumenter.rb:24:in `instrument'
activesupport (7.0.0) lib/active_support/notifications.rb:206:in `instrument'
actionpack (7.0.0) lib/action_controller/metal/instrumentation.rb:66:in `process_action'
actionpack (7.0.0) lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
activerecord (7.0.0) lib/active_record/railties/controller_runtime.rb:27:in `process_action'
actionpack (7.0.0) lib/abstract_controller/base.rb:151:in `process'
actionview (7.0.0) lib/action_view/rendering.rb:39:in `process'
actionpack (7.0.0) lib/action_controller/metal/live.rb:266:in `block (2 levels) in process'
activesupport (7.0.0) lib/active_support/concurrency/share_lock.rb:162:in `sharing'
activesupport (7.0.0) lib/active_support/dependencies/interlock.rb:37:in `running'
actionpack (7.0.0) lib/action_controller/metal/live.rb:258:in `block in process'
actionpack (7.0.0) lib/action_controller/metal/live.rb:343:in `block in new_controller_thread'
Full Trace
activestorage (7.0.0) lib/active_storage/direct_upload_token.rb:35:in `rescue in valid_direct_upload_token?'
activestorage (7.0.0) lib/active_storage/direct_upload_token.rb:31:in `valid_direct_upload_token?'
activestorage (7.0.0) lib/active_storage/direct_upload_token.rb:21:in `verify_direct_upload_token'
activestorage (7.0.0) app/controllers/active_storage/direct_uploads_controller.rb:20:in `verified_service_name'
activestorage (7.0.0) app/controllers/active_storage/direct_uploads_controller.rb:10:in `create'
actionpack (7.0.0) lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
actionpack (7.0.0) lib/abstract_controller/base.rb:214:in `process_action'
actionpack (7.0.0) lib/action_controller/metal/rendering.rb:53:in `process_action'
actionpack (7.0.0) lib/abstract_controller/callbacks.rb:234:in `block in process_action'
activesupport (7.0.0) lib/active_support/callbacks.rb:118:in `block in run_callbacks'
actiontext (7.0.0) lib/action_text/rendering.rb:20:in `with_renderer'
actiontext (7.0.0) lib/action_text/engine.rb:69:in `block (4 levels) in <class:Engine>'
activesupport (7.0.0) lib/active_support/callbacks.rb:127:in `instance_exec'
activesupport (7.0.0) lib/active_support/callbacks.rb:127:in `block in run_callbacks'
activesupport (7.0.0) lib/active_support/callbacks.rb:138:in `run_callbacks'
actionpack (7.0.0) lib/abstract_controller/callbacks.rb:233:in `process_action'
actionpack (7.0.0) lib/action_controller/metal/rescue.rb:22:in `process_action'
actionpack (7.0.0) lib/action_controller/metal/instrumentation.rb:67:in `block in process_action'
activesupport (7.0.0) lib/active_support/notifications.rb:206:in `block in instrument'
activesupport (7.0.0) lib/active_support/notifications/instrumenter.rb:24:in `instrument'
activesupport (7.0.0) lib/active_support/notifications.rb:206:in `instrument'
actionpack (7.0.0) lib/action_controller/metal/instrumentation.rb:66:in `process_action'
actionpack (7.0.0) lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
activerecord (7.0.0) lib/active_record/railties/controller_runtime.rb:27:in `process_action'
actionpack (7.0.0) lib/abstract_controller/base.rb:151:in `process'
actionview (7.0.0) lib/action_view/rendering.rb:39:in `process'
actionpack (7.0.0) lib/action_controller/metal/live.rb:266:in `block (2 levels) in process'
activesupport (7.0.0) lib/active_support/concurrency/share_lock.rb:162:in `sharing'
activesupport (7.0.0) lib/active_support/dependencies/interlock.rb:37:in `running'
actionpack (7.0.0) lib/action_controller/metal/live.rb:258:in `block in process'
actionpack (7.0.0) lib/action_controller/metal/live.rb:343:in `block in new_controller_thread'
Request parameters
{"blob"=>{"filename"=>"IMG_0700.JPG",
"content_type"=>"image/jpeg",
"byte_size"=>1970730,
"checksum"=>"il85E0GriR/6BKnCtaDmYg=="},
"direct_upload_token"=>"[FILTERED]",
"direct_upload"=>{"blob"=>{"filename"=>"IMG_0700.JPG",
"content_type"=>"image/jpeg",
"byte_size"=>1970730,
"checksum"=>"il85E0GriR/6BKnCtaDmYg=="},
"direct_upload_token"=>"[FILTERED]"}}
Session dump
_csrf_token: "-x38kEGvAajnAo6YlDIZsaOjPm1ZpiunN8w-Rn44mQ4"
_direct_upload_token: "UwtB4BnCZH21GN2wJM7fcGcDo7L24MqZimRO8v6WlMY="
session_id: "45f12a183d6afbdf33194251dbac81f8"
warden.user.user.key: [[1], "$2a$12$1WIydVKfCbd1zDKN0WoEqe"]
Env dump
GATEWAY_INTERFACE: "CGI/1.2"
HTTP_ACCEPT: "application/json"
HTTP_ACCEPT_ENCODING: "gzip, deflate"
HTTP_ACCEPT_LANGUAGE: "en-US,en;q=0.5"
HTTP_ORIGIN: "http://localhost:3000"
HTTP_VERSION: "HTTP/1.1"
HTTP_X_CSRF_TOKEN: "6o-PbMbzQwHxI3pPpUWXXvSCIvjSsR3NEbZhqlowWiZPi5oNF3oBKc-1h-xkRM_yidedFVp22Xf0NvJ66Q7D3g"
ORIGINAL_SCRIPT_NAME: ""
REMOTE_ADDR: "127.0.0.1"
SERVER_NAME: "localhost"
SERVER_PROTOCOL: "HTTP/1.1"
Response headers
None
Thank you
Expected behavior
The direct upload controller should accept the token return 200 and upload the image
Actual behavior
The direct upload does not accept the controller and return 500
System configuration
Rails version: 7.0.0
Ruby version: 3.0.3
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 15 (7 by maintainers)
Hello again, So now I understand why it wasn’t working.
Two points I missed:
Both the attachment name and direct upload token can be found in (as @brenogazzola mentioned):
This will output something like:
From then on, you can use activestorage direct upload with the proper params:
Thank you all for your help
Sure.
Ok, so here’s how the new direct upload workflow works on v7.0.
Model:
Form:
Notice that your
form
needs a model and thefile_field
attribute has to match the an attachment name in the model. This will generate the following:Notice that the file field has both
data-direct-upload-token
anddata-direct-upload-attachment-name
. When you submit the form, you will do:So basically, the javascript constructor changed to have two more attributes, which are the upload token and the attachment name. Both are generated automatically by
field_field
helper, as long as you have a valid model and use a valid attachment as the atribute.