Password Management - Reset Password - Part 4


In this video we are going to finish off the Password Reset functionality by adding in all the "unhappy paths".

At this stage there should be no new code to write.

As we are making use of the existing FOSUserBundle controller action implementations, and by the translation strings also provided inside FOSUserBundle, all we need to do here is to make sure the outcomes are as expected.

Also, we do not need to write any additional Behat step definitions, as again we are making use of existing functionality within our RestApiContext.

One thing to note here is that these tests have been updated to address a Security Fix discussed in this video write up.

Aside from this, these tests are pretty much as we have covered in other "unhappy path" videos in this course.

For reference, here is the feature in full:

# /src/AppBundle/Features/password_reset.feature

Feature: Handle password changing via the RESTful API

  In order to help users quickly regain access to their account
  As a client software developer
  I need to be able to let users request a password reset

  Background:
    Given there are Users with the following details:
      | uid | username | email          | password | confirmation_token |
      | u1  | peter    | peter@test.com | testpass |                    |
      | u2  | john     | john@test.org  | johnpass | some-token-string  |
    And I set header "Content-Type" with value "application/json"

  ############################
  ## Password Reset Request ##
  ############################
  Scenario: Cannot request a password reset for an invalid username
    When I send a "POST" request to "/password/reset/request" with body:
      """
      { "username": "davey" }
      """
    Then the response code should be 403
    And the response should contain "User not recognised"

  Scenario: Can request a password reset for a valid username
    When I send a "POST" request to "/password/reset/request" with body:
      """
      { "username": "peter" }
      """
    Then the response code should be 200
    And the response should contain "An email has been sent. It contains a link you must click to reset your password."

  Scenario: Cannot request another password reset for an account already requesting, but not yet actioning the reset request
    When I send a "POST" request to "/password/reset/request" with body:
      """
      { "username": "john" }
      """
    Then the response code should be 403
    And the response should contain "The password for this user has already been requested within the last 24 hours."

  ############################
  ## Password Reset Confirm ##
  ############################

  Scenario: Cannot confirm without a token
    When I send a "POST" request to "/password/reset/confirm" with body:
      """
      { "bad": "data" }
      """
    Then the response code should be 400
    And the response should contain "You must submit a token"

  Scenario: Cannot confirm with an invalid token
    When I send a "POST" request to "/password/reset/confirm" with body:
      """
      { "token": "invalid token string" }
      """
    Then the response code should be 400

  Scenario: Cannot confirm without a valid new password
    When I send a "POST" request to "/password/reset/confirm" with body:
      """
      {
        "token": "some-token-string",
        "plainPassword": {
          "second": "first-is-missing"
        }
      }
      """
    Then the response code should be 400
    And the response should contain "The entered passwords don't match"

  Scenario: Cannot confirm with a mismatched password and confirmation
    When I send a "POST" request to "/password/reset/confirm" with body:
      """
      {
        "token": "some-token-string",
        "plainPassword": {
          "first": "some password",
          "second": "oops"
        }
      }
      """
    Then the response code should be 400
    And the response should contain "The entered passwords don't match"

  Scenario: Can confirm with valid new password
    When I send a "POST" request to "/password/reset/confirm" with body:
      """
      {
        "token": "some-token-string",
        "plainPassword": {
          "first": "new password",
          "second": "new password"
        }
      }
      """
    Then the response code should be 200
    And the response should contain "The password has been reset successfully"
    And I send a "POST" request to "/login" with body:
      """
      {
        "username": "john",
        "password": "new password"
      }
      """
    Then the response code should be 200
    And the response should contain "token"

This covers the expected good and bad paths through our Password Reset and Password Reset Confirmation journeys.

However, there is a gotcha' to this setup:

Because we are not using the "normal" Twig templates etc, we are going to have to implement an intermediate page in our front end to handle the updating of the User's password.

In the standard FOS User Bundle setup, this would be done by generating a link to a page that contains a form for new password entry. This link would also contain the generated password reset token.

We can't do this.

Well, we can, but we can't generate a link to a GET request that displays a form... it won't work like that.

Instead, we need to send our User to somewhere on the front end, passing along the token, and then expecting that token and the new password information to be POST'ed back.

Whilst not demonstrated here, this topic will be covered in the front end series that makes use of this back end.

Code For This Course

Get the code for this course.

Episodes