@prmichaelsen/firebase-admin-sdk-v8 2.0.0 → 2.0.2

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/dist/index.js CHANGED
@@ -82,6 +82,94 @@ function getProjectId() {
82
82
  return projectId;
83
83
  }
84
84
 
85
+ // src/x509.ts
86
+ function decodeBase64(str) {
87
+ const binary = atob(str);
88
+ const bytes = new Uint8Array(binary.length);
89
+ for (let i = 0; i < binary.length; i++) {
90
+ bytes[i] = binary.charCodeAt(i);
91
+ }
92
+ return bytes;
93
+ }
94
+ function parseElement(bytes) {
95
+ let position = 0;
96
+ let tag = bytes[0] & 31;
97
+ position++;
98
+ if (tag === 31) {
99
+ tag = 0;
100
+ while (bytes[position] >= 128) {
101
+ tag = tag * 128 + bytes[position] - 128;
102
+ position++;
103
+ }
104
+ tag = tag * 128 + bytes[position] - 128;
105
+ position++;
106
+ }
107
+ let length = 0;
108
+ if (bytes[position] < 128) {
109
+ length = bytes[position];
110
+ position++;
111
+ } else if (bytes[position] === 128) {
112
+ length = 0;
113
+ while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) {
114
+ if (length > bytes.byteLength) {
115
+ throw new TypeError("Invalid indefinite form length");
116
+ }
117
+ length++;
118
+ }
119
+ const byteLength2 = position + length + 2;
120
+ return {
121
+ byteLength: byteLength2,
122
+ contents: bytes.subarray(position, position + length),
123
+ raw: bytes.subarray(0, byteLength2)
124
+ };
125
+ } else {
126
+ const numberOfDigits = bytes[position] & 127;
127
+ position++;
128
+ length = 0;
129
+ for (let i = 0; i < numberOfDigits; i++) {
130
+ length = length * 256 + bytes[position];
131
+ position++;
132
+ }
133
+ }
134
+ const byteLength = position + length;
135
+ return {
136
+ byteLength,
137
+ contents: bytes.subarray(position, byteLength),
138
+ raw: bytes.subarray(0, byteLength)
139
+ };
140
+ }
141
+ function getElement(seq) {
142
+ const result = [];
143
+ let next = 0;
144
+ while (next < seq.length) {
145
+ const nextPart = parseElement(seq.subarray(next));
146
+ result.push(nextPart);
147
+ next += nextPart.byteLength;
148
+ }
149
+ return result;
150
+ }
151
+ async function spkiFromX509(buf) {
152
+ const tbsCertificate = getElement(
153
+ getElement(parseElement(buf).contents)[0].contents
154
+ );
155
+ const spki = tbsCertificate[tbsCertificate[0].raw[0] === 160 ? 6 : 5].raw;
156
+ return await crypto.subtle.importKey(
157
+ "spki",
158
+ spki,
159
+ {
160
+ name: "RSASSA-PKCS1-v1_5",
161
+ hash: "SHA-256"
162
+ },
163
+ true,
164
+ ["verify"]
165
+ );
166
+ }
167
+ async function importPublicKeyFromX509(pem) {
168
+ const base64 = pem.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, "");
169
+ const raw = decodeBase64(base64);
170
+ return await spkiFromX509(raw);
171
+ }
172
+
85
173
  // src/auth.ts
86
174
  var publicKeysCache = null;
87
175
  var publicKeysCacheExpiry = 0;
@@ -115,17 +203,6 @@ function parseJWT(token) {
115
203
  const payload = JSON.parse(base64UrlDecode(parts[1]));
116
204
  return { header, payload };
117
205
  }
118
- async function importPublicKey(pem) {
119
- const pemContents = pem.replace("-----BEGIN CERTIFICATE-----", "").replace("-----END CERTIFICATE-----", "").replace(/\s/g, "");
120
- const binaryDer = Uint8Array.from(atob(pemContents), (c) => c.charCodeAt(0));
121
- return await crypto.subtle.importKey(
122
- "spki",
123
- binaryDer,
124
- { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
125
- false,
126
- ["verify"]
127
- );
128
- }
129
206
  async function verifySignature(token, publicKey) {
130
207
  const parts = token.split(".");
131
208
  const signedData = `${parts[0]}.${parts[1]}`;
@@ -183,9 +260,12 @@ async function verifyIdToken(idToken) {
183
260
  const publicKeys = await fetchPublicKeys();
184
261
  const publicKeyPem = publicKeys[header.kid];
185
262
  if (!publicKeyPem) {
186
- throw new Error("Public key not found for kid: " + header.kid);
263
+ const availableKids = Object.keys(publicKeys).join(", ");
264
+ throw new Error(
265
+ `Public key not found for kid: ${header.kid}. Available kids: ${availableKids}. This might indicate a key rotation issue or the token is from a different Firebase project.`
266
+ );
187
267
  }
188
- const publicKey = await importPublicKey(publicKeyPem);
268
+ const publicKey = await importPublicKeyFromX509(publicKeyPem);
189
269
  const isValid = await verifySignature(idToken, publicKey);
190
270
  if (!isValid) {
191
271
  throw new Error("Invalid token signature");
package/dist/index.mjs CHANGED
@@ -42,6 +42,94 @@ function getProjectId() {
42
42
  return projectId;
43
43
  }
44
44
 
45
+ // src/x509.ts
46
+ function decodeBase64(str) {
47
+ const binary = atob(str);
48
+ const bytes = new Uint8Array(binary.length);
49
+ for (let i = 0; i < binary.length; i++) {
50
+ bytes[i] = binary.charCodeAt(i);
51
+ }
52
+ return bytes;
53
+ }
54
+ function parseElement(bytes) {
55
+ let position = 0;
56
+ let tag = bytes[0] & 31;
57
+ position++;
58
+ if (tag === 31) {
59
+ tag = 0;
60
+ while (bytes[position] >= 128) {
61
+ tag = tag * 128 + bytes[position] - 128;
62
+ position++;
63
+ }
64
+ tag = tag * 128 + bytes[position] - 128;
65
+ position++;
66
+ }
67
+ let length = 0;
68
+ if (bytes[position] < 128) {
69
+ length = bytes[position];
70
+ position++;
71
+ } else if (bytes[position] === 128) {
72
+ length = 0;
73
+ while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) {
74
+ if (length > bytes.byteLength) {
75
+ throw new TypeError("Invalid indefinite form length");
76
+ }
77
+ length++;
78
+ }
79
+ const byteLength2 = position + length + 2;
80
+ return {
81
+ byteLength: byteLength2,
82
+ contents: bytes.subarray(position, position + length),
83
+ raw: bytes.subarray(0, byteLength2)
84
+ };
85
+ } else {
86
+ const numberOfDigits = bytes[position] & 127;
87
+ position++;
88
+ length = 0;
89
+ for (let i = 0; i < numberOfDigits; i++) {
90
+ length = length * 256 + bytes[position];
91
+ position++;
92
+ }
93
+ }
94
+ const byteLength = position + length;
95
+ return {
96
+ byteLength,
97
+ contents: bytes.subarray(position, byteLength),
98
+ raw: bytes.subarray(0, byteLength)
99
+ };
100
+ }
101
+ function getElement(seq) {
102
+ const result = [];
103
+ let next = 0;
104
+ while (next < seq.length) {
105
+ const nextPart = parseElement(seq.subarray(next));
106
+ result.push(nextPart);
107
+ next += nextPart.byteLength;
108
+ }
109
+ return result;
110
+ }
111
+ async function spkiFromX509(buf) {
112
+ const tbsCertificate = getElement(
113
+ getElement(parseElement(buf).contents)[0].contents
114
+ );
115
+ const spki = tbsCertificate[tbsCertificate[0].raw[0] === 160 ? 6 : 5].raw;
116
+ return await crypto.subtle.importKey(
117
+ "spki",
118
+ spki,
119
+ {
120
+ name: "RSASSA-PKCS1-v1_5",
121
+ hash: "SHA-256"
122
+ },
123
+ true,
124
+ ["verify"]
125
+ );
126
+ }
127
+ async function importPublicKeyFromX509(pem) {
128
+ const base64 = pem.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, "");
129
+ const raw = decodeBase64(base64);
130
+ return await spkiFromX509(raw);
131
+ }
132
+
45
133
  // src/auth.ts
46
134
  var publicKeysCache = null;
47
135
  var publicKeysCacheExpiry = 0;
@@ -75,17 +163,6 @@ function parseJWT(token) {
75
163
  const payload = JSON.parse(base64UrlDecode(parts[1]));
76
164
  return { header, payload };
77
165
  }
78
- async function importPublicKey(pem) {
79
- const pemContents = pem.replace("-----BEGIN CERTIFICATE-----", "").replace("-----END CERTIFICATE-----", "").replace(/\s/g, "");
80
- const binaryDer = Uint8Array.from(atob(pemContents), (c) => c.charCodeAt(0));
81
- return await crypto.subtle.importKey(
82
- "spki",
83
- binaryDer,
84
- { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
85
- false,
86
- ["verify"]
87
- );
88
- }
89
166
  async function verifySignature(token, publicKey) {
90
167
  const parts = token.split(".");
91
168
  const signedData = `${parts[0]}.${parts[1]}`;
@@ -143,9 +220,12 @@ async function verifyIdToken(idToken) {
143
220
  const publicKeys = await fetchPublicKeys();
144
221
  const publicKeyPem = publicKeys[header.kid];
145
222
  if (!publicKeyPem) {
146
- throw new Error("Public key not found for kid: " + header.kid);
223
+ const availableKids = Object.keys(publicKeys).join(", ");
224
+ throw new Error(
225
+ `Public key not found for kid: ${header.kid}. Available kids: ${availableKids}. This might indicate a key rotation issue or the token is from a different Firebase project.`
226
+ );
147
227
  }
148
- const publicKey = await importPublicKey(publicKeyPem);
228
+ const publicKey = await importPublicKeyFromX509(publicKeyPem);
149
229
  const isValid = await verifySignature(idToken, publicKey);
150
230
  if (!isValid) {
151
231
  throw new Error("Invalid token signature");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/firebase-admin-sdk-v8",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
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",