Cloud Storage Client-Side Javascript File Upload 403 SignatureDoesNotMatch

2,965 views
Skip to first unread message

Scott Campbell

unread,
Nov 19, 2017, 2:06:17 PM11/19/17
to Google App Engine
I am trying to upload audio files on the Client-Side directly to my Google Cloud Storage bucket, for the purpose of avoiding a server-side upload (which has file size limits).

My Issue: I am getting a 403  SignatureDoesNotMatch  error on upload. 

Here is the error from the response:

  1. <Error>
    1. <Code>SignatureDoesNotMatch</Code>
    2. <Message>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Message>
    3. <StringToSign>PUT audio/mp3 1511112552 /bucketname/pathtofile/019%20-%20top%20cntndr%20V1.mp3</StringToSign>
    4. </Error>


I have created a signed url. It looks like this:

https://storage.googleapis.com/google-testbucket/testdata.txt?GoogleAccessId=
123456...@developer.gserviceaccount.com&Expires=1331155464&Signature=BCl
z9e4UA2MRRDX62TPd8sNpUCxVsqUDG3YGPWvPcwN%2BmWBPqwgUYcOSszCPlgWREeF7oPGowkeKk
7J4WApzkzxERdOQmAdrvshKSzUHg8Jqp1lw9tbiJfE2ExdOOIoJVmGLoDeAGnfzCd4fTsWcLbal9
sFpqXsQI8IQi1493mw%3D

The signed url is built following the guidlines found in the Google Docs here https://cloud.google.com/storage/docs/access-control/create-signed-urls-program

However, the client-side javascript portion of handling this signed url is very unclear in the documentation.




Here is my python code to create and return the signed url.


GOOGLE_SERVICE_CREDENTIALS
= 'google-service-credentials.json'


def get_signed_url(request):
    filename
= request.GET.get('filename')
    expiration
= request.GET.get('expiration')
    type
= request.GET.get('type')
    signed_url
= CloudStorageSignedURL(
                method
='PUT',
                file_name
=filename,
                expiration_m
=expiration,
                content_type
=type
               
)
    signed_url
= signed_url.sign_url()


   
return JsonResponse({ 'signed_url': signed_url })






class CloudStorageSignedURL(object):


   
def __init__(self, method, file_name, expiration_m, content_type):
       
self.HTTP_method = method
       
self.content_type = 'content-type: ' + content_type
       
self.expiration = int(expiration_m)
       
self.file_name = file_name


   
def sign_url(self):


        expiration_dt
= datetime.utcnow() + timedelta(minutes=self.expiration)
        expiration
= int(time.mktime( expiration_dt.timetuple() ))
        bucket_path
= '/' + settings.CLOUD_STORAGE_BUCKET + '/dev/tests/' + self.file_name
        signature_string
= self.HTTP_method + '\n' + '\n' + self.content_type + "\n" + str(expiration) + '\n' + bucket_path
       
print(signature_string)
        creds
= ServiceAccountCredentials.from_json_keyfile_name(GOOGLE_SERVICE_CREDENTIALS)
        client_email
= creds.service_account_email
        signature
= creds.sign_blob(signature_string)[1]
        encoded_signature
= base64.urlsafe_b64encode(signature).decode('utf-8')
        base_url
= settings.CLOUD_STORAGE_ROOT + 'dev/tests/' + self.file_name


       
return base_url + '?GoogleAccessId=' + client_email + '&Expires=' + str(expiration) + '&Signature=' + encoded_signature






Client-side Javascript to upload the file

import $ from 'jquery';
import axios from 'axios';



$
("document").ready( () => {
  console
.log('window loaded');


  $
("#id_audio_file").change(function() {

   
const file = this.files[0]

   
const url = window.location.href.replace('submit/', 'upload/');
    $
.get(url + `?filename=${file.name}&expiration=10&type=${file.type}`, (data) => {
      upload
(data.signed_url, file);
   
})

 
});
});



function upload(url, file) {

 
const config = {
    headers
: {
     
'Content-Type': file.type,
   
}
 
}

  axios
.put(url, file, config)
   
.then(function (res) {
      console
.log(res);
   
})
   
.catch(function (err) {
      console
.log(err);
   
});
}




I really feel like I covered all the bases here, but I am obviously missing something minute.  Any help would be greatly appreciated!

Yannick (Cloud Platform Support)

unread,
Nov 20, 2017, 10:18:10 AM11/20/17
to Google App Engine
Hello Scott, the "StringToSign" field tells you what it expects the canonical string to look like. You should double-check that the string your client uses is exactly the same (including any whitespace).

For this kind of technical question I also recommend that you post it to Stack Overflow using one of the tags monitored by our community technical support team.


On Sunday, November 19, 2017 at 2:06:17 PM UTC-5, Scott Campbell wrote:
I am trying to upload audio files on the Client-Side directly to my Google Cloud Storage bucket, for the purpose of avoiding a server-side upload (which has file size limits).

My Issue: I am getting a 403  SignatureDoesNotMatch  error on upload. 

Here is the error from the response:

  1. <Error>
    1. <Code>SignatureDoesNotMatch</Code>
    2. <Message>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Message>
    3. <StringToSign>PUT audio/mp3 1511112552 /bucketname/pathtofile/019%20-%20top%20cntndr%20V1.mp3</StringToSign>
    4. </Error>


I have created a signed url. It looks like this:

https://storage.googleapis.com/google-testbucket/testdata.txt?GoogleAccessId=
1234567890123@developer.gserviceaccount.com&Expires=1331155464&Signature=BCl
z9e4UA2MRRDX62TPd8sNpUCxVsqUDG3YGPWvPcwN%2BmWBPqwgUYcOSszCPlgWREeF7oPGowkeKk
7J4WApzkzxERdOQmAdrvshKSzUHg8Jqp1lw9tbiJfE2ExdOOIoJVmGLoDeAGnfzCd4fTsWcLbal9
sFpqXsQI8IQi1493mw%3D

Scott Campbell

unread,
Nov 20, 2017, 3:02:08 PM11/20/17
to Google App Engine
Yannick!  Thanks again for your support, I do appreciate it.  This will help me debug the issue now that I know where to look.  I will link my stack-overflow question.  




On Monday, November 20, 2017 at 10:18:10 AM UTC-5, Yannick (Cloud Platform Support) wrote:
Hello Scott, the "StringToSign" field tells you what it expects the canonical string to look like. You should double-check that the string your client uses is exactly the same (including any whitespace).

For this kind of technical question I also recommend that you post it to Stack Overflow using one of the tags monitored by our community technical support team.

On Sunday, November 19, 2017 at 2:06:17 PM UTC-5, Scott Campbell wrote:
I am trying to upload audio files on the Client-Side directly to my Google Cloud Storage bucket, for the purpose of avoiding a server-side upload (which has file size limits).

My Issue: I am getting a 403  SignatureDoesNotMatch  error on upload. 

Here is the error from the response:

  1. <Error>
    1. <Code>SignatureDoesNotMatch</Code>
    2. <Message>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Message>
    3. <StringToSign>PUT audio/mp3 1511112552 /bucketname/pathtofile/019%20-%20top%20cntndr%20V1.mp3</StringToSign>
    4. </Error>


I have created a signed url. It looks like this:

https://storage.googleapis.com/google-testbucket/testdata.txt?GoogleAccessId=
123456...@developer.gserviceaccount.com&Expires=1331155464&Signature=BCl
z9e4UA2MRRDX62TPd8sNpUCxVsqUDG3YGPWvPcwN%2BmWBPqwgUYcOSszCPlgWREeF7oPGowkeKk
7J4WApzkzxERdOQmAdrvshKSzUHg8Jqp1lw9tbiJfE2ExdOOIoJVmGLoDeAGnfzCd4fTsWcLbal9
sFpqXsQI8IQi1493mw%3D

Steren Giannini

unread,
Nov 20, 2017, 9:08:25 PM11/20/17
to Google App Engine
When it comes to storing items in Cloud Storage from client side web, I recommend looking at the web SDK of Cloud Storage for Firebase: https://firebase.google.com/docs/storage/web/start
Reply all
Reply to author
Forward
0 new messages