diff --git a/package-lock.json b/package-lock.json index 61a3551..1ad601b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,21 +8,23 @@ "name": "notification-wakeup-service", "version": "0.1.0", "dependencies": { - "@peculiar/asn1-ecc": "^2.3.8", - "@peculiar/asn1-schema": "^2.3.8", - "cbor-x": "^1.5.9", + "@peculiar/asn1-ecc": "^2.7.0", + "@peculiar/asn1-schema": "^2.7.0", + "cbor-x": "^1.6.4", "cors": "^2.8.6", "did-jwt": "^7.4.7", "did-resolver": "^4.1.0", - "express": "^5.1.0", - "firebase-admin": "^13.9.0" + "dotenv": "^16.6.1", + "express": "^5.2.1", + "firebase-admin": "^13.10.0", + "tsx": "^4.22.3" }, "devDependencies": { "@types/cors": "^2.8.19", - "@types/express": "^5.0.0", - "@types/node": "^22.10.0", + "@types/express": "^5.0.6", + "@types/node": "^22.19.19", "tsx": "^4.19.2", - "typescript": "^5.7.2" + "typescript": "^5.9.3" } }, "node_modules/@cbor-extract/cbor-extract-darwin-arm64": { @@ -1146,9 +1148,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.18.tgz", - "integrity": "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ==", + "version": "22.19.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.21.tgz", + "integrity": "sha512-VMeFBSCKQKmm2swI2kW51SFusDqekC6q9trBCvJ/JliDchFSuoYYKN7yVNjPthP1HKZcx3U1gI/wTcEBjEFKTA==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -1631,6 +1633,18 @@ "integrity": "sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA==", "license": "Apache-2.0" }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1982,9 +1996,9 @@ } }, "node_modules/firebase-admin": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.9.0.tgz", - "integrity": "sha512-qiCVBBFH+kfLiCXuuE9eAbBQSckPuA43fbQ/MNvQfd9nZcHFQExmQICD/N0sZrNZDNy8FSywhjFzJJGVQzG5UA==", + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.10.0.tgz", + "integrity": "sha512-rbuCrJvYRwqBqvbccMS8fj/x2zsaMisdf5RQbRzQzr14Rbq9r2UlpuBHqWAwrO6c9dIRF56xF/xoepXsD5yDuQ==", "license": "Apache-2.0", "dependencies": { "@fastify/busboy": "^3.0.0", @@ -1994,9 +2008,7 @@ "fast-deep-equal": "^3.1.1", "google-auth-library": "^10.6.1", "jsonwebtoken": "^9.0.0", - "jwks-rsa": "^3.1.0", - "node-forge": "^1.4.0", - "uuid": "^11.0.2" + "jwks-rsa": "^3.1.0" }, "engines": { "node": ">=18" @@ -2906,15 +2918,6 @@ } } }, - "node_modules/node-forge": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", - "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, "node_modules/node-gyp-build-optional-packages": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", @@ -3622,19 +3625,6 @@ "license": "MIT", "optional": true }, - "node_modules/uuid": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", - "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 93bc835..8464a5b 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@peculiar/asn1-schema": "^2.7.0", "cbor-x": "^1.6.4", "cors": "^2.8.6", + "dotenv": "^16.6.1", "did-jwt": "^7.4.7", "did-resolver": "^4.1.0", "express": "^5.2.1", diff --git a/src/env.ts b/src/env.ts new file mode 100644 index 0000000..f4378ae --- /dev/null +++ b/src/env.ts @@ -0,0 +1,3 @@ +import { config } from "dotenv"; + +config(); diff --git a/src/index.ts b/src/index.ts index ee06521..ec178e1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +import "./env.js"; import cors from "cors"; import express from "express"; import "./services/firebase.js"; diff --git a/src/services/firebase.ts b/src/services/firebase.ts index cc8d655..3240b91 100644 --- a/src/services/firebase.ts +++ b/src/services/firebase.ts @@ -2,11 +2,45 @@ import admin from "firebase-admin"; import type { ServiceAccount } from "firebase-admin/app"; import type { Messaging } from "firebase-admin/messaging"; +type ServiceAccountJson = ServiceAccount & { project_id?: string }; + +function serviceAccountProjectId(account: ServiceAccountJson): string | undefined { + if (typeof account.projectId === "string" && account.projectId.length > 0) { + return account.projectId; + } + if (typeof account.project_id === "string" && account.project_id.length > 0) { + return account.project_id; + } + return undefined; +} + function resolveCredential(): admin.credential.Credential { const json = process.env.FIREBASE_SERVICE_ACCOUNT_JSON; if (json !== undefined && json.trim() !== "") { - return admin.credential.cert(JSON.parse(json) as ServiceAccount); + let account: ServiceAccountJson; + try { + account = JSON.parse(json) as ServiceAccountJson; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error( + "[Firebase] FIREBASE_SERVICE_ACCOUNT_JSON parse failed:", + message + ); + throw err; + } + const projectId = serviceAccountProjectId(account); + console.log( + "[Firebase] Credential: FIREBASE_SERVICE_ACCOUNT_JSON (parsed successfully)" + ); + if (projectId !== undefined) { + console.log("[Firebase] project_id:", projectId); + } else { + console.log("[Firebase] project_id: (not found in service account JSON)"); + } + return admin.credential.cert(account); } + + console.log("[Firebase] Credential: Application Default Credentials"); return admin.credential.applicationDefault(); }