@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 +93 -13
- package/dist/index.mjs +93 -13
- package/package.json +1 -1
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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