The use of email verification + email/password authentication doesn't quite work for services that absolutely need email verification before the user can begin using the service.
Let me explain with an example for Google sign in first.
First, the user signs into their Google account (say email is op#op.com), and authorizes your app. Then you create a credential using the tokens received through that, and exchange those tokens with Firebase to log the user into Firebase. The user needs to exist in Firebase for you to use Firebase' email verification service (because the only way to get info on whether an email is verified is to check the currentUser object in the client, so you need a logged in user to check if their email is verified. You can't call an Auth method with an email address to check if it's verified or not). So once you log the user into Firebase, you send them a verification link, and all is good. You can configure the view on your client by checking the user object for email verification. An important point to note here is that some other user who knows this user's email address cannot register using op#op.com on your service: this is because they need to sign into Google with that email to register.
Facebook is similar to Google sign-in in this regard.
However, for email/password, anyone can take someone else's email and create an account with it! And since you cannot send a verification link before the user is registered in Firebase, you're essentially letting anyone in the world "block" email addresses on your service. I was initially trying to ensure email verification before the email is registered into Firebase, but quickly realized that I need the user in Firebase to do any email verification.
Am I missing something, or is this the expected behavior? If this is really how it works, then I might just not allow email/password login in my app.
Side note: another idea I had was to do verification by sending them a 6-digit code, and maintain my own verification system in Firebase. But then I can't add any security rules to it, since any client without a logged in user would need access to it ==> potential system abuse.
Thanks in advance for attempting to read through the long explanation.
So even though an account can be created unverified, you can still block user access using security rules. The latter is what matters and controls access. Here is an example how you can do so with realtime database rules:
{
"rules": {
"users": {
"$user": {
".read": "auth.token.email_verified == true && auth.uid === $user",
".write": "auth.token.email_verified == true && auth.uid === $user"
}
}
}
}
You can also do this on your own if you are verifying the ID token on your server by parsing the token payload and inspecting the email_verified.
So even if the user account is created, unless the user is verified, they will not have access to your app/site data.
Related
I need to implement a standard user registration/activation workflow with Firebase. There doesn't seem to be an obvious way to implement this. When I say "standard", I mean how most email/password accounts work - not necessarily specific to Firebase. I'm sure you're familiar with this. This is the workflow:
User enters their username/password on a form with some validation and submits details
The back-end creates the user record in the database, but the account remains deactivated (i.e. user cannot authenticate - the activated flag is set to false)
The back-end sends an email to the user with a link to activate the account
The user clicks the link in their email which triggers activation. This is probably a Web API of some description.
At this point, the user record's activated flag ticks over to true, and the user can now authenticate
The link probably also has a deep link that opens the app or navigates to a web page
The user can now log into the app
How do I configure Firebase to do all this?
Currently, the app allows the user to register. I am using the Flutterfire SDK. I call createUserWithEmailAndPassword, which successfully creates the user in Firebase. But, the user is already activated. The user should have a state of "disabled" in firebase until the account becomes activated. I can't find any settings to default the user to disabled when the account is first created.
I also managed to get Firebase to send out an activation email by calling sendSignInLinkToEmail, but this call is really designed for email authentication - not email activation. Opening the link should activate the account, but I have not figured out how to do this. This documentation makes it sound like it is possible. Perhaps, the Flutterfire SDK is missing this? I don't want to allow people to log in without a password. I only want to use this call to send out an email.
What am I missing here? Is this non-standard behavior for Firebase? If so, why? If the user is allowed to use an app with an email address that is not activated, they can impersonate someone else. We need to confirm at least that they are custodians of the email address that they are claiming to have.
Do other Firebase people just not worry about this?
Lastly, I know I can achieve this by creating a collection for users in Firebase and putting an "activated" flag there. But, if I do that, I've got to write a cloud function that accepts the link and then updates the user in the collection based on the received link. But I thought this would be automatic in Firebase. If Firebase doesn't have this built-in, I have to put all the security over the top to stop users from authenticating when they have not yet activated their account.
This is a pretty valid concern. I suppose the way around this is to check whether the signed-in user is verified whenever the app is launched. The User object that is returned from Firebase Auth has an emailVerified flag. Check this page for more details.
Using this flag you can choose to show a different screen or pop-up that has a button to send a verification link to the registered email address. Until the user verifies this address, you can limit access to some of the app's screens if you want.
Please note that I have not checked if this emailVerified flag is true for sign ups using Federated login providers like Google Sign-in and Apple Sign In. You might want to check that out.
I'm making a simple social media app. Now I'm trying to provide security while registering users.
I'd like something like. By the way, JUST code for when things go wrong, like if username exists in db, not when things go right for example: Auth.auth().crateUser()
if username already exists in db {
(dont let user register.)
} else {
(let user register)
}
if email is not valid {
(dont let user register.)
} else {
(let user register)
}
Something like that. I'm thinking maybe using alerts, etc.
Any answer is VERY appreciated...
You can tell Firebase Authentication to only allow a single user with each specific email address to register in your project. You do this by enabling the One account per email address setting in the authentication providers panel in the Firebase console.
If you mean with email validation that you want to ensure the user actually has access to the mail address that they enter, that is called email verification in Firebase. Firebase doesn't require that the user verifies their email address before they can sign in. But if your application requires that, you can tell Firebase to send a verification email from within the app. Then when the user clicks the link in that email, a property is set on their account that you can then check in your code (or in the server-side security rules if you're using the Realtime Database, Cloud Firestore, or Cloud Storage). For more on this flow, see the blog post Email Verification in Firebase Auth.
An alternative to this flow is to use Firebase's newer Email link provider. This prevents the need for the user to enter a password, instead using an email with a link for them to sign in. This automatically then also sets their email address to verified.
I'm building a website with a login and registration system where the users' information will be stored on a database. I'm implementing a standard e-mail verification step after the account creation. The process is the following: An account is created, but has a 'verified' flag set to False. Then, an e-mail is sent to the user with a link to verify the account. Finally, after the user clicks the link, the 'verified' flag is set to True.
This should be sufficient, but it got me thinking. What happens to the unverified accounts? Someone could set up millions of unverified accounts that fill up my database storage space and available usernames. This could be problematic.
Is there a way to build a similar system in which the account would only be stored in the database AFTER the verification?
One way to solve your "Problem" would be to use a Capchta at the registration and/or to delete all not-activated accounts when the registration date is further ago than x days.
You could encrypt all information of the registration in the verification link, instead of storing that information in a new user account.
When the user clicks the registration link, you would decrypt that information and create the account.
To keep the verification URL as short as possible, this would require to minimize the amount of information you request during registration. I would suggest to ask for the E-Mail only and to request all the other attributes on the verification page.
I'm trying to create a demo account for my app, so that I can submit it to the iOS App Store. The app requires that your email be verified before you can use it. However, the verification emails are not coming into my demo#xxx.xxx inbox, and so I cannot verify the account. Is there any way I can manually set that firebase account as verified?
Thanks
This works as a cloud function.
I add the userid as a key to a child called force with any value and it triggers this function that authenticates the email.
you also must include
const admin = require('firebase-admin');
cloud function
exports.forceQue = functions.database.ref('/force/{uid}').onWrite((data,context) => {
if (!data.after.exists()) {
return false;
}
const uid = context.params.uid;
admin.auth().updateUser(uid, {emailVerified: true});
return true;
});
Firebase Authentication doesn't have an API to set the verification status of an email address.
The Firebase Admin SDK (which should only be run on a trusted environment, such as a machine you control) can set this flag. See this documentation section for an example.
But unless you're submitting apps on behalf of the users of your app, shouldn't the email address just be your usual (real) mail address?
if you mean adding an email on your firbase to have access as a user to the app then you can do that as per the picture
I've got a decent set of existing users on my website who login via their emailaddress as their username.
I'm setting up Facebook OAuth mechanism to allow new users to sign up more conveniently, but I'm not sure how to handle the scenario when a user who already has an email address registered with our system and now tries to login via Facebook.
Should I consider him the same user?
Should I treat him like a new user?
The situation is more complicated by the fact that I dont validate their email addresses (when they login directly into my system), so i cant assume they are the same user.
How do others resolve this conflict, or do other folks simply treat this user connecting via FB as a new entity?
On your login screen, users can have a choice: you may put
new user: signup using facebook
since this is a totally new user account, then you just need to do your facebook connect + request for email permission, etc.
existing user: login by email
Once they do this, let them login using the old-fashioned way. Then once signed in, prompt them to connect this email address to their facebook account. So the flow is login via email then optional facebook connect.
To do this, I assume you've added a field on your database table for user_accounts, that is facebook_user_id or fb_id or user_id, etc. Then on facebook connect, get the logged-in-email, UPDATE table SET fb_id = xxx WHERE email = xx
I've pondered the same issue. I think we'll go with the verified email (Facebook Connect) getting attached to and logged into the existing account with the same email.
Before we connect and log them in we'll explain that the account exist and ask for their password (they signed up using email and password, so they should know it) to ensure it's the right person.
If you support multiple external authentications (Google OAuth, Facebook etc) then you may not have a password, and in that case it gets slightly trickier.
If you do log them in to the existing account without asking for a password, make sure you clear existing sessions to avoid 'anticipation attacks', where an attacker anticipates that the target signs up, creates an account and keep the session after they've signed up and attached to the attackers existing account.