I'm trying to figure out what is the proper way to keep the users in the firebase. Each user in my app must use an email. I saw people use:
new_user.put("email", email);
FirebaseUser user = mAuth.getCurrentUser();
if (user != null && user.getEmail() != null) {
fireDB.collection("users").document(user.getEmail()).set(new_user);
}
On the other hand I saw people use:
fireDB.collection("users").document(user.getUID()).set(new_user);
Which is better? getUID() or getEmail()? Why?
Don't use the email. Use the UID. It will never change, it's properly case sensitive, it doesn't contain any information (so it's safe to share with other users), and is always going to be a valid key for Firebase API calls.
Bear in mind also that a user isn't guaranteed to have an email address. Especially if they signed in with phone authentication.
If that's not enough reason, all of the provided sample code in the documentation uses the UID as the index. It's the gold standard for use with security rules, and recommended throughout the documentation:
https://firebase.google.com/docs/firestore/solutions/role-based-access
https://firebase.google.com/docs/storage/security/user-security
And I discuss it in my blogs:
https://medium.com/firebase-developers/patterns-for-security-with-firebase-per-user-permissions-for-cloud-firestore-be67ee8edc4a
In firebase you can access the current user's data by using FirebaseAuth.getInstance().getCurrentUser()..., I am wondering is there a way to access other Authenticated users public data by their id or email.
Accesing user data is a dangerous operation, imagine an app that allows you to change others people user name.
So in the clients you cant, unless you duplicate the user data to the RTD or the Firestore and using rules set privacy controls.
What I think you are looking for is something like the admin sdk for auth that allows to search for users using email or uid.
You can see the docs here
https://firebase.google.com/docs/auth/admin/manage-users
If you dont want to setup a server you can go all the way in the Firebase way; using Functions. Functions is a trusted enviroment like a server, so it can use the admin sdk for auth. You could create an onCall function for doing whatever you want.
If you want to only search users, please consider having a searchable version of the user data on any database, if you are looking for an admin type of feature then Functions onCall is what you need.
You will probably want to set admin privileges using customs claims
https://firebase.google.com/docs/auth/admin/manage-users
exports.userCreationListener = functions.auth.user().onCreate(user => {
const admins = {
"first#admin.com": true
};
const email = user.email;
if (!admins[email]) {
return false;
}
const uid = user.uid;
return admin.auth().setCustomUserClaims(uid, {superAdmin: true}).then(
()=>admin.database().ref(`users/${uid}`).set(true)).catch(error=>{
console.log("SUPER_ADMIN_UPDATE_ERROR", error);
return false;
});
});
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
// The user's ID, unique to the Firebase project. Do NOT use this value to
// authenticate with your backend server, if you have one. Use
// FirebaseUser.getIdToken() instead.
String uid = user.getUid();
}
Read guide https://firebase.google.com/docs/database/admin/retrieve-data
With This Question in mined here´s a follow up question.
Correct me on my assumptions please my learning curve is aggressive :).
If making lets say a chat app having Firebase as backend storage, then using the FirebaseAuth.getInstance().getCurrentUser().getUid() uid inside the chat system as a chat member identifier, a bad idea if, you allow users to delete there account, and allowing account linking. The uid would change and that would break the database right?
My next assumption now is that to have a secure user id inside the chat app one can use the signed in E-mail address right because it´s a trusted provider!?
My conclusion is to never use the getCurrentUser().getUid() uid as an identifierar for the user or?
It's not a bad idea to use getCurrentUser().getUid() but a better idea is to use as an identifier the email address. I'm saying this because in the case in which the user is is deleting the account and than returns, the uid will be for sure different. Because Firebase does not allow the dot symbol . in the key, the email address must be encoded like this:
name#email.com -> name#email,com
As you probably see, i have changed the . with ,. To do this, i recomand you using this methods:
static String encodeUserEmail(String userEmail) {
return userEmail.replace(".", ",");
}
static String decodeUserEmail(String userEmail) {
return userEmail.replace(",", ".");
}
Hope it helps.
Question says it all. In Firebase, how do I confirm email when a user creates an account, or, for that matter, do password reset via email.
I could ask more broadly: is there any way to send emails out from Firebase? E.g. notifications, etc. This isn't the kind of thing you would usually do client-side.
Update
Note that this was never a very secure way of handling email verification, and since Firebase now supports email verification, it should probably be used instead.
Original answer
I solved the email verification using the password reset feature.
On account creation I give the user a temporary (randomly generated) password. I then trigger a password reset which will send an email to the user with a link. The link will allow the user to set a new password.
To generate a random password you can use code similar to this:
function () {
var possibleChars = ['abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!?_-'];
var password = '';
for(var i = 0; i < 16; i += 1) {
password += possibleChars[Math.floor(Math.random() * possibleChars.length)];
}
return password;
}
Note that this is happening on the client, so a malicious user could tamper with your logic.
This would need to be done outside of firebase. I store users at /users/ and keep a status on them (PENDING, ACTIVE, DELETED). I have a small service that monitors users of a PENDING status and sends out a confirmation email. Which has a link to a webservice I've created to update the user status to ACTIVE.
[Engineer at Firebase - Update 2014-01-27]
Firebase Simple Login now supports password resets for email / password authentication.
Each of the Simple Login client libraries has been given a new method for generating password reset emails for the specified email address - sendPasswordResetEmail() on the Web and Android, and sendPasswordResetForEmail() on iOS.
This e-mail will contain a temporary token that the user may use to log into their account and update their credentials. This token will expire after 24 hours or when the user changes their password, whichever occurs first.
Also note that Firebase Simple Login enables full configuration of the email template as well as the sending address (including whitelabel email from your domain for paid accounts).
To get access to this feature, you'll need to update your client library to a version of v1.2.0 or greater. To grab the latest version, check out https://www.firebase.com/docs/downloads.html.
Also, check out https://www.firebase.com/docs/security/simple-login-email-password.html for the latest Firebase Simple Login - Web Client docs.
As at 2016 July, you might not have to use the reset link etc. Just use the sendEmailVerification() and applyActionCode functions:
In short, below is basically how you'll approach this, in AngularJS:
// thecontroller.js
$scope.sendVerifyEmail = function() {
console.log('Email sent, whaaaaam!');
currentAuth.sendEmailVerification();
}
// where currentAuth came from something like this:
// routerconfig
....
templateUrl: 'bla.html',
resolve: {
currentAuth:['Auth', function(Auth) {
return Auth.$requireSignIn() // this throws an AUTH_REQUIRED broadcast
}]
}
...
// intercept the broadcast like so if you want:
....
$rootScope.$on("$stateChangeError", function(event, toState, toParams, fromState, fromParams, error) {
if (error === "AUTH_REQUIRED") {
$state.go('login', { toWhere: toState });
}
});
....
// So user receives the email. How do you process the `oobCode` that returns?
// You may do something like this:
// catch the url with its mode and oobCode
.state('emailVerify', {
url: '/verify-email?mode&oobCode',
templateUrl: 'auth/verify-email.html',
controller: 'emailVerifyController',
resolve: {
currentAuth:['Auth', function(Auth) {
return Auth.$requireSignIn()
}]
}
})
// Then digest like so where each term is what they sound like:
.controller('emailVerifyController', ['$scope', '$stateParams', 'currentAuth', 'DatabaseRef',
function($scope, $stateParams, currentAuth, DatabaseRef) {
console.log(currentAuth);
$scope.doVerify = function() {
firebase.auth()
.applyActionCode($stateParams.oobCode)
.then(function(data) {
// change emailVerified for logged in User
console.log('Verification happened');
})
.catch(function(error) {
$scope.error = error.message;
console.log(error.message, error.reason)
})
};
}
])
And ooh, with the above approach, I do not think there's any need keeping the verification of your user's email in your user data area. The applyActionCode changes the emailVerified to true from false.
Email verification is important when users sign in with the local account. However, for many social authentications, the incoming emailVerified will be true already.
Explained more in the article Email Verification with Firebase 3.0 SDK
What I did to work around this was use Zapier which has a built in API for firebase. It checks a location for added child elements. Then it takes the mail address and a verification url from the data of new nodes and sends them forwards. The url points back to my angular app, which sets the user email as verified.
As I host my app files in firebase, I don't need have to take care of any servers or processes doing polling in the background.
There is a delay, but as I don't block users before verifying mails it's ok. Zapier has a free tier and since I don't have much traffic it's a decent workaround for time being.
The new Firebase SDK v3 appears to support email address verification, see here (put your own project id in the link) but it doesn't appear to be documented yet.
I have asked the question on SO here
See #SamQuayle's answer there with this link to the official docs.
As noted by various others Firebase does now support account related emails but even better, as of 10 days ago or so it also supports sending any kind of email via Firebase Functions. Lots of details in the docs and example code here.
I used following code to check the email verification after creating new account.
let firAuth = FIRAuth.auth()
firAuth?.addAuthStateDidChangeListener { auth, user in
if let loggedUser = user {
if loggedUser.emailVerified == false {
loggedUser.sendEmailVerificationWithCompletion({ (error) in
print("error:\(error)")
})
}
else {
print(loggedUser.email)
}
} else {
// No user is signed in.
print("No user is signed in.")
}
}
I used MandrillApp. You can create an API key that only allows sending of a template. This way even thought your key is exposed it can't really be abused unless someone wants to fire off tonnes of welcome emails for you.
That was a hack to get myself off the ground. I'm now enabling CORS from a EC2 that uses the token to verify that the user exists before extending them a welcome via SES.
How do I fetch the email address from LinkedIn profile in my Android app?
You need to set scope with email permission. After that you will be able to recover that specific data.
private static Scope buildScope() {
return Scope.build(Scope.R_BASICPROFILE, Scope.W_SHARE, Scope.R_EMAILADDRESS);
}
Then use following URL to make GET request.
String url = "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,public-profile-url,picture-url,email-address,picture-urls::(original))";
With this scope your ApiResponse will retrieve user's email.
Did you check the API documentation? From what I can see, there is no way to get the email ID from their API. My guess is that LinkedIn protects this particular information (as they should). If they allowed my personal information to be retrieved by anyone with access to the API I would likely get a lot more spam then I do.
I don't think you can access this information.
https://developer.linkedin.com/documents/people