@prmichaelsen/firebase-admin-sdk-v8 2.3.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.3.1] - 2026-02-14
11
+
12
+ ### Fixed
13
+ - **CRITICAL**: Fixed signed URL generation to match Google Cloud Storage SDK encoding
14
+ - Implemented `fixedEncodeURIComponent` to additionally encode `! * ' ( )` characters
15
+ - This fixes `SignatureDoesNotMatch` errors in production environments
16
+ - Path encoding now exactly matches official `@google-cloud/storage` SDK behavior
17
+
10
18
  ## [2.3.0] - 2026-02-14
11
19
 
12
20
  ### Added
package/dist/index.js CHANGED
@@ -1372,7 +1372,7 @@ function getStorageBucket() {
1372
1372
  return customBucket;
1373
1373
  }
1374
1374
  const projectId = getProjectId();
1375
- return `${projectId}.appspot.com`;
1375
+ return `${projectId}.firebasestorage.app`;
1376
1376
  }
1377
1377
  function getExpirationTimestamp(expires) {
1378
1378
  if (expires instanceof Date) {
@@ -1390,10 +1390,18 @@ function actionToMethod(action) {
1390
1390
  return "DELETE";
1391
1391
  }
1392
1392
  }
1393
- function stringToHex(str) {
1393
+ function fixedEncodeURIComponent(str) {
1394
+ return encodeURIComponent(str).replace(
1395
+ /[!'()*]/g,
1396
+ (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase()
1397
+ );
1398
+ }
1399
+ async function sha256Hex(str) {
1394
1400
  const encoder = new TextEncoder();
1395
- const bytes = encoder.encode(str);
1396
- return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1401
+ const data = encoder.encode(str);
1402
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
1403
+ const hashArray = new Uint8Array(hashBuffer);
1404
+ return Array.from(hashArray).map((b) => b.toString(16).padStart(2, "0")).join("");
1397
1405
  }
1398
1406
  async function signData(data, privateKey) {
1399
1407
  const pemHeader = "-----BEGIN PRIVATE KEY-----";
@@ -1458,17 +1466,15 @@ async function generateSignedUrl(path, options) {
1458
1466
  }
1459
1467
  const sortedParams = Object.keys(queryParams).sort();
1460
1468
  const canonicalQueryString = sortedParams.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(queryParams[key])}`).join("&");
1461
- const encodedPath = path.split("/").map((segment) => encodeURIComponent(segment)).join("/");
1469
+ const encodedPath = path.split("/").map((segment) => fixedEncodeURIComponent(segment)).join("/");
1462
1470
  const canonicalUri = `/${bucket}/${encodedPath}`;
1463
- const canonicalRequest = [
1464
- method,
1465
- canonicalUri,
1466
- canonicalQueryString,
1467
- canonicalHeaders,
1468
- signedHeaders,
1469
- "UNSIGNED-PAYLOAD"
1470
- ].join("\n");
1471
- const canonicalRequestHash = stringToHex(canonicalRequest);
1471
+ const canonicalRequest = `${method}
1472
+ ${canonicalUri}
1473
+ ${canonicalQueryString}
1474
+ ${canonicalHeaders}
1475
+ ${signedHeaders}
1476
+ UNSIGNED-PAYLOAD`;
1477
+ const canonicalRequestHash = await sha256Hex(canonicalRequest);
1472
1478
  const stringToSign = [
1473
1479
  "GOOG4-RSA-SHA256",
1474
1480
  dateTimeStamp,
package/dist/index.mjs CHANGED
@@ -1316,7 +1316,7 @@ function getStorageBucket() {
1316
1316
  return customBucket;
1317
1317
  }
1318
1318
  const projectId = getProjectId();
1319
- return `${projectId}.appspot.com`;
1319
+ return `${projectId}.firebasestorage.app`;
1320
1320
  }
1321
1321
  function getExpirationTimestamp(expires) {
1322
1322
  if (expires instanceof Date) {
@@ -1334,10 +1334,18 @@ function actionToMethod(action) {
1334
1334
  return "DELETE";
1335
1335
  }
1336
1336
  }
1337
- function stringToHex(str) {
1337
+ function fixedEncodeURIComponent(str) {
1338
+ return encodeURIComponent(str).replace(
1339
+ /[!'()*]/g,
1340
+ (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase()
1341
+ );
1342
+ }
1343
+ async function sha256Hex(str) {
1338
1344
  const encoder = new TextEncoder();
1339
- const bytes = encoder.encode(str);
1340
- return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1345
+ const data = encoder.encode(str);
1346
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
1347
+ const hashArray = new Uint8Array(hashBuffer);
1348
+ return Array.from(hashArray).map((b) => b.toString(16).padStart(2, "0")).join("");
1341
1349
  }
1342
1350
  async function signData(data, privateKey) {
1343
1351
  const pemHeader = "-----BEGIN PRIVATE KEY-----";
@@ -1402,17 +1410,15 @@ async function generateSignedUrl(path, options) {
1402
1410
  }
1403
1411
  const sortedParams = Object.keys(queryParams).sort();
1404
1412
  const canonicalQueryString = sortedParams.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(queryParams[key])}`).join("&");
1405
- const encodedPath = path.split("/").map((segment) => encodeURIComponent(segment)).join("/");
1413
+ const encodedPath = path.split("/").map((segment) => fixedEncodeURIComponent(segment)).join("/");
1406
1414
  const canonicalUri = `/${bucket}/${encodedPath}`;
1407
- const canonicalRequest = [
1408
- method,
1409
- canonicalUri,
1410
- canonicalQueryString,
1411
- canonicalHeaders,
1412
- signedHeaders,
1413
- "UNSIGNED-PAYLOAD"
1414
- ].join("\n");
1415
- const canonicalRequestHash = stringToHex(canonicalRequest);
1415
+ const canonicalRequest = `${method}
1416
+ ${canonicalUri}
1417
+ ${canonicalQueryString}
1418
+ ${canonicalHeaders}
1419
+ ${signedHeaders}
1420
+ UNSIGNED-PAYLOAD`;
1421
+ const canonicalRequestHash = await sha256Hex(canonicalRequest);
1416
1422
  const stringToSign = [
1417
1423
  "GOOG4-RSA-SHA256",
1418
1424
  dateTimeStamp,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/firebase-admin-sdk-v8",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "Firebase Admin SDK for Cloudflare Workers and edge runtimes using REST APIs",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",