brendanhay / amazonka

A comprehensive Amazon Web Services SDK for Haskell.
https://amazonka.brendanhay.nz
Other
599 stars 227 forks source link

Presigned POST request with policy #813

Closed farzadbekran closed 2 years ago

farzadbekran commented 2 years ago

I was wondering if it is possible to presign a POST request like described here?

The reason I'm trying to use a POST method is that I need to set some policy regarding the content type and content length range, which is only possible in the POST method.

I tried to do it by modifying the request before it is signed like this (changing method to POST and adding a policy inside form data):

presignWithHeaders' :: ( MonadIO m, AWSRequest a )
  => ([ Header ] -> [ Header ]) -- ^ Modify the default headers.
  -> (Service -> Service) -- ^ Modify the default service configuration.
  -> Auth -> Region -> UTCTime              -- ^ Signing time.
  -> Seconds              -- ^ Expiry time.
  -> a                    -- ^ Request to presign.
  -> m ClientRequest
presignWithHeaders' f g a r ts ex x = do
  formData <- liftIO getFormData -- this part uses tools from Network.HTTP.Client.MultipartFormData to make a valid multipart body
  withAuth a $ \ae -> return
    $! sgRequest
    $ rqPresign ex (request x
                    & rqHeaders %~ f
                    & rqService %~ g
                    & rqMethod .~ POST
                    & rqBody .~ toBody formData) ae r ts

but this does not seem to lead to anything as the server complains about a missing aws2 access key when I include the policy field, but if I don't include it, I can use it to upload files but without any policy.

farzadbekran commented 2 years ago

OK so the process of signing a post request is a bit different, 'StringToSign' is the Base64 encoding of the policy you want to set, which can be done with a slightly different version of the 'signature' function in the library. something like this:

signature :: UTCTime -> SecretKey -> ByteString -> ByteString
signature t k = digestToBase Base16 . hmacSHA256 signingKey
  where
    signingKey = Fold.foldl' hmac ("AWS4" <> toBS k)
      [ toBS (Time t :: BasicTime), "default", "s3", "aws4_request" ]

    hmac x y = digestToBS (hmacSHA256 x y)

integrating this into the library is a different story though, I'm not sure if it would be possible with the way things are currently setup. My guess is it will require setting up a new service altogether, but for simple use cases this will suffice.