Search code examples
node.jsgoogle-cloud-platformgoogle-cloud-cdn

Getting `signed_request_invalid_format` when trying to access GCP bucket content via Cloud CDN


I am using signed cookies, to access some restricted files stored in GCP bucket via Cloud CDN. I am using the below function to sign the cookie:

signCookie(urlPrefix: string, keyName: string, key: string, expires: number) {
    // Base64url encode the url prefix
    const urlPrefixEncoded = Buffer.from(urlPrefix).toString('base64url');

    // Input to be signed
    const input = `URLPrefix=${urlPrefixEncoded}:Expires=${expires}:KeyName=${keyName}`;

    // Create bytes from given key string.
    const keyBytes = Buffer.from(key, 'base64');

    // Use key bytes and crypto.createHmac to produce a base64 encoded signature which is then escaped to be base64url encoded.
    const signature = createHmac('sha1', keyBytes).update(input).digest('base64url');

    // Adding the signature on the end if the cookie value
    const signedValue = `${input}:Signature=${signature}`;

    return signedValue;
  }

The cookie is getting generated and getting sent through to the CDN, but it fails with status 403, and the following message comes up:

Error: Forbidden
Your client does not have permission to get URL ********/sample.pdf from this server.

On checking, the load balancer logs, I get this statusDetails: "signed_request_invalid_format". Unfortunately, this message is not present in the list of error messages listed here. Can anyone help me in pointing out the issue, is the signed cookie invalid? Is there any error in the cookie generation logic?


Solution

  • This answer is for the people using express for their server. The sign cookie function generates the cookie as expected, the issue comes when setting the cookie. When we set the cookie like this:

    response.cookie('Cloud-CDN-Cookie', cookieValue, {
              httpOnly: true,
              expires: new Date(cookieExpiryTime),
    });
    

    The cookie value gets URI encoded, and as a result of this all the reserved characters for the URL like =, get replaced with %3D and so on. This breaks the cookie structure, as declared in the documentation:

    URLPrefix=$BASE64URLECNODEDURLORPREFIX:Expires=$TIMESTAMP:KeyName=$KEYNAME:Signature=$BASE64URLENCODEDHMAC
    

    To fix this pass an extra encode function while setting cookie:

    response.cookie('Cloud-CDN-Cookie', cookieValue, {
              httpOnly: true,
              expires: new Date(cookieExpiryTime),
              encode: val => val
    });
    

    Hopefully, it will save you time.