FIrebase Authentication | Any triggers for when a user Signs in? [duplicate] - firebase

I see how execute a Cloud Function on user account creation:
exports.myFunction = functions.auth.user().onCreate(event => {
But I need my function to execute when the user logs in. Is there a onLogin trigger?
And can someone with 1500 points create a tag for firebase-cloud-functions?

There's no event for login, because only the client side can define exactly when a login happens. Different clients may define this in different ways. If you need to trigger something on login, figure out when that point is in your app, then trigger it from the client via a database or HTTP function.

This worked in the controller:
firebase.auth().onAuthStateChanged(function(user) { // this runs on login
if (user) { // user is signed in
console.log("User signed in!");
$scope.authData = user;
firebase.database().ref('userLoginEvent').update({'user': user.uid}); // update Firebase database to trigger Cloud Function
} // end if user is signed in
else { // User is signed out
console.log("User signed out.");
}
}); // end onAuthStateChanged
And this is the trigger in the Cloud Function:
exports.getWatsonToken = functions.database.ref('userLoginEvent').onUpdate(event => { // authentication trigger when user logs in
I made a location in Firebase Database called userLoginEvent.
One confusing bit is that in the functions console it's /userLoginEvent but in your code you must leave out the slash.

You can create your own analytics event, like login and used it as the trigger for your cloud function.
Then in your app, when the user successfully authenticate you use firebase analytics to send an event with the name you defined, like login
exports.sendCouponOnPurchase = functions.analytics.event('login').onLog((event) => {
const user = event.user;
const uid = user.userId; // The user ID set via the setUserId API.
});

You can trigger an https onCall firebase cloud function on login
ex: This is your login button trigger function which calls an https onCall function after authenticating the user.
_login() {
firebase
.auth()
.signInWithEmailAndPassword(this.state.email, this.state.password)
.then(function (user) {
var addMessage = firebase.functions().httpsCallable('myCloudFunctionName');
addMessage("whatever variable I want to pass")
.catch(error => {
console.log("I triggered because of an error in addMessage firebase function " + error)
)}
}).catch(error => {
console.log(error);
});
}

There is also another way you can do this inside Google Cloud if you enable Identity Platform for a project. Then you can follow this guide:
https://cloud.google.com/functions/docs/calling/logging
And trigger cloud functions for any of these Firebase Authentication events:
https://cloud.google.com/identity-platform/docs/activity-logging?authuser=1&_ga=2.226566175.-360767162.1535709791#logged_operations
The only problem I've just noticed is that the logs generated for Sign In event do not include the firebase app id or anything to determine which client the user logged in on which is really annoying as this was the main reason we needed to do this!

Related

How can I retrieve the authentication data of a user when using a Firebase callable function?

My mobile app built in Flutter uses google login to register users. From within this app I am calling a Firebase cloud function (called questionAnswer) using the Cloud Functions Plugin for Flutter.
If I understand correctly from this documentation, the https request should automatically include the Firebase authentication of the user.
How can I retrieve the authentication information of the user from within the Cloud Function? I need it in order to access data associated with that specific user in a Firebase Cloud Database. Should I include the google auth token as a parameter in the https request?
Here is my code in the Flutter app:
final HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(
functionName: 'questionAnswer',
);
fetchHttps() async {
dynamic resp = await callable.call();
print(resp);
}
This is the code in the Cloud Function
exports.questionAnswer = functions.https.onCall(() => {
console.log('addNumbersLog', "holaLog");
return answer;
});
As you can see from the documentation, auth information is in the second parameter passed to the function. You will need to declare and use it:
exports.questionAnswer = functions.https.onCall((data, context) => {
console.log('addNumbersLog', "holaLog");
console.log(context.auth);
return answer;
});
context here is a CallableContext object.
You can obtain the uid of the user from the CallableContext parameter, which is passed to your onCall handler as the second argument.
From there on you can retrieve the Firebase UserRecord using the .getUser() method and passing in the uid.
exports.questionAnswer = functions.https.onCall((data, { auth }) => {
admin.auth().getUser(auth.uid)
.then((userRecord) => {
// See the UserRecord reference doc for the contents of userRecord.
console.log('Successfully fetched user data:', userRecord.toJSON());
})
.catch(function(error) {
console.log('Error fetching user data:', error);
});
});
});

How do I query the Firebase authentication by phone number?

In our React 16.13.0 application, we are using Firebase. We link a user to a phone number like so
return firebase
.auth()
.currentUser.linkWithPhoneNumber(phoneNumber, recaptchaVerfier)
.then(function (confirmationResult: any) {
var code = window.prompt("Provide your SMS code");
recaptchaVerfier.clear();
return confirmationResult.confirm(code).then(() => {
callback();
});
})
I was curious how would we then go back and query the Firebase authentication table for users that have a particular phone number, assuming that phone number is used as the identifier for the user, as seen in the portal Authentication view below
. The purpose of querying is not for logging in, but rather for looking up various users.
You cannot query the Authentication database with the Client SDKs but you can with the Admin SDKs.
This means that you will need to implement this querying in your own server or in a Cloud Function.
You could for example write a Callable Cloud Function that would return the user details for a specific user.
The code would look like:
exports.getUserByPhone = functions.https.onCall(async (data, context) => {
try {
const phoneNbr = data.phoneNbr;
const userRecord = await admin.auth().getUserByPhoneNumber(phoneNbr);
return userRecord;
} catch (error) {
// See https://firebase.google.com/docs/functions/callable#handle_errors
// Also see here the error codes: https://firebase.google.com/docs/auth/admin/errors
// In particular, the auth/user-not-found code is returned if there is no existing user record corresponding to the provided identifier.
}
});
You would then call this Cloud Function from your front-end as explained here in the doc, by passing the value of the desired phoneNbr.

Firebase Auth and Caledar API

I am trying to log in a user and create an event in their calendar.
I am using Firebase Auth and Google Calendar API. I am not sure how I can use Firebase Access Token to create event for the user.
<script type="text/javascript">
initApp = function() {
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
user.getIdToken().then(function(accessToken) {
//how can I use this accessToken to create event in the calendar?
});
} else {}
}, function(error) {});
};
window.addEventListener('load', function() {
initApp();
});
</script>
I have a script that can log in a user and I have a script that can create an event (code not posted because the code is very similar to the attached links). How can I combine these two tasks?
From reading the documentation, I know that I have to add Calendar scopes and discovery documents to the auth script. However, I am struggling with the next steps.
Thanks for the help!
You can't use the Firebase ID token to call Google Calendar APIs.
You need to use the Google JS library to sign in to Google and to call calendar APIs. You can then get the Google ID token token to sign in with Firebase.
// Build Firebase credential with the Google ID token.
const credential = firebase.auth.GoogleAuthProvider.credential(
googleUser.getAuthResponse().id_token);
// Sign in with credential from the Google user.
firebase.auth().signInWithCredential(credential)
.then((authResult) => {
// User signed in with Firebase.
})
.catch((error) => {
// Error while signing in.
});

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();.
});

How to get the email of any user in Firebase based on user id?

I need to get a user object, specifically the user email, I will have the user id in this format:
simplelogin:6
So I need to write a function something like this:
getUserEmail('simplelogin:6')
Is that possible?
It is possible with Admin SDK
Admin SDK cannot be used on client, only in Firebase Cloud Functions which you can then call from client. You will be provided with these promises: (it's really easy to set a cloud function up.)
admin.auth().getUser(uid)
admin.auth().getUserByEmail(email)
admin.auth().getUserByPhoneNumber(phoneNumber)
See here https://firebase.google.com/docs/auth/admin/manage-users#retrieve_user_data
In short, this is what you are looking for
admin.auth().getUser(data.uid)
.then(userRecord => resolve(userRecord.toJSON().email))
.catch(error => reject({status: 'error', code: 500, error}))
full snippet
In the code below, I first verify that the user who calls this function is authorized to display such sensitive information about anybody by checking if his uid is under the node userRights/admin.
export const getUser = functions.https.onCall((data, context) => {
if (!context.auth) return {status: 'error', code: 401, message: 'Not signed in'}
return new Promise((resolve, reject) => {
// verify user's rights
admin.database().ref('userRights/admin').child(context.auth.uid).once('value', snapshot => {
if (snapshot.val() === true) {
// query user data
admin.auth().getUser(data.uid)
.then(userRecord => {
resolve(userRecord.toJSON()) // WARNING! Filter the json first, it contains password hash!
})
.catch(error => {
console.error('Error fetching user data:', error)
reject({status: 'error', code: 500, error})
})
} else {
reject({status: 'error', code: 403, message: 'Forbidden'})
}
})
})
})
BTW, read about difference between onCall() and onRequest() here.
Current solution as per latest update of Firebase framework:
firebase.auth().currentUser && firebase.auth().currentUser.email
See: https://firebase.google.com/docs/reference/js/firebase.auth.Auth.html#currentuser
Every provider haven't a defined email address, but if user authenticate with email. then it will be a possible way to achieve above solution.
To get the email address of the currently logged in user, use the getAuth function. For email and password / simplelogin you should be able to get the email like this:
ref = new Firebase('https://YourFirebase.firebaseio.com');
email = ref.getAuth().password.email;
In my opinion, the password object is not very aptly named, since it contains the email field.
I believe it is not a Firebase feature to get the email address of just any user by uid. Certainly, this would expose the emails of all users to all users. If you do want this, you will need to save the email of each user to the database, by their uid, at the time of account creation. Other users will then be able to retrieve the email from the database by the uid .
simple get the firebaseauth instance.
i created one default email and password in firebase. this is only for the security so that no one can get used other than who knows or who purchased our product to use our app.
Next step we are providing singup screen for user account creation.
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
String email = user.getEmail();
every time user opens the app, user redirecting to dashboard if current user is not equal to our default email.
below is the code
mAuth = FirebaseAuth.getInstance();
if (mAuth.getCurrentUser() != null){
String EMAIL= mAuth.getCurrentUser().getEmail();
if (!EMAIL.equals("example#gmail.com")){
startActivity(new Intent(LoginActivity.this,MainActivity.class));
finish();
}
}
i Am also searching for the same solution finally i got it.
I had the same problem. Needed to replace email in Firestore by uid in order to not keep emails all around the place. It is possible to call it from a script on your computer using Service Account. You don't need Firebase Functions for this.
First Generate service account and download its json key.
Firebase Console > gear icon > Project settings > Service accounts > Generate a new private key button.
https://console.firebase.google.com/u/0/project/MYPROJECT/settings/serviceaccounts/adminsdk
Then create project, add the key and call the Admin SDK.
npm init
npm install dotenv firebase-admin
Place the json key file from above into .keys directory, keeping the project directory clean of keys files. Also .gitignore the directory.
Write the path of the json key file into .env file like this: GOOGLE_APPLICATION_CREDENTIALS=".keys/MYPROJECT-firebase-adminsdk-asdf-234lkjjfsoi.json". We will user dotenv to load it later.
Write following code into index.js:
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.applicationDefault(),
});
(async () => {
const email = "admin#example.com";
const auth = admin.auth();
const user = await auth.getUserByEmail(email);
// Or by uid as asked
//const user = await auth.getUser(uid);
console.log(user.uid, user.email);
//const firestore = admin.firestore();
// Here be dragons...
})();
Run as follows node -r dotenv/config index.js
See the docs
Current solution (Xcode 11.0)
Auth.auth().currentUser? ?? "Mail"
Auth.auth().currentUser?.email ?? "User"

Resources