Description
Describe your environment
- Operating System version: window 11
- Firebase SDK version: firebase-admin 13.0.0
- Firebase Product: credential, auth
- Node.js version: 20.12.0
- NPM version: 10,5.0
[REQUIRED] Step 3: Describe the problem
I followed the example provided for the admin.credential.cert() function, but encountered an issue.
I'm trying to initialize the app with admin.initializeApp() using Firebase Admin SDK version 13. However, there seems to be a discrepancy between the type defined in ServiceAccount and the key values actually used for authentication.
Steps to reproduce:
The following code causes the following error.
The incoming JSON object does not contain a private_key field
The incoming JSON object does not contain a client_email field
as-is
import admin, { ServiceAccount } from "firebase-admin";
const serviceAccount: ServiceAccount = {
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, "\n"),
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
};
if (!admin.apps.length) {
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: `https://${process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID}.firebaseio.com`,
});
}
export default admin;
So, after modifying the code as follows, it worked correctly.
import admin from 'firebase-admin'
const serviceAccount = {
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
client_email: process.env.FIREBASE_CLIENT_EMAIL,
private_key: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
}
if (!admin.apps.length) {
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: `https://${process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID}.firebaseio.com`,
})
} else {
admin.app()
}
export default admin
Relevant Code:
In my opinion, the transition to version 13.0 involved switching to use google-auth-library, which seems to be causing this issue.
maybe auth.fromJSON() cause error
src/app/credential-internal.ts 456 line
function populateGoogleAuth(keyFile: string | object, httpAgent?: Agent)
: { auth: GoogleAuth, client: AnyAuthClient | undefined } {
let client: AnyAuthClient | undefined;
const auth = new GoogleAuth({
scopes: SCOPES,
clientOptions: {
transporterOptions: {
agent: httpAgent,
},
},
keyFile: (typeof keyFile === 'string') ? keyFile : undefined,
});
if (typeof keyFile === 'object') {
if (!util.isNonNullObject(keyFile)) {
throw new FirebaseAppError(
AppErrorCodes.INVALID_CREDENTIAL,
'Service account must be an object.',
);
}
client = auth.fromJSON(keyFile);
}
return { auth, client };
}
google-auth-library /src/aut/jtwclient.js 206 line
fromJSON(json) {
if (!json) {
throw new Error('Must pass in a JSON object containing the service account auth settings.');
}
if (!json.client_email) {
throw new Error('The incoming JSON object does not contain a client_email field');
}
if (!json.private_key) {
throw new Error('The incoming JSON object does not contain a private_key field');
}
// Extract the relevant information from the json key file.
this.email = json.client_email;
this.key = json.private_key;
this.keyId = json.private_key_id;
this.projectId = json.project_id;
this.quotaProjectId = json.quota_project_id;
this.universeDomain = json.universe_domain || this.universeDomain;
}