Second Firebase project cant access first project Firestore - firebase

I'm building an e-commerce website and admin dashboard.
Website using its own hosting, Firestore and authentication.
Admin dashboard own hosting and authentication, but using website Firestore .
The security rules are not letting me in. When I change to admin dashboard project id to the website one, I can access the website Firestore.
I'm thinking that it's know I am coming from the second project that why it didn't allow me to access the website Firestore.
How can I allow second project to access the first project Firestore
function authorize() {
return request.auth != null &&
request.auth.token.email_verified;
}
function matchOwnID(userID) {
return request.auth.uid == userID;
}
function authAdmin() {
return authorize() &&
exists(/databases/$(database)/documents/admin/$(request.auth.uid));
}
match /admin/{adminID} {
allow get : if authAdmin() && matchOwnID(adminID); <--cant access from second project but first project can access.
// allow get : if true; <-----can access from second project.
allow list : if false;
allow write: if false;
}

How can I allow second project to access the first project Firestore
You cannot have users authenticated through the Auth service of one Firebase project accessing the Firestore database of an another Firebase project which has security rules based on the user's authentication state. This is not possible, since you cannot share the Firebase ID tokens across different projects.
One solution is to configure the two projects in your app, as explained in the "Configure multiple projects" doc.
Then you will be able to login with the two different user accounts corresponding to the two Firebase projects.
For the web, it could be done as follows:
const primaryAppConfig = {
apiKey: '.....',
authDomain: '.....',
// ...
};
firebase.initializeApp(primaryAppConfig);
const secondaryAppConfig = {
apiKey: '.....',
authDomain: '.....',
// ...
};
var secondaryApp = firebase.initializeApp(
secondaryAppConfig,
'secondary'
);
firebase
.auth()
.signInWithEmailAndPassword(email1, password1)
.then(() => {
secondaryApp.auth().signInWithEmailAndPassword(email2, password2);
})
.then(() => {
firebase
.firestore()
.collection('...') // Firestore collection from the Primary Project
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, ' => ', doc.data());
});
});
secondaryApp
.firestore()
.collection('...') // Firestore collection from the Secondary Project
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, ' => ', doc.data());
});
});
});
Having said that, are you 100% sure that you need to use the authentication services of the two Firebase Projects? A common approach is to have your main app in a first Firebase Project, using the Auth service of this project. And then use a second Firebase project just for the hosting of the Admin dashboard app. But, in the Admin dashboard app, you point to the first project.
Another possible approach is to set up two Firebase Hosting sites in a single Firebase project, see the doc for more details.

Related

React-Native + Apple sign-in + Firestore: permission-denied

I'm trying to add Apple Sign-In to my project which is based on react native and firestore. Authentication flow itself works fine but firestore security rules reject my request when I try to create a user profile afterwards.
Firebase security rules:
rules_version = '2';
service cloud.firestore {
match /users/{userId} {
allow create:
if request.auth != null;
...
}
...
}
Simplified React Native code:
import { firebase } from './config';
import { firebase as RNFBAuth } from '#react-native-firebase/auth';
// Step 1
const credential = RNFBAuth.auth.AppleAuthProvider.credential(token, nonce);
// Step 2
RNFBAuth.auth().signInWithCredential(credential).then((response) => {
if (response.additionalUserInfo.isNewUser) {
// Step 3
firebase.firestore()
.collection('users')
.doc(uid)
.set({
// profile details
})
.then(() => {
// update local state
})
.catch((_error) => {
console.log(_error + ": " + _error.code);
});
}
});
Step 3 is failing with error code FirebaseError: The caller does not have permission: permission-denied.
Error is gone when Firestore security rules are downgraded to "allow create: if true". Unfortunately it does not fly for me for obvious reasons.
My guess is firebase/firestore does not know that user completed authentication via firebase/auth package thus request in "Step 3" is being send as unauthenticated one. Any ideas how to sync them?
Other Auth Providers like Google and Facebook are located at the main firebase package instead of firebase/auth thus same problem does not apply for them:
const credential = firebase.auth.FacebookAuthProvider.credential(token);
const credential = firebase.auth.GoogleAuthProvider.credential(token);
const credential = RNFBAuth.auth.AppleAuthProvider.credential(token, nonce);
Any ideas how to solve it?
Eventually the problem has been found - incompatible package versions. I've upgraded all firebase packages and #invertase/react-native-apple-authentication to the latest versions and everything seems to work fine now.
As sugested in the comment you should use the onAuthStateChanged listener:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
var uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});
That way you have a general solution for all auth providers. It would not matter wich one you use. The trigger will be fired asap a user is signed in. I also use that method in all of my apps to sync user data.
You can read more about it here.

create user with self-specified uid

I am using flutter with firebase to manage my users, and in this link, it says you can specify the uid during user creation: https://firebase.google.com/docs/auth/admin/manage-users#create_a_user
My question: What's the equivalent in dart/ flutter? I understand firebase auto-generates one for you, but in my use case I need to be able to specify mine.
For flutter, I am only aware of createUserWithEmailAndPassword method but it does not have a 'uid' argument.
FirebaseAuth.instance.createUserWithEmailAndPassword(email: null, password: null)
In the link above, however, they provided an example (node.js) with such methods.
admin.auth().createUser({
uid: 'some-uid',
email: 'user#example.com',
phoneNumber: '+11234567890'
})
.then(function(userRecord) {
// See the UserRecord reference doc for the contents of userRecord.
console.log('Successfully created new user:', userRecord.uid);
})
.catch(function(error) {
console.log('Error creating new user:', error);
});
You can fully control the creation of Firebase Authentication users by implementing a custom provider. But as you can probably imagine, this is a sensitive operation, so the code requires that you have full administrative access to the Firebase project. For that reason, you can't run this type of operation in your Flutter app, but must run it on a trusted environment, such as your development machine, a server you control, or Cloud Functions.
Typically this means:
you'll gather the user's credentials in your Flutter app
send them (securely) to a custom endpoint on the server
and there validate the the user credentials are correct
and use the Admin SDK to create a token for the user
that you then send back securely to the Flutter app
There is no such option for any of the Firebase the client SDKs on any platform (Android, iOS, web, Flutter, etc).
Firebase Admin is a server SDK, and trusts that the code calling its methods is privileged and running in a secure and trusted environment. This is considered to be safe for inventing UIDs. The client SDKs are run on user devices, which is considered untrusted and could be compromised. In that case, the Firebase Auth product has to come up with an appropriate UID for the user.
Use the firebase cloud functions
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.createUser1 = functions.https.onCall(async (data, _context) => {
try {
const user = await admin.auth().createUser({
uid: data.uid,
phoneNumber: data.phoneNumber,
disabled: false,
}); return {response: user};
} catch (error) {
throw new functions.https.HttpsError("failed to create a user");
}
});
then request from flutter app
{
"data":
{
"uid": "12345678",
"phoneNumber": "+905378227777",
"disabled": false
}
}

one of the firebase realtime database instance not authenticated when initialized for multiple database in web javascript

in web i have firebase project with two database instances in the same project / app
after successful login with firebase auth;
the second database reference seems to be non authenticated and thus the database access fail; with auth != null in rules.
if i replace the second database url in config object and put default database url in config2 the second database than starts to work fine authenticated but old database fails with database access rule auth != null
How can i use both database in web javascript code without anyone having the above issue ?
Is the initialisation done correctly ?
Thanks in Advance
Should be documented properly in firebase documentation for Auth in web
but unfortunately its not for this question.
So the firebase auth instances are maintained separately including the auth callback
So i had to do like bellow
await firebase.auth().signInWithEmailAndPassword(_ths.form.email, _ths.form.password);
await firebase.app('app2').auth().signInWithEmailAndPassword(_ths.form.email, _ths.form.password);
let authPromises = [];
authPromises.push(new Promise((resolve,reject)=>{
firebase.auth().onAuthStateChanged(function (user) {
if (user) resolve(user);
});
}));
authPromises.push(new Promise((resolve,reject)=>{
firebase.app('app2').auth().onAuthStateChanged(function (user) {
if (user) resolve(user);
});
}));
Promise.all(authPromises).then(async(users)=>{
// ....
let oldDbInstance = firebase.database();
let newDbInstance = firebase.database(app2);
});
Phew!!!

Firebase Functions cannot always save a user to Realtime Database

I use Firebase auth and realtime database in my Android app. This is the code that I use in Firebase functions to save the user email into the realtime database when they register in the app with email:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.initializeUserProfile = functions.auth.user().onCreate(user => {
const userUid = user.uid;
return admin.auth().getUser(userUid).then(userRecord => {
const userProfile = {
email: userRecord.email
};
return admin.database().ref(`/profiles/${userUid}`).set(userProfile);
}).catch(error => {
console.log("Error fetching user data: ", error);
});
});
exports.removeUserProfile = functions.auth.user().onCreate(user => {
const userUid = user.uid;
return admin.database().ref(`/profiles/${userUid}`).remove();
});
When I register an user in the android app (I use the built in registration UI for Firebase), it gives me no error in the Functions logs:
My problem is that although I don't have an error in the log and the user was added to the Firebase Authentication section, the Realtime database doesn't contain the node with the email. The problem is very sporadic. Sometimes it registers it fine into the realtime database, but sometimes it doesn't (like in the log of Jun 25). In the Android app I try to query the database node of the user after registration to display they email and there I get an error (maybe it is an bug in my app, but anyhow, that code up there should be run on server side and the email should be in the Firebase Realtime Database).
What I also don't know is that why do I have those removeUserProfile calls in the log as I didn't remove any user from the Authentication database or from the Realtime database.
Actually, your two Cloud Functions are triggered with exactly the same event, i.e. onCreate(user). So it is normal that they are triggered (almost) simultaneously and that you see the two invocations in the log.
Since you write that "The problem is very sporadic" what is probably happening is that the new record is first created at /profiles/${userUid} by the initializeUserProfile Cloud Function BUT is then removed by the removeUserProfile Cloud Function.
So you should change the trigger of the removeUserProfile Cloud Function to onDelete():
exports.removeUserProfile = functions.auth.user().onDelete((user) => {
const userUid = user.uid;
return admin.database().ref(`/profiles/${userUid}`).remove();.
});

Firestore in functions throws billing error about external traffic

I have a firebase function for creating thumbnails whenever an image is uploaded. It's working great!
I then decided I wanted to store the thumbnail URL in the proper document in a firestore collection. I went through the examples and I found the relevant code to access my firestore through the admin object
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
// etc
return admin.firestore()
.collection("my-collection")
.doc(colId)
.set({ thumbUrl: fileUrl });
But when I added this code the function started failing with this message:
Billing account not configured. External network is not accessible and
quotas are severely limited. Configure billing account to remove these
restrictions
My code
// before is the upload to bucket that works
.then(() => {
fs.unlinkSync(tempFilePath);
return Promise.all([
tempFilePath.getSignedUrl({
action: "read",
expires: "03-09-2491"
})
]);
})
.then(results => {
console.log("Got thumb signed URL");
const thumbResult = results[0];
const thumbFileUrl = thumbResult[0];
// Add the URLs to the Database
return admin
.firestore()
.collection("my-collection")
.doc(colId)
.set({ thumbUrl: thumbFileUrl });
})
.then(() => console.log("User was updated with thumb url"));
Well, I understand that the free plan only allows external access to Google's internal stuff but I'm using firestore... it's Google.
What could be wrong?
firebase-admin is at v5.4.2
firebase-functions is at v0.7.1
When you are using the free Spark plan these messages will appear even if you are not trying to do any external access.

Resources