firebase-functions: 0.7.5
firebase-tools: 3.16.0
firebase-admin: 5.5.0
Disclaimer: I have a unique issue where I need to initialize the app for each request. Each client will have their own firebase app and therefore their own credentials to manage their own Admin SDK. That is why the admin.initializeApp is inside the /addUser block. I suspect this could be part of the issue.
```const express = require('express');
const app = express();
const functions = require('firebase-functions');
const Promise = require('promise');
const cors = require('cors');
const admin = require('firebase-admin');
const bodyParser = require('body-parser');
const fs = require('fs');
const staffRoles = ['admin', 'sysadmin', 'teacher', 'staff', 'superadmin'];
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors());
app.post('/addUser', function(req, res){
var userRecordUID = null;
var db = null;
var newUser = null;
var uid = null;
getSDKCredentials(req.body.state, req.body.district)
.then(function(response){
var serviceAccount = JSON.parse(response);
// intialize school with credentials
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: 'https://' + serviceAccount.project_id + '.firebaseio.com'
});
db = admin.database();
// verify user with token
return admin.auth().verifyIdToken(req.body.token)
.then(function(decodedToken){
return decodedToken;
}, function(error){
return Promise.reject({
msg: error.message,
error: error.code
});
});
}, function(error){
console.log(req.body.state + ' - ' + req.body.district + ': credentials not found. Error - 1003');
return Promise.reject({msg: 'Your school needs additional setup. Please contact support to correct this issue.', error: '1003'});
})
.then(function(decodedToken){
uid = decodedToken.uid;
// get varified user
return db.ref('users-public/' + uid).once('value')
.then(function(snapshot){
return snapshot.val();
});
})
.then(function(verifiedUser){
if(staffRoles.indexOf(verifiedUser.role) === -1){
console.log(req.body.state + ' - ' + req.body.district + ': Error 1004. UID-' + uid + ' ' + verifiedUser.firstName + ' ' + verifiedUser.lastName + ' role-' + verifiedUser.role);
return Promise.reject({msg: 'You are not authorized to perform this task.', error: '1004'});
} else {
newUser = req.body.newUser;
// creat new user with email and password
return admin.auth().createUser({
email: newUser.email,
password: newUser.password
})
.then(function(userRecord){
return userRecord;
}, function(error){
return Promise.reject({msg: newUser.email + ' - ' + error.message, error: error.code});
});
}
})
.then(function(userRecord){
userRecordUID = userRecord.uid;
var updatedUserData = {};
if(newUser.role === 'student'){
updatedUserData['users-public/' + userRecord.uid] = {
email: newUser.email,
firstName: newUser.firstName,
lastName: newUser.lastName,
role: newUser.role,
yog: newUser.yog,
dob: newUser.dob || null
};
updatedUserData['students-private/' + userRecord.uid] = {
firstName: newUser.firstName,
lastName: newUser.lastName,
address1: newUser.address1 || null,
address2: newUser.address2 || null,
city: newUser.city || null,
state: newUser.state || null,
zip: newUser.zip || null,
guardians: newUser.guardians || null
};
updatedUserData['passwords/' + userRecord.uid] = newUser.password;
// add student info to database
return db.ref().update(updatedUserData)
.then(function(){
return Promise.resolve();
}, function(error){
console.log('write error');
return Promise.reject('write error');
});
} else {
// staff member to database
return db.ref('users-public/' + userRecord.uid).set({
email: newUser.email,
firstName: newUser.firstName,
lastName: newUser.lastName,
role: newUser.role,
officialTitle: newUser.officialTitle,
_title: newUser._title,
ext: newUser.ext
})
.then(function(){
return Promise.resolve();
}, function(error){
return Promise.reject('write error');
});
}
})
.then(function(){
// success!
res.status(200).json({
msg: 'User ' + newUser.firstName + ' ' + newUser.lastName + ' was successfully created'
});
})
.catch(function(error){
console.log(error);
res.json(error);
});
});
function getSDKCredentials(state, district){
var filePath = './credentials/' + state + '-' + district + '.json';
return new Promise(function(resolve, reject){
fs.readFile(filePath, 'utf8', function(error, response){
if(error){
return reject(error);
} else {
return resolve(response);
}
});
});
}
function promiseError(details){
this.msg = details.msg;
this.error = details.error;
}
exports.api = functions.https.onRequest(app);
### Steps to reproduce
HTTP POST Request to `https://my.cloud.function.url/api/addUser`
I was testing errors stepping through each promise to make sure I was getting the proper response in the .catch block. I noticed that SOMETIMES the response returned is
{ Error: The default Firebase app already exists. This means you called initializeApp() more than once without providing an app name as the second argument. In most cases you only need to call initializeApp() once. But if you do want to initialize multiple apps, pass a second argument to initializeApp() to give each app a unique name. at FirebaseAppError.Error (native) at FirebaseAppError.FirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:39:28) at FirebaseAppError.PrefixedFirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:85:28) at new FirebaseAppError (/user_code/node_modules/firebase-admin/lib/utils/error.js:119:28) at FirebaseNamespaceInternals.initializeApp (/user_code/node_modules/firebase-admin/lib/firebase-namespace.js:55:23) at FirebaseNamespace.initializeApp (/user_code/node_modules/firebase-admin/lib/firebase-namespace.js:312:30) at /user_code/index.js:26:10 at tryCallOne (/user_code/node_modules/promise/lib/core.js:37:12) at /user_code/node_modules/promise/lib/core.js:123:15 at flush (/user_code/node_modules/promise/node_modules/asap/raw.js:50:29) errorInfo: { code: 'app/duplicate-app', message: 'The default Firebase app already exists. This means you called initializeApp() more than once without providing an app name as the second argument. In most cases you only need to call initializeApp() once. But if you do want to initialize multiple apps, pass a second argument to initializeApp() to give each app a unique name.' }, codePrefix: 'app' }
```
Sometimes I can run my request receiving the expected error any number of times in a row. For instance, I am testing for the auth/email-already-exists error, I might get the aforementioned error about the Firebase app already exists, or I will get the auth/email-already-exists error. There doesn't seem to be any pattern that returns one over the other. Sometimes it will work as expected, sometimes it won't.
Yes
Should return my custom responses and never 'Firebase App Already Exists'
Randomized as expected responses with 'Firebase App Already Exists' responses
Solved!
For future reference if anybody else needs this.
First I created a function to get the desired app.
var apps = {};
function getApp(name){
if(apps[name]){
return;
}
// if app doesn't exist yet, initialize
apps[name] = admin.initializeApp(config, name);
return;
}
Then you can specify which app you are going to use when the route is called.
// retreive stored app
var thisApp = apps[name];
// retreive auth
var thisAppAuth = thisApp.auth();
// retreive database
var thisAppDb = thisApp.database();
Most helpful comment
Solved!
For future reference if anybody else needs this.
First I created a function to get the desired app.
Then you can specify which app you are going to use when the route is called.