Search code examples
javascriptphpamazon-s3fine-uploader

Fineuploader not setting x-amz-credentials


Preface: I had my fineuploader powered upload site working at the end of 2013 with S3 as storage. Amazon then upgraded authorization mechanism to V4 at many places. So I decided to upgrade my fineuploader.

My problem is that it is not sending x-amz-credential while cors.sendCredentials is true. Because of that the (freshly downloaded and adjusted) PHP server side says undefined variable: credentialcondition. Let's see the configuration and relevant codes:

I configured fineUploader as:

var s3Uploader = new qq.s3.FineUploader({
    element: document.getElementById("fineuploader-s3"),
    debug: true,
    request: {
        endpoint: "anonimized-bucket-name.s3.amazonaws.com",
        accessKey: "AKIAANONIMIZEDG4AIYQ"
    },
    cors: {
        expected: true,
        sendCredentials: true
    },
    template: "qq-simple-thumbnails-template",
    signature: {
        endpoint: "index.php?mode=video&formId=3&hash=28120&v4=true"
    },
    uploadSuccess: {
        endpoint: "index.php?mode=video&formId=3&hash=28120&v4=true&success=1"
    },
    iframeSupport: {
        localBlankPagePath: "/include/blank.html"
    },
    chunking: {
        enabled: true
    },
    resume: {
        enabled: true
    },
    deleteFile: {
        enabled: false,
        method: "POST",
        endpoint: "index.php?mode=video&formId=3&hash=28120&v4=true"
    },
    validation: {
        itemLimit: 1000,
        sizeLimit: 5000111000
    },
    thumbnails: {
        placeholders: {
            notAvailablePath: "fu/assets/not_available-generic.png",
            waitingPath: "fu/assets/waiting-generic.png"
        }
    }
});

The POST request to my PHP server contained this body (line breaks are added to make reading easier):

{"expiration":"2016-12-22T09:59:32.767Z","conditions":[{"acl":"private"},
{"bucket":"anonimized-bucket-name"},{"Content-Type":"application/pdf"},
{"success_action_status":"200"},{"key":"c7e7c9dc-c4d3-4bec-af52-d3ad34c202ad.pdf"},
{"x-amz-meta-qqfilename":"Adatbazisok_2.pdf"},["content-length-range","0","5000111000"]]}

The relevant server side code is (unchanged compared to the reference implementation):

function signV4Policy($stringToSign, $policyObj) {
    global $clientPrivateKey;
    foreach ($policyObj["conditions"] as $condition) {
        if (isset($condition["x-amz-credential"])) {
            $credentialCondition = $condition["x-amz-credential"];
        }
    }
    $pattern = "/.+\/(.+)\\/(.+)\/s3\/aws4_request/";
    preg_match($pattern, $credentialCondition, $matches);
    $dateKey = hash_hmac('sha256', $matches[1], 'AWS4' . $clientPrivateKey, true);
    $dateRegionKey = hash_hmac('sha256', $matches[2], $dateKey, true);
    $dateRegionServiceKey = hash_hmac('sha256', 's3', $dateRegionKey, true);
    $signingKey = hash_hmac('sha256', 'aws4_request', $dateRegionServiceKey, true);
    return hash_hmac('sha256', $stringToSign, $signingKey);
}

The error is at line of preg_match saying undefined variable: cretentialCondition.

Sample policyObj that I have received:

array(2) {
  ["expiration"]=>
  string(24) "2016-12-22T09:59:32.767Z"
  ["conditions"]=>
  array(7) {
    [0]=>
    array(1) {
      ["acl"]=>
      string(7) "private"
    }
    [1]=>
    array(1) {
      ["bucket"]=>
      string(12) "anonimized-bucket-name"
    }
    [2]=>
    array(1) {
      ["Content-Type"]=>
      string(15) "application/pdf"
    }
    [3]=>
    array(1) {
      ["success_action_status"]=>
      string(3) "200"
    }
    [4]=>
    array(1) {
      ["key"]=>
      string(40) "c7e7c9dc-c4d3-4bec-af52-d3ad34c202ad.pdf"
    }
    [5]=>
    array(1) {
      ["x-amz-meta-qqfilename"]=>
      string(17) "Adatbazisok_2.pdf"
    }
    [6]=>
    array(3) {
      [0]=>
      string(20) "content-length-range"
      [1]=>
      string(1) "0"
      [2]=>
      string(10) "5000111000"
    }
  }
}

Versions: php 7, fineuploader 5.11.0 (newest), browser: Win 7 Firefox 50.1.0 (newest)


Solution

  • Well. I am an idiot. Key mistakes:

    • objectProperties.region must be set to the home region of your S3 bucket
    • signature.version must be set to 4 where 4 is 4, not "4".
    • I don't know for sure, but I think you have to set cors.sendCredentials to true.

    This is the manual I have failed to find then failed to follow precisely: https://blog.fineuploader.com/2015/11/16/fine-uploader-5-4-aws-s3-version-4-signature-support/