shrinerb / shrine

File Attachment toolkit for Ruby applications
https://shrinerb.com
MIT License
3.18k stars 275 forks source link

SignatureDoesNotMatch error when uploading to DigitalOcean Spaces with presigned URL #676

Closed flyingboy007 closed 8 months ago

flyingboy007 commented 8 months ago

I'm working on a Rails application that uses Shrine to upload files directly to DigitalOcean Spaces using presigned URLs. However, I'm encountering a SignatureDoesNotMatch error during the upload process. I've verified my credentials and ensured that the Content-Type and Content-Disposition headers match between the presigned URL generation and the actual upload request. Despite this, the error persists.

Here's the relevant part of my code for generating the presigned URL:

require 'shrine/storage/s3'

s3_options = {
  endpoint: 'https://blr1.digitaloceanspaces.com',
  bucket: 'test-dev',
  access_key_id: ENV['DO_SPACES_ACCESS_KEY_ID'],
  secret_access_key: ENV['DO_SPACES_SECRET_ACCESS_KEY'],
  region: 'blr1',
}

Shrine.storages = {
  cache: Shrine::Storage::S3.new(prefix: 'cache', **s3_options),
  store: Shrine::Storage::S3.new(prefix: 'store', **s3_options),
}

Shrine.plugin :activerecord
Shrine.plugin :cached_attachment_data
Shrine.plugin :restore_cached_data
Shrine.plugin :backgrounding
Shrine.plugin :derivatives
Shrine.plugin :determine_mime_type
Shrine.plugin :presign_endpoint, presign_options: -> (request) {
  filename = 'invoice.pdf'
  type = request.params['type'] || 'application/pdf'

  {
    content_disposition: ContentDisposition.inline(filename),
    content_type: type,
    method: :put,
  }
}

The presigned URL generated looks like this:

{
    "fields": {},
    "headers": {
        "Content-Type": "application/pdf",
        "Content-Disposition": "inline; filename=\"invoice.pdf\"; filename*=UTF-8''invoice.pdf"
    },
    "method": "put",
    "url": "https://test-dev.blr1.digitaloceanspaces.com/cache/49d38c38478222056e32ea3bfff5c783?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=DO00RZLGPKJ6JWCCTQLD%2F20240301%2Fblr1%2Fs3%2Faws4_request&X-Amz-Date=20240301T024329Z&X-Amz-Expires=900&X-Amz-SignedHeaders=content-disposition%3Bcontent-type%3Bhost&X-Amz-Signature=0814b01e839daa73312c73ce27b3b18b473bef05bdecff426b1d3c50127dd3dc"
}

And the error response from DigitalOcean Spaces is:

<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>SignatureDoesNotMatch</Code>
    <Message></Message>
    <RequestId>tx000001b5e7e7644078f7a-0065e141a8-e2afaf-blr1a</RequestId>
    <HostId>e2afaf-blr1a-blr1-zg01</HostId>
</Error>

Here is the curl from postman

curl --location --request PUT 'https://test-dev.blr1.digitaloceanspaces.com/cache/49d38c38478222056e32ea3bfff5c783?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=DO00RZLGPKJ6JWCCTQLD%2F20240301%2Fblr1%2Fs3%2Faws4_request&X-Amz-Date=20240301T024329Z&X-Amz-Expires=900&X-Amz-SignedHeaders=content-disposition%3Bcontent-type%3Bhost&X-Amz-Signature=0814b01e839daa73312c73ce27b3b18b473bef05bdecff426b1d3c50127dd3dc' \
--header 'Content-Type: application/pdf' \
--header 'Content-Disposition: inline; filename=\"invoice.pdf\"; filename*=UTF-8'\'''\''invoice.pdf' \
--data '@postman-cloud:///1eed75ce-e91a-46f0-9ce6-cac7b50716a0'

Has anyone encountered this issue before or can spot what might be going wrong?

flyingboy007 commented 8 months ago

I switched to using post method for presign generation and uploading and it works