Results 1 to 7 of 7

Thread: Web Development - Forgot Password

  1. #1

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,715

    Web Development - Forgot Password

    I am developing a web application with vanilla HTML/CSS/JavaScript on the front-end and PHP on the back-end (hello early 2000s!).

    I have authentication setup whereby the user submits a login request, a JSON web token is returned, and the respective token is persisted in local storage. Nothing fancy.

    Now I'm at the point where I need to implement a forgot password (aka - password reset, password recovery, etc.), but before I begin I wanted to make sure that I understand how the overall concept works. Could y'all confirm or provide feedback on how to improve what I'm thinking?

    This is what I have in mind:
    1. Create a table named PasswordReset in my database with the following fields:
      1. PasswordResetId: non-nullable, auto-incremented, primary key
      2. UserId: non-nullable, foreign key referencing the User table
      3. Token: non-nullable, varchar
      4. CreatedOn: non-nullable, timestamp (aka DateTimeOffset in SQL Server)
      5. IsValid: non-nullable, bit
    2. The user submits a request to the forgot password endpoint, providing their email address
    3. The forgot password endpoint does the following:
      1. Query the User table by the given email address
        1. If the user doesn't exist, return a 404
      2. Generate a random string
      3. Create a new record in the PasswordReset with the following values:
        1. UserId: result from step 3a
        2. Token: result from step 3b
        3. CreatedOn: right now
        4. IsValid: 1
      4. Email the user at the given email address with a link that contains the token from step 3b as a query string parameter
      5. Return a 201


    Then once the user clicks on the link in the email, it allows the user to set a new password by:
    1. Confirming the password and confirm password values match
      1. If the values do not match, return a 500
    2. Query the PasswordReset table by the given token
      1. If the record doesn't exist, return a 404
      2. If the record exists, but the IsValid is 0, return a 403
      3. If the record exists, the IsValid is 1, but the CreatedOn + (some amount of time) is greater than right now, return a 403
    3. Query the User table by the UserId from step 2
      1. If the user doesn't exist, return a 404
    4. Update the password for the user from step 3
    5. Update the record from step 2 so that the IsValid is 0
    6. Return a 204


    A quick note - I am salting/hashing passwords I just did not include the details of that above for brevity.

    So with all of that being said... is this more or less correct?
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  2. #2
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Web Development - Forgot Password

    In my opinion, the user should have to provide the email address again when they create the new password and you should check that that matches up with the token in the URL. If you don't then, in theory, anyone could guess a token and reset the password for an account that they have no connection with. By requiring them to enter the email address that the token was sent to, you can at least be confident that they have control of that email account.

  3. #3
    King of sapila
    Join Date
    Oct 2006
    Location
    Greece
    Posts
    6,597

    Re: Web Development - Forgot Password

    Seems OK to me + taken JMC suggestions, I would have changed 2 things:
    First I would have created the PK on PasswordReset table with UserID + Token since you don't show any actual use of the PasswordResetId field, this is for better indexing if the table gets large and for a select on userid + token since that will make the select unique, so "2. Query the PasswordReset table by the given token and userid", you don't need to do it if you don't have a lot of users or don't query like this to the table.
    Secondly I would not check if a user exists, that would mean that the users gets deleted rather than updated to active/ non active , so I would use a userisactive in the user table. It's not a good idea to delete from user tables, rather you should mark them as active / inactive, that is a general rule of thumb in user tables, later on, you may do a batch delete if you want with a datefrom value.
    Last edited by sapator; Jan 28th, 2021 at 12:48 AM.
    ἄνδρα μοι ἔννεπε, μοῦσα, πολύτροπον, ὃς μάλα πολλὰ
    πλάγχθη, ἐπεὶ Τροίης ἱερὸν πτολίεθρον ἔπερσεν·

  4. #4

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,715

    Re: Web Development - Forgot Password

    @JMcIlhinney - I think from a practical standpoint it wouldn't matter. Not only would they have to guess a random token value (that I intend on making 32 characters long) but even if they did guess it correctly they would have to also know (or also guess) the user's email, have the token coincide with the email, and do all of that before the token expires.

    To me the chances of that happening are astronomical. However, your suggestion is trivial to implement, so I may go ahead and implement it.

    @sepator - I will have to agree to disagree with you on using composite primary keys. At work, I'm in the middle of doing a legacy conversion project from VB6 to C# and very many of our challenges have been that the original developers used composite primary keys. Just as a personal matter, I feel like the cons outweigh the pros when it comes to composite primary keys.

    With regards to your second point, I think that you misunderstood the process. 3.a.i. of the first process indicates that the SQL query was unable to find a user by a given email address, e.g. the user entered "uesr@email.com" instead of "user@email.com". 3.a of the second process is simply a check for bad data. I am not assuming that any data exists in the database at a given point because any given number of things could potentially happen to corrupt the data.

    Also, for what it is worth, I do not do hard deletes on users. I have an audit field named DeletedOn that is a timestamp. If the field is not null then the record is treated as "deleted".
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  5. #5
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Web Development - Forgot Password

    Quote Originally Posted by dday9 View Post
    even if they did guess it correctly they would have to also know (or also guess) the user's email
    There's nothing in your algorithm that requires that and that's my point. Your algorithm requires them only to navigate to a URL containing a valid token, which you then use to get the corresponding UserId. At no point do you require them to actually know what user they're resetting the password for. My suggestion is that you add the requirement that you claim you already have but, unless I'm missing something, you do not.

    That is, unless you mean that the URL in the link they click contains both the token and the email address. There's nothing in your algorithm that indicates that so, if I were implementing it, I wouldn't have included that. If that is the case then you'd need that confirmation/validation step included in your algorithm at least. Personally, I would tend not to include an email address in a URL like that but, rather, require it to be input manually. Others may disagree.

    Either way, just as you say, there's little chance that anyone would guess a valid token in the first place but it is better to be safe than sorry when implementation is simple and, in my experience, users don't find such a change onerous.

  6. #6
    PowerPoster kfcSmitty's Avatar
    Join Date
    May 2005
    Posts
    2,248

    Re: Web Development - Forgot Password

    Most things seem to be covered here, but I also wouldn't give a not found error if the email doesn't exist. There should be no way for someone to see if an email exists in your system if they don't own it.

    Just phrase it like "if your email exists in our system, you will receive a password reset request" or something.

    I also wouldn't return a 500 if the passwords don't match, I would return a 400. Your server is not having an interal server error, the data passed with the request was invalid.

  7. #7
    New Member
    Join Date
    May 2020
    Posts
    3

    Re: Web Development - Forgot Password

    New section after "Then once the user clicks on the link in the email", I would add the logic
    + If user logs in normally (existing id/pw), destroy the temporary token(s).

    Second, if you only require the link, email address and reset token, you don't have any real secret. Anyone that intercepted the email would have all the credentials to commandeer the account.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width