What's your biggest current challenge? Have your say in Community Polls along the right column.
Our Partner & Developer boards on the community are moving to a brand new home: the .dev community forums! While you can still access past discussions here, for all your future app and storefront building questions, head over to the new forums.

Attaching a PDF / API issues : Missing headers: x-goog-expires

Attaching a PDF / API issues : Missing headers: x-goog-expires

killian-vision
Shopify Partner
1 0 0

Hello.
We've been attempting to make a PDF upload to shopify thanks to the documentation and this thread

The final goal is to attach these PDF files to customer orders later down the line via metafields.
With it, we've succeeded to get the staged upload site, however, any attempt at this point yields errors that we cannot find responses to.

We are currently working strictly on Postman to understand the request flow we seek

Our current progress is:
POST https://{{Environment}}.myshopify.com/admin/api/2024-01/graphql.json (Request Id = 1f80e45d-deb0-4b6f-980a-b2fb58d4a5cf)
Yields 200 with body:

 

{
  "data": {
    "stagedUploadsCreate": {
      "stagedTargets": [
        {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/pdf"
            },
            {
              "name": "success_action_status",
              "value": "201"
            },
            {
              "name": "acl",
              "value": "private"
            },
            {
              "name": "key",
              "value": "tmp/(numbers)/files/(Guid)/F1234.pdf"
            },
            {
              "name": "x-goog-date",
              "value": "20240125T093520Z"
            },
            {
              "name": "x-goog-credential",
              "value": "merchant-assets@shopify-tiers.iam.gserviceaccount.com/20240125/auto/storage/goog4_request"
            },
            {
              "name": "x-goog-algorithm",
              "value": "GOOG4-RSA-SHA256"
            },
            {
              "name": "x-goog-signature",
              "value": "(long string of chars)"
            },
            {
              "name": "policy",
              "value": "eyJjb25kaXRpb25zIjpbeyJDb250ZW50LVR5cGUiOiJhcHBsaWNhdGlvblwvcGRmIn0seyJzdWNjZXNzX2FjdGlvbl9zdGF0dXMiOiIyMDEifSx7ImFjbCI6InByaXZhdGUifSxbImNvbnRlbnQtbGVuZ3RoLXJhbmdlIiwxLDIwOTcxNTIwXSx7ImJ1Y2tldCI6InNob3BpZnktc3RhZ2VkLXVwbG9hZHMifSx7ImtleSI6InRtcFwvNzY1Mzk0OTQ3MDBcL2ZpbGVzXC8yZDM0NzA5Ny00OWM0LTRjMDItYjU0Mi1kN2Q4NDA1OWM0MWZcL0YxMjM0LnBkZiJ9LHsieC1nb29nLWRhdGUiOiIyMDI0MDEyNVQwOTM1MjBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6Im1lcmNoYW50LWFzc2V0c0BzaG9waWZ5LXRpZXJzLmlhbS5nc2VydmljZWFjY291bnQuY29tXC8yMDI0MDEyNVwvYXV0b1wvc3RvcmFnZVwvZ29vZzRfcmVxdWVzdCJ9LHsieC1nb29nLWFsZ29yaXRobSI6IkdPT0c0LVJTQS1TSEEyNTYifV0sImV4cGlyYXRpb24iOiIyMDI0LTAxLTI2VDA5OjM1OjIwWiJ9"
            }
          ],
          "url": "https://shopify-staged-uploads.storage.googleapis.com/",
          "resourceUrl": "https://shopify-staged-uploads.storage.googleapis.com/tmp/(numbers)/files/(Guid)/F1234.pdf"
        }
      ]
    }
  },
  "extensions": {
    "cost": {
      "requestedQueryCost": 11,
      "actualQueryCost": 11,
      "throttleStatus": {
        "maximumAvailable": 2000.0,
        "currentlyAvailable": 1989,
        "restoreRate": 100.0
      }
    }
  }
}

 

However, when it comes to sending the file itself
both
PUT https://shopify-staged-uploads.storage.googleapis.com
and
PUT https://shopify-staged-uploads.storage.googleapis.com/tmp/(numbers)/files/(Guid)/F1234.pdf

with parameters (copied from Postman's bulk edit):

Content-Type:application/pdf
success_action_status:201
acl:private
key:tmp/(numbers)/files/(Guid)/F1234.pdf
x-goog-date:20240125T093520Z
x-goog-credential:merchant-assets@shopify-tiers.iam.gserviceaccount.com/20240125/auto/storage/goog4_request
x-goog-algorithm:GOOG4-RSA-SHA256
x-goog-signature: (long string of chars)
policy:eyJjb25kaXRpb25zIjpbeyJDb250ZW50LVR5cGUiOiJhcHBsaWNhdGlvblwvcGRmIn0seyJzdWNjZXNzX2FjdGlvbl9zdGF0dXMiOiIyMDEifSx7ImFjbCI6InByaXZhdGUifSxbImNvbnRlbnQtbGVuZ3RoLXJhbmdlIiwxLDIwOTcxNTIwXSx7ImJ1Y2tldCI6InNob3BpZnktc3RhZ2VkLXVwbG9hZHMifSx7ImtleSI6InRtcFwvNzY1Mzk0OTQ3MDBcL2ZpbGVzXC8yZDM0NzA5Ny00OWM0LTRjMDItYjU0Mi1kN2Q4NDA1OWM0MWZcL0YxMjM0LnBkZiJ9LHsieC1nb29nLWRhdGUiOiIyMDI0MDEyNVQwOTM1MjBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6Im1lcmNoYW50LWFzc2V0c0BzaG9waWZ5LXRpZXJzLmlhbS5nc2VydmljZWFjY291bnQuY29tXC8yMDI0MDEyNVwvYXV0b1wvc3RvcmFnZVwvZ29vZzRfcmVxdWVzdCJ9LHsieC1nb29nLWFsZ29yaXRobSI6IkdPT0c0LVJTQS1TSEEyNTYifV0sImV4cGlyYXRpb24iOiIyMDI0LTAxLTI2VDA5OjM1OjIwWiJ9

and a PDF file, in binary, as the body
yields 400:

 

 

 

<?xml version='1.0' encoding='UTF-8'?>
<Error>
<Code>MissingSecurityHeader</Code>
<Message>Invalid argument.</Message>
<Details>Your request was missing a required header. Missing headers: x-goog-expires</Details>
</Error>

 

 

 

So, we've attempted to add headers as it requests (x-goog-expires:4800 and x-goog-signedHeaders:host), but we are then met with

 

 

 

<?xml version='1.0' encoding='UTF-8'?>
<Error>
    <Code>SignatureDoesNotMatch</Code>
    <Message>Access denied.</Message>
    <Details>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Details>
    <StringToSign>GOOG4-RSA-SHA256
20240125T093520Z
20240125/auto/storage/goog4_request
dd7020393f65ba8691d0bbbcd3c7caf5b2fc5f29c959e426ad21b9adc057911c</StringToSign>
    <CanonicalRequest>PUT
(the path and params are pasted here)

host
UNSIGNED-PAYLOAD</CanonicalRequest>
</Error>

 

 

 

Any help would be welcome!

Replies 2 (2)

Liam
Community Manager
3108 344 895

Hi Killian- the MissingSecurityHeader error suggests that the required x-goog-expires header was not included in your PUT request. This header is necessary for Google Cloud Storage to know how long the signed URL should be valid. You mentioned adding x-goog-expires:4800, which should theoretically solve this issue. Ensure this header is correctly formatted and included in your request. The SignatureDoesNotMatch error is more complex. It indicates that the signature you're providing doesn't match what Google Cloud Storage expects. This is often due to:

  1. Incorrect Signing Method: Make sure you're using the correct method for signing your request. It needs to match with what you've specified in the x-goog-algorithm header.

  2. Incorrect Secret Key: Verify that the secret key you're using is correct and has the necessary permissions.

  3. String To Sign Mismatch: The string you're using to generate the signature might not match what Google Cloud expects. Double-check that you're including all the necessary components in the correct order: HTTP method, URL path, query parameters, headers, and payload hash.

  4. Time Sync Issue: Ensure that your system's clock is synchronized with NTP. A time mismatch can lead to signature mismatches.

Hope this helps,

Liam | Developer Advocate @ Shopify 
 - Was my reply helpful? Click Like to let me know! 
 - Was your question answered? Mark it as an Accepted Solution
 - To learn more visit Shopify.dev or the Shopify Web Design and Development Blog

dugudlabs
Shopify Partner
1 0 0

Hi, 
We are trying to upload a file to store. But Response of stagedUploadsCreate only keys 2 parameters as content_type and acl. NO google headers.
And the url given in response , wehn we try it , gives 403 Forbidden.

Below is our grpahql request:

mutation generateStagedUploads {
  stagedUploadsCreate(input: [
    {
      filename: "tryon.png",
      mimeType: "image/png",
      resource: IMAGE
    }
  ])
    {
    stagedTargets {
      url
      resourceUrl
      parameters {
        name
        value
      }
    }
    userErrors {
      field, message
    }
  }
}