Creating signed URLs for Google Cloud Storage using NodeJS

google cloud storage signed url javascript
google cloud storage signed url python
google cloud storage upload from url
google cloud storage public url
google cloud storage nodejs upload file
google cloud storage - npm
google cloud storage private url
generate_signed_url

I'm trying to create a signature for a privately stored file in Google Cloud Storage; so that I can distribute a time-limited link.

Currently doing this and it makes a signature that's too short ... where am I going wrong?

var crypto = require("crypto");

var ttl = new Date().getTime() + 3600;
var id = 'the_target_file.txt';
var bucketName = 'bucket_name';
var POLICY_JSON = "GET\n" + "\n" + "\n" + ttl + "\n" + '/' + bucketName + '/' + id;

// stringify and encode the policy
var stringPolicy = JSON.stringify(POLICY_JSON);
var base64Policy = Buffer(stringPolicy, "utf-8").toString("base64");

// sign the base64 encoded policy
var privateKey = "MY_PRIVATE_KEY";
var sha256 = crypto.createHmac("sha256", privateKey);
var signature = sha256.update(new Buffer(base64Policy, "utf-8")).digest("base64");

console.log ( signature );

Realised what I was doing wrong ... I was hashing the policy string instead of signing it. The below code now gives me the correct output.

var crypto = require("crypto");
var fs = require("fs");

var expiry = new Date().getTime() + 3600;
var key = 'the_target_file';
var bucketName = 'bucket_name';
var accessId = 'my_access_id';
var stringPolicy = "GET\n" + "\n" + "\n" + expiry + "\n" + '/' + bucketName + '/' + key; 
var privateKey = fs.readFileSync("gcs.pem","utf8");
var signature = encodeURIComponent(crypto.createSign('sha256').update(stringPolicy).sign(privateKey,"base64"));   
var signedUrl = "https://" + bucketName + ".commondatastorage.googleapis.com/" + key +"?GoogleAccessId=" + accessId + "&Expires=" + expiry + "&Signature=" + signature;

console.log(signedUrl);

For completeness ... here is a PHP version that does the same thing, which I used to check my results

$expiry = time() + 3600;
$key = 'the_target_file';
$bucketName = 'bucket_name';
$accessId = 'my_access_id';
$stringPolicy = "GET\n\n\n".$expiry."\n/".$bucketName."/".$key;
$fp = fopen('gcs.pem', 'r');
$priv_key = fread($fp, 8192);
fclose($fp);
$pkeyid = openssl_get_privatekey($priv_key,"password"); 
if (openssl_sign( $stringPolicy, $signature, $pkeyid, 'sha256' )) {
    $signature = urlencode( base64_encode( $signature ) );    
    echo 'https://'.$bucketName.'.commondatastorage.googleapis.com/'.
              $key.'?GoogleAccessId='.$accessId.'&Expires='.$expiry.'&Signature='.$signature;
}

Creating signed URLs for Google Cloud Storage using NodeJS , gcloud-signed-url.js Google Cloud Storage Bucket Name with Google Developer Console) stored in gcloud storage using node.js. Simply specify Cloud Storage resources, point to the host storage.googleapis.com, and use Google HMAC credentials in the process of generating the signed URL. Signed URL example The following is an example of a signed URL that was created following the V4 signing process with service account authentication:

There is an API/module for getting signed URLs now.

module: https://www.npmjs.com/package/@google-cloud/storage API docs: https://cloud.google.com/nodejs/docs/reference/storage/1.6.x/File#getSignedUrl

Example

var storage = require('@google-cloud/storage')();
var myBucket = storage.bucket('my-bucket');

var file = myBucket.file('my-file');

//-
// Generate a URL that allows temporary access to download your file.
//-
var request = require('request');

var config = {
  action: 'read',
  expires: '03-17-2025'
};

file.getSignedUrl(config, function(err, url) {
  if (err) {
    console.error(err);
    return;
  }

  // The file is now available to read from this URL.
  request(url, function(err, resp) {
    // resp.statusCode = 200
  });
});

Example on how to create a signed URL using Node.js for , Imports the Google Cloud client library const Storage In a Firebase function, using GCS node js API ver 1.7.0, uploading an image to I created a new service account and then generated a signed URL for a GET request. Service for creating and managing Google Cloud resources. Note: HMAC credentials are not supported when using Cloud Storage tools to generate signed URLs.

Assuming this question is to sign the CDN url backed by google bucket backend, here what works for me (code above did not work for me).

Opts and signing function calling:

const signUrlOptions = {
  expires: '' + new Date().getTime() + 3600, // one hour
  keyName: '_SIGNING_KEY_NAME_', // URL signing key name (the one one you created in the CDN backend bucket)
  keyBase64: '_SIGNING_KEY_BASE64_', // the URL signing key base64 content (base64-encoded, 128-bit value, ~24 characters)
  baseUrl: '_CDN_BASE_URL_' // your base CDN URL (can be IP http://123.... when dev env or https://cdn_dns_name or https dns name)
}

const signedUrl = signCdnUrl('demo.png', signedUrlOptions);

signing function:

import { createHmac } from 'crypto';

const BASE64_REPLACE = { '+': '-', '/': '_', '=': '' };

export function signCdnUrl(fileName, opts) {
  // URL to sign
  const urlToSign = `${opts.baseUrl}/${fileName}?Expires=${opts.expires}&KeyName=${opts.keyName}`;

  // Compute signature
  const keyBuffer = Buffer.from(opts.keyBase64, 'base64');
  let signature = createHmac('sha1', keyBuffer).update(urlToSign).digest('base64');
  signature = signature.replace(/[+/=]/g, c => (<any>BASE64_REPLACE)[c]); // might be a better way

  // Add signature to urlToSign and return signedUrl
  return urlToSign + `&Signature=${signature}`;
}

Hope this helps. Somehow google cloud doc does not have a nodejs example and the file.getSignedUrl() add confusion to the mix as it does not seem to be related to CDN URL signing.

Note:

Note: Probably want to move base64 -> buffer work to the caller as opts.keyBuffer

getSignedURL() in Google Cloud Function produces link that works , Create a new helper file which calls the constructor. helpers/google-cloud- storage.js. const GoogleCloudStorage = require(  This page shows you how to create Cloud Storage buckets. For an overview of buckets, read the Key Terms.If not otherwise specified in your request, buckets are created in the US multi-region and have a default storage class of Standard Storage.

Node.js, Node.js idiomatic client for Cloud Storage. You can use Google Cloud Storage for a range of scenarios including serving website content, storing data for archival and Generate V4 Upload Signed Url, source code · Open in Cloud Shell. Hello, I have the exact same problem (signed URLs expire after some time, even though 'expires' property is set to a date far in the future). This is the relevant code (initialization is slightly different than the example @colinjstief provided):

Google Cloud Storage: Node.js Client, Allow uploads directly from the browser to Google Cloud Storage while controlling access with Nodejs and signed urls. Describes options for uploading objects to a Cloud Storage bucket. An object consists of the data you want to store along with any associated metadata. You can upload objects using the supplied code and API samples.

gcs-signed-urls, I want to implement signed URL for resumable upload in GCS, But the problem are only uploading resources (writing) to an access-controlled bucket, you can use the Is it possible to obtain this token in the Lambda function using Node.js? To ensure a smooth transition, we are slowly updating App Engine to use region IDs. If we haven't updated your Google Cloud project yet, you won't see a region ID for your app. Since the ID is optional for existing apps, you don't need to update URLs or make other changes once the region ID is available for your existing apps.

Comments
  • how to reduce expiry time.
  • Man, this is why I still come to SO. ⭐️
  • what is var accessId = 'my_access_id'; ?
  • Great example! One small note - in php 3600 is seconds, while in js 3600 is in ms, as javascript's getTime() returns milliseconds.
  • I noticed there's no secretAccessKey here, I assumed it is not needed here anymore?
  • This seems to only create the link for the bucket access, but not for the CDN. Adding the cname option for the CDN still gives no permission to the service account.
  • This looks like a really handy abstraction of the previously convoluted requirements. What is being used to sign the URLs in this case?