How to delete other user using Firebase functions? - javascript

As I understand, it is not possible for one user to delete another user in the firebase. From previous topic I learn that I can use firebase functions for that. Each user has a document in the cloud firebase (path: /users/userPhoneNumber/{age,height,...}). Once the document is deleted, I want to delete the user from the firebase authentication. I know how to catch a change in the cloud firebase using function (although I'm not sure how to catch a deletion), but the problem I'm having is how can I delete the user? I'm using Java for my app side and javascript for my funcations side. As I understand, the user should have the app installed on the phone in order to delete his authentication.

Since the user's Firestore document ID is the user's phone number, you can write a Cloud Function as follows, by using the Admin SDK getUserByPhoneNumber() and deleteUser() methods.
exports.deleteUser = functions.firestore
.document('users/{userPhoneNbr}')
.onDelete(async (snap, context) => {
try {
const userPhoneNbr = context.params.userPhoneNbr;
const userRecord = await admin.auth().getUserByPhoneNumber(userPhoneNbr);
await admin.auth().deleteUser(userRecord.uid);
return null;
} catch (error) {
// ....
}
});

Related

Create multiple Firebase Instances for the same project in Node.js

I have a Node.js server, inside which I want to have two firebase instances.
One instance should use the JavaScript SDK and will be used to provide authentication - login/register. The other instance should use the Admin SDK and will be used to read/write from the Realtime Database. I want to use this approach, so that I don't have to authenticate the user before each request to the Realtime DB.
I've read how we're supposed to initialize Firebase instances for multiple projects, but I'm not sure if my issue isn't coming from the fact that both instances are for the same project.
My issue is that I can use the JS SDK without any issue and I can login/register the user, but for some reason I can't get the Admin SDK to work.
Here's how I'm instantiating the apps:
const admin = require("firebase-admin");
const { applicationDefault } = require('firebase-admin/app');
admin.initializeApp({
credential: applicationDefault(),
databaseURL: 'my-database-url'
}, 'adminApp');
const firebase = require("firebase/app");
firebase.initializeApp(my-config);
Now I can use the JS SDK without an issue, but not the Admin SDK. I've created a test endpoint to just get data from my Realtime DB:
app.get("/api/test", (req, res) => {
const uid = 'my-user-UID';
admin.database().ref(`users/${uid}`)
.once('value', (snapshot) => {
if(snapshot) {
console.log('data');
} else {
console.log('no data');
}
});
});
Now here as an approach to getting the data from the Realtime DB, I tried all possible described approaches. Using get with child and all sorts of possible combinations. Here's an example of another approach I used:
get(child(ref(admin.database()), `users/${uid}`)).then((snapshot) => {
if (snapshot.exists()) {
// retrieved data
} else {
// No data
}
}).catch((error) => {
console.error(error);
});
For the first approach I wasn't getting any response at all, like the once wasn't executing. For the second one I think I was getting - typeerror: pathstring.replace is not a function firebase. At some point I was getting a no firebase app '[default]' has been created . These errors don't worry me as much, but since I saw the last error I moved my focus to the initialization of the apps, but still to no avail.
I just need a direction of where my issue might be coming from.
Update:
The solution is to not pass a second argument (app name) to any of the Firebase initializations. Looks like it's not needed in case you're referencing the same project.

How To Setup Custom Claims In My React Website For a Login Page

I want to set up custom claims to a certain number of users let's say 5 users would be admins on my website. I want these 5 users to be able to log in through the login page which would redirect them to the dashboard.
but I still don't fully understand the concept of the custom claims and how to use them and firebase documentation is limited with examples.
In their example they show that I can pass a uid that I want to assign a custom claim to, but how is this supposed to be a variable when i want certain users uid's from my firestore database Users collection to be admins and have a custom claim, in other words, where would I put this code or how would I assign a custom claim to more than one user at a time and how and where would this code be executed.
if anyone can give me an example of how I would make this work.
here is what I did:
created a firebaseAdmin.js file:
var admin = require("firebase-admin");
// lets say for instance i want these two users to be admins
//2jfow4fd3H2ZqYLWZI2s1YdqOPB42
//2jfow4vad2ZqYLWZI2s1YdqOPB42 what am i supposed to do?
admin
.auth()
.setCustomUserClaims(uid, { admin: true })
.then(() => {
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
});
I honestly don't know what to do from here.
Custom Claims can only be set from a privileged server environment via the Firebase Admin SDK. The easiest ways are either using a Node.js script (running the Admin SDK) or a Cloud Function (which also uses the Admin SDK).
Let's look at the example of a Callable Cloud Function that you call from your front-end (and in which you could check the UID of the user who is calling it, i.e. a Super Admin).
exports.setAdminClaims = functions.https.onCall(async (data, context) => {
// If necessary check the uid of the caller, via the context object
const adminUIDs = ['2jfow4fd3H2ZqYLWZI2s1YdqOPB42', '767fjdhshd3H2ZqYLWZI2suyyqOPB42'];
await Promise.all(adminUIDs.map(uid => admin.auth().setCustomUserClaims(uid, { admin: true })));
return { result: "Operation completed" }
});
A Node.js script would be similar:
#!/usr/bin/node
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.cert(".....json") // See remark on the private key below
});
const adminUIDs = ['2jfow4fd3H2ZqYLWZI2s1YdqOPB42', '767fjdhshd3H2ZqYLWZI2suyyqOPB42'];
Promise.all(adminUIDs.map(uid => admin.auth().setCustomUserClaims(uid, { admin: true })))
.then(() => {
console.log("Operation completed")
})
You must generate a private key file in JSON format for your service account , as detailed in the doc.
Then, when the Claims are set, you can access these Claims in your web app, and adapt the UI (or the navigation flow) based on the fact the user has (or not) the admin claim. More detail here in the doc.

Firebase - check if user created with Google Account is signing up or logging in?

I'm trying to create a web application, where you can log-in and register an account with a google account. I have managed to make it so they can log-in with the signInWithPopup(provider), but not sure how to Implement the sign-up. Any suggestions? or know of any functions in firebase i can use?
There aren't any separate methods to login and sign up when using Google Sign-In or any other provider. If a user with that credential exists then user will be logged in else a new user account will be created. You can then use additionalUserInfo.isNewUser property from the result received from signInWithPopUp method to check if the user is new.
firebase.auth().signInWithPopup(provider).then(function (result) {
const {additionalUserInfo: {isNewUser}} = result;
console.log(isNewUser ? "This user just registered" : "Existing User")
})
For the new Modular SDK (V9.0.0+), the same can be written as:
import { signInWithPopup, getAdditionalUserInfo } from "firebase/auth"
const result = await signInWithPopup(auth, googleProvider);
// Pass the UserCredential
const { isNewUser } = getAdditionalUserInfo(result)
So far, as I understood, you have two options to log in to your website: one is to make a local username/password account on your website, and the other option is to use your google account. I suppose the best way would be to check if the Google account is linked to any existing user using additionalUserInfo.isNewUser, and then linking up your google account with the account that is created via your website locally by following this article: https://firebase.google.com/docs/auth/web/account-linking?hl=lt
Once you have Firebase dependency inside your application. You can use createUserWithEmailAndPassword method to do that.
firebase
.auth()
.createUserWithEmailAndPassword("email#domain.com", "123123")
.then(data => {
data.user.updateProfile({
displayName: this.form.name
}).then(() => {});
})
.catch(err => {
this.error = err.message;
});

How to delete authenticated user from firebase in angular

when clicking on a button i called a function,
onDelete(id:string){ this.db.collection('Students').doc(id).delete(); }
Here, id is a name of document that i want to delete, db is a property of type AngularFireStore, 'Students' is a name of collection.
Structure of document:
enter image description here
In the above image, collection name is Students, under which multiple documents exist, since document name must be unique so i given that name a number of type string which acts as id. In every document, there is email field, i want to delete that email from authentication when i delete the same document.
code to sign up users:
this.afAuth.auth.createUserWithEmailAndPassword(email:string,password:string).then(res=>{})
If you want to delete a user existing in Firebase authentication you have two possibilities:
1/ Using the JavaScript SDK (since your app is made with angular)
You call the delete() method, as follows:
const user = firebase.auth().currentUser;
user.delete()
.then(() => {
//....
})
.catch(err => {
if (err.code === "auth/requires-recent-login") {
//Re-authenticate the user
} else {
//....
}
})
Note however, that this method "requires the user to have recently signed in. If this requirement isn't met, ask the user to authenticate again and then call firebase.User.reauthenticateWithCredential". An error with the auth/requires-recent-login code is "thrown if the user's last sign-in time does not meet the security threshold".
So, only the logged-in user can call this method from a front-end, in order to delete his/her own account.
2/ Using the Admin SDK
You can use the Admin SDK's deleteUser() method, for example within a Cloud Function.
In this case, there is no need to have the user logged-in since this is executed in the back-end and it is therefore possible to delete any user.
For example, you could have a Callable Cloud Function triggered by an admin user.
Another possibility, is to trigger a Cloud Function upon the Firestore user's document deletion.
Update based on your Question update:
I understand that you want to delete the user record in the Auth service upon deletion. For that you can write a Cloud Function as follows:
exports.deleteUser = functions.firestore
.document('Students/{studentID}')
.onDelete((snap, context) => {
const deletedValue = snap.data();
const userEmail = deletedValue.Email;
return admin.auth().getUserByEmail(userEmail)
.then(userRecord => {
const userID = userRecord.uid;
return admin.auth().deleteUser(userID)
})
.catch(error => {
console.log(error.message);
return null;
})
});

Firebase functions with AdminSdk and RealtimeDatabase not working

I'd like to create, edit, read and delete on the RealTime Database using the firebase functions. Looking at other similar questions I saw that the AdminSdk has to be used, and so I did.
I basically copy/pasted the code provided by the same firebase guides.
const admin = require("firebase-admin");
const functions = require("firebase-functions");
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
});
const db = admin.database();
db.ref("devices")
.once("value")
.then(snapshot => console.log("Snapshot: ",snapshot.val())
.catch(error => console.log(error))
});
In the initialization I set the credential with applicationDefault() as I previously set the GOOGLE_APPLICATION_CREDENTIALS env variable with my service_account_key.json path.
I tried anyway to set it with the cert method and the result didn't change. As 3 accounts are showed in the Service account section I tried with all of them as well.
This said,when starting the functions from console with 'firebase serve' the log is not showed and no error either.
Is there anything I'm missing? Some further configuration or whatever error you might be aware of?
Thank you in advance!
Update following your comments:
You want to "create, edit, read and delete on the Realtime Database using Cloud Functions", as indicated in your question, mimicking the behaviour of a Client SDK but from a server that you control. You should use one or more Cloud Functions that you call directly from this server. The most appropriate (based on your comments) would be to use an HTTPS Cloud Function.
For example you could have an HTTPS Cloud Function like the simple one below, to write to a specific node of the Realtime Database, as follows:
exports.writeToNode = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const dbNode = req.body.nodeRef;
const objToWrite = req.body.nodeValue;
return admin.database().ref(dbNode).push(objToWrite)
.then(() => {
return res.send("Node " + dbNode + " updated!");
})
.catch(err => {
//please watch the official video https://www.youtube.com/watch?v=7IkUgCLr5oA&t=1s&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=3
});
});
});
You would call it by issuing a POST to the following URL https://us-central1-YOURPROJECTID.cloudfunctions.net/writeToNode, with a body like:
{
nodeRef: 'theNode',
nodeValue: {
firstName: 'John',
lastName: 'Doe'
}
}
Initializing the Admin SDK:
If you want to interact, from a Cloud Function, with the Realtime Database that is in the same Firebase project, you just need to initialize the Admin SDK without any parameter (i.e. admin.initializeApp();)
This way, the Admin SDK will use the Project's default service account, and will have full access to the Realtime Database (i.e. bypassing all the security rules).
So, initialize as follows:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
///// Additional thought /////
Note that you could maybe use the REST API exposed by the Realtime Database, instead of developing an entire set of CRUD endpoints through Cloud Functions. See https://firebase.google.com/docs/database/rest/start
REMAINING PART OF THE CONTENT OF THE INITIAL ANSWER, about background triggered Cloud Functions
You then need to declare a Cloud Function, as shown in the example below, by:
Selecting an "event handler";
Specifying the database path where it will listen for events and;
Executing the desired logic (normally using the data that was written at the path, or indicating that the node was deleted, etc...)
exports.makeUppercase = functions.database.ref('/devices/{pushId}/original')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime Database.
const original = snapshot.val();
console.log('Uppercasing', context.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the Realtime Database returns a Promise.
return snapshot.ref.parent.child('uppercase').set(uppercase);
});
This code snippet, copied from the documentation, will listen to any new node created under the devices node and will create an uppercase node the value of the original node in uppercase.
Note that this is a background triggered Cloud Function which is triggered when something "happens" at the specific path.
If you want to "create, edit, read and delete on the RealTime Database", as indicated in your question, mimicking the behaviour of a Client SDK, you may define one or more Cloud Functions that you call directly from your App. See the Callable Cloud Functions documentation.
You may alse read the following documentation items https://firebase.google.com/docs/functions/get-started and https://firebase.google.com/docs/functions/database-events and also watch the video series: https://firebase.google.com/docs/functions/video-series

Categories

Resources