aws-beam/aws-elixir

Presigning S3 get object URI

ceolinrenato opened this issue · 5 comments

Hey I was trying to use the lib to return a presigned url to access a file from inside a S3 bucket.

I was able to accomplish the task by following this docs https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html#query-string-auth-v4-signing-example

However I couldn't use AWS.Signature.sign_v4_query/6 because it always tries to hash to payload and for requests without a payload or when you don't know which payload will be used you have to include UNSIGNED_PAYLOAD in the canonical URL instead.

Here's how I did it https://gist.github.com/ceolinrenato/cc7f036ef7867c4ccd08ddfd932d1520

I think a very simple fix like #88 could made the AWS.Signature.sign_v4_query/6 work for this use case

We could also update test/aws/signature_test.exs sign_v4_query/6 test, since it seems to be target to the same use case. We need to add data to the query string instead of headers

Working example of how to get presigned url to download a s3 object (using the changes in the PR)

  def signed_uri(reference, ttl) do
    %{
      remote_storage_bucket: remote_storage_bucket,
      access_key_id: access_key_id,
      remote_storage_region: remote_storage_region
    } = aws_config()

    uri =
      URI.parse(
        "https://#{remote_storage_bucket}.s3.#{remote_storage_region}.#{aws_client().endpoint}/#{AWS.Util.encode_uri(reference, true)}"
      )

    now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
    long_date = NaiveDateTime.to_iso8601(now, :basic) <> "Z"
    short_date = Date.to_iso8601(now, :basic)

    query_params = [
      {"X-Amz-Expires", Integer.to_string(ttl)},
      {"X-Amz-Algorithm", "AWS4-HMAC-SHA256"},
      {"X-Amz-Credential",
       "#{access_key_id}/#{short_date}/#{remote_storage_region}/s3/aws4_request"},
      {"X-Amz-SignedHeaders", "host"},
      {"X-Amz-Date", long_date}
    ]

    url = URI.to_string(%{uri | query: URI.encode_query(query_params)})

    signature_tuple =
      %{aws_client() | service: "s3"}
      |> AWS.Signature.sign_v4_query(now, :get, url, [{"Host", uri.host}], nil)
      |> Enum.find(fn {key, _value} -> key == "X-Amz-Signature" end)

    URI.to_string(%{
      uri
      | query: URI.encode_query([signature_tuple | query_params])
    })
  end

Hi @ceolinrenato 👋
Thank you for opening the issue and the PR. Unfortunately we just removed the signature part from this library. The idea is to keep signature features in https://github.com/aws-beam/aws_signature and share that lib with the generated packages (aws-elixir and aws-erlang).
I think we can support this feature in that lib too.

I'm closing the issue and the PR, but I have opened a new issue there: aws-beam/aws_signature#4
Thanks again!

Thanks for maintaining this lib @philss, Wish I knew some erlang to help on the aws-beam/aws_signature side