I have decided to implement some extra security processes in my app. Previously, I was updating the user password directly from the client side. I have decided to do it inside a Cloud Function, in order to also notify the user via email.
For this, I am passing the newPassword as param to my callable cloud function, where I execute:
admin.auth().updateUser(uid, {
password: newPassword,
});
I have thought that it will also be a good idea, to increase the security, to make sure that the user writes his current password in the form, in order to verify it in my cloud function, before updating the password.
How can I compare the current user’s password in admin mode (GCF)?
Related
I am implementing a forgot/reset password functionality in a Node.js app using NestJs.
That is the general flow:
A user types in his email in a "forgot password" form and submits the request
The server generates a jwt token with the user's ID as the payload, then sends an email with the token as the link to reset the password (example: GET: example.com/reset/generated_jwt_token)
User clicks the link from his email, the reset password page is rendered, he fills the form with the new password and submits the form with the password as the body (example: POST: example.com/reset/generated_jwt_token)
Server verifies the token (that is not expired + user ID from payload exists in the DB) and updates the password.
The main problem with this approach is that the jwt token can be used unlimited amount of times to reset the password (until it expires after X minutes).
Is there a way to solve this? some say to put the current password's hash as the payload since it will be changed anyway and will guarantee 1 time use, but I'm not a fan of this method.
EDIT: Another approach i encountered is creating a blacklist collection in the DB of jwt token that cannot be used more than once. Or using a cache in redis the same way, but it seems not very scalable.
When a token is generated, you could save it (or something unique embedded inside it) into the database under that user. Then, the server verifies the token:
(1) when the link from the reset email is clicked
(2) when the user submits the reset password page
by checking that the token is the same as the one for that user in the database.
Also, when the user successfully changes their password, clear the token from the database so it can't be used again.
I agree with the (accepted) answer of #CertainPerformance.
I would like to add - Consider using authentication-flows-js. You will not have to worry about the implementation at all!
It is a module that answers most flows - authentication, registration, forgot-password, change password etc., and it is secured enough so applications can use it without the fear that it will be easily hacked.
From an article I wrote:
Forgot Password
The user enters their email in the forgot password
form, and clicks submit. The server (AFM) verifies the account exists
and is not locked. If it is locked, AFM throws an error. Otherwise, an
email is sent to the user with a token. That token is stored in the DB
in the same row/document/record of the user.
Read the full article with more explanations here.
I want to allow users to sign in/up via GitHub using firebase by clicking on the same button.
I create a new authentication for every user in the server side.
With the little piece of code, I'm able to detect if either the user is new or not:
const provider = new firebase.auth.GithubAuthProvider();
firebase.auth().signInWithPopup(provider).then((result) => {
if (result.additionalUserInfo.isNewUser) {
// The user is new
} else {
// The user is old
}
But, when the function signInWithPopup is called, if the user is a new user, a new authentication is automatically created for him. How can I avoid this?
And if the user is already authenticate, how can the user sign in from the client side? Where is the link between the authentication done from the back end with the user that wants to sign in the front end?
This is not how OAuth works. If you use an authentication provider like GitHub, they handle auth flow for you. The only thing that you are left with on the frontend side is an idToken with your identity, basic profile info, and a signature so you can as a user using this token. There's no distinction between sign up/sign in actions.
As you have noticed, Firebase is an extra layer in this flow, it creates an account for a user who signs in for the first time. But there's no user limit or extra payment so I wouldn't bother too much about these extra accounts. You might consider periodical cleanups if you care about the security here.
If you want to actually check if the user exists you have to use firebase-admin e.g. in a Firebase Function before the signInWithPopup is called. But still, unless you want to prevent users from signing up, you can hook your server logic into functions.auth.user().onCreate trigger.
To answer your last question, when the user is already signed in, you'll get the user object in firebase.auth().onAuthStateChanged when a page is loaded. Login state is stored by Firebase.js so once you have called signInWithPopup, you don't need extra steps.
I have a Form to signup new User with email, password, full name and phone number.
I can use firebase.auth().createUserWithEmailAndPassword to create new user if successfully. Then I use firebase.database().ref('users/..').set(..)to save fullname and phone number to firebase database.
But If the saving processing to database occur error (By rule write:false for example), I STILL have an user Identifier created in User Authentication. So an user exist without phone number and full number.
How can I can multi-location insert user authentication and database. If one of these two failed firebase auto reject inserting new identifier and inserting into database. (The processing only return success if both success).
There is no way to make the combined Auth and Database operation atomic. If you want to be atomic, you should use only one service: the database or authentication.
To make this atomic you could pass username, password, full name, and phone number to a HTTP function in Cloud Functions. This then creates a user with the email and password, then writes the full name and phone number to the database.
But note that this still isn't guaranteed to be atomic: the same glitch that you're anticipating on the phone, could happen on the server. While chances are much smaller, there's no guarantee.
This means you'll need to make sure that your code is robust enough to handle cases where the full name and phone number are missing. And if you need to do that in the server-side case, it will also work the same if you'd leave the cross-product code on the client.
I'm building a Meteor app for contracts. A signed in user can create a contract. But I'd like for them to be able to send a contract to another party, who can view the contract without signing up or creating an account. The problem is that, even if the URL is long and random, www.example.com/docs/bM7GjrRq1wABYdxws3 -- I'm not sure it is private enough -- because maybe all these contracts could be easily crawled or end up in search like this.
I'd like the creator of the contract to set a password that can be emailed to the other party along with the link to the contract. Before the party can view the contract, they need to enter the password.
My solution is to allow a user to password protect the page (but without a username/email). But I'm not sure how to do it. How can I password protect a page with a password set by a user?
You could save password within the page object on mongodb.
Then when he creates or edit the page, he might choose to share publicly or with password.
The publish method could look somewhat like this.
Meteor.publish("page", function (){
if (!this.userId) {
return Pages.find({
_id: Session.get("pageId"),
private: true,
password: Session.get("pagePassword")
});
} else {
// code for logged in users
}
});
Of course it's a good idea to store a hash for the password and compare to the hash the user entered, instead of storing the raw password.
I'm not fully understanding the process for processing a 'forgot password' request from the user. Just looking for a little clarification.
On the client you take the user's email and pass that to Accounts.forgotPassword which triggers a call to Accounts.sendResetPasswordEmail on the server. Accounts.forgotPassword only takes email as an argument.
So Accounts.sendResetPasswordEmail is triggered which actually sends the email to the user, but that's looking for a userId...
Just confused by the logic there. Why not just call sendResetPasswordEmail from the client with an email and a userId as arguments?
It's important to note that Accounts.sendResetPasswordEmail can only be called from the server, and is automatically called for you as the result of the call to Accounts.forgotPassword. So in general you don't need to directly call it, however you probably do want to customize the email (from, subject, body) via Accounts.emailTemplates.
Of course, you could call Accounts.sendResetPasswordEmail via a method but consider the most common use case:
I'm a user of the system, but I'm not logged in (no Meteor.userId()). I have an email which is attached to my account. I want to get a reset password email based only on that information. And that's exactly what Accounts.forgotPassword does for you. How convenient!
Note also that by requiring an email on a route that has to be exposed to the public, you are also inherently filtering out baddies which don't know the emails of your users.