Password Management

Resetting Password

The administrator user was created with a null value for the password hash. Let’s add functionality to enable the user to reset their own password. Although the process is actually resetting the null password hash, from the user’s perspective it is creating the password instead of resetting it.

The password reset process involves several steps:

  • User clicks on the Create Password link in the email.
  • Controller reads the token provided in the link and validates it.
  • If the token is valid, the password reset view is rendered.
  • User types in the new password.
  • AJAX POST request is generated with the token and password as the payload.
  • Controller reads the request payload and invokes User Operations’ hash method.
  • The hash method validates the token passed by the controller and generates a Bcrypt password hash using the Hash utility. User is updated to save the hash and the Key Operations’ delete method is invoked.
  • The delete method deletes the password reset key.

Authentication Template

Authentication template will support all views dealing with authentication functions: password resetting, forgotten passwords, and signing in. The template is based on the Bootstrap Sign In example.

Ctrl-click views and select New - HTML File.

Enter authentication_template.scala.html in the name field.

Double-click HTML 5 file.

If prompted, click Add to add the file to version control.

Browse to the project repository to view the file:

LineDrop Play/Scala Web Application - Authentication Template

Copy the contents of the file and paste them into authentication_template.scala.html replacing the original code.

Reference

Bootstrap - Signin Template

Reset View

Reset view will prompt the user to enter the new password.

Ctrl-click views and select New - HTML File.

Enter reset.scala.html in the name field.

Double-click HTML 5 file.

If prompted, click Add to add the file to version control.

Browse to the project repository to view the file:

LineDrop Play/Scala Web Application - Reset View

Copy the contents of the file and paste them into reset.scala.html replacing the original code.

The view contains two hidden fields which are used in jQuery functions: token and csrf. Both fields’ values are defined by the parameters passed to the view by the controller.

Sign In Stylesheet

The view uses an additional stylesheet signin.css which is shared with the Sign In and Forgot Password views. The stylesheet is a slightly modified version of the Sign In template from Bootstrap.

Expand the public directory and ctrl-click stylesheets and select New - File.

Type in signin.css in the name field and press Enter key.

If prompted, click Add to add the file to version control.

Browse to the project repository to view the file:

LineDrop Play/Scala Web Application - Sign In Stylesheet

Copy the contents of the file and paste them into signin.css.

Reset jQuery

Reset.js is a companion file to the reset view.

Expand the public directory and ctrl-click javascripts and select New - File.

Type in reset.js in the name field and press Enter key.

If prompted, click Add to add the file to version control.

Browse to the project repository to view the file:

LineDrop Play/Scala Web Application - Reset jQuery

Copy the contents of the file and paste them into reset.js.

The top two functions are event handlers to associate the button tap and the Enter key with the validate function.

The validate function checks that the string entered in the reset_password_1 field has at least 6 characters and that the reset_password_2 field contents match the reset_password_1 field. If both conditions are true, the hash function is called.

Hash function creates a structured AJAX request of POST type to a URL /hash/json. The payload of the request is a JSON-formatted string carrying the token and the password.

CSRF token, stored on in a hidden field, is added to the header of the request.

On a successful server response, the proceed layer is displayed and the reset layer is hidden. If an error occurs, the message is printed in the browser’s console.

Expired View

The expired view is rendered by the controller if the reset token is invalid.

Ctrl-click views and select New - HTML File.

Enter expired.scala.html in the name field.

Double-click HTML 5 file.

If prompted, click Add to add the file to version control.

Browse to the project repository to view the file:

LineDrop Play/Scala Web Application - Expired View

Copy the contents of the file and paste them into expired.scala.html replacing the original code.

Key Model

Let’s add two methods to support the password reset process.

Open the Key model and add the validate method.

The validate method uses the Database utility to search a document in the key collection by the token. If a document is found, an instance of Key is created and Some(Key) is returned. Otherwise, None is returned.

We’ll also need a delete method to remove the key once a password has been created or reset. Add the method to the Key model.

The delete method deletes the key document using the Database utility.

Example: LineDrop Play/Scala Web Application - Key Model

Session Model

Session model manages user session objects.

Ctrl-click models and select New - Scala Class.

Enter Session in the name field and double-click Object.

If prompted, click Add to add the file to version control.

Add the following import statements below the package declaration:

Delete the current Session class definition and replace it with the case class:

Add SessionOperations object:

Inside the object, add two private values:

Collection defines the database collection session, where the model data is stored, and log defines an instance of the Logger.

Add the create method:

The create method accepts the user object as a parameter and instantiates a new session object defined by user email with the random string as a token and a current timestamp. Random and TimeStamp utilities are used.

MondoDB document is composed and inserted into the session collection using the Database utility and a Session instance is returned.

Example: LineDrop Play/Scala Web Application - Session Model

User Model

Play Framework provides a nifty device called a JSON Reads Converter for mapping POST requests carrying JSON data directly into case classes. To use JSON Reads, the case class parameters must have the same name and data type as the key/value pairs in the JSON-formatted payload.

Open the User model and add a UserHashForm case class to support the JSON formatted data in the POST request.

The UserHashForm case class definition matches the data payload in the AJAX request generated by Reset jQuery:

Add a method for reading user data from users collection.

The read method accepts an email string as a parameter and uses the Database utility to search the users collection by the email address. If a document is found, a User object is instantiated and Some(User) is returned.

During instantiation, if the hash is null, it is populated with an empty string to conform to the User case class. If no document is found, None is returned.

Add a method to update the user.

The update method accepts a User object as a parameter. MongDB document with user data is composed and saved in the users collection using the Database utility.

Add another method to hash user passwords.

The hash method accepts the token and passwords as parameters and returns Option[User]. The token’s existence is verified by the Key Operations’ validate method. If the key exists, a hash is created from the password using the Hash utility.

User is retrieved by the key’s email property using the read method. If a user with that email address exists, the user is updated, storing the password hash.

The key is deleted with the Key Operations’ delete method and Some(User) is returned. If any of the conditions fail, None is returned.

Example: LineDrop Play/Scala Web Application - User Model

Reference

Scala Json Combinators - 2.8.x

Authentication Controller

Authentication controller provides support for signing in, issuing sessions, creating new passwords and resetting the forgotten ones.

Ctrl-click controllers and select New - Scala Class.

Enter AuthenticationController in the name field and double-click Class.

If prompted, click Add to add the file to version control.

Add the following import statements below the package declaration:

Delete the current AuthenticationController class definition and replace it with the following:

Inside the class, add a JSON Reads converter to map the request data to a UserHashForm instance.

Add the reset method to read the token and render the view.

The method reads the passed token string and uses Key Operations to validate the key.

If the key is valid, the controller renders the Reset view passing the key’s token and a CSRF token as parameters.

If the key is invalid, the Expired view is rendered. Add a method to handle the AJAX POST request.

Authentication Controller’s hash_json method reads the POST request and maps the JSON payload into a UserHashForm class. User Operations' hash is invoked passing the token and password. If the hash is successful, a session token is read from the session object created by Session Operations.

The session token is passed to the client using the withSession modifier, which stores the session token as a key/value pair in a browser cookie. We will learn how to use the cookie to authenticate a user later in the guide.

Example: LineDrop Play/Scala Web Application - Authentication Controller

Reference

Scala Session Flash - 2.8.x

Routing

Finally, let’s route the URL to the controller method.

Open the routes file from the project root. Add entries for a GET request to /reset/token and a POST request to /hash/json.

Test

Before testing, please make sure that the database and the application are running.

Application Response

Open the Welcome email that you’d received from the system and click on the Create Password link. The link will be SendGrid’s tracking link but it will redirect to http://localhost/reset/token, where token is an alphanumeric string.

If, for some reason, you do not have the email, you can view the token by opening MongoDB Compass and viewing the keyscollection.

Test the view by leaving the fields blank, by entering a password that is less than 6 characters, and by re-entering a password that does not match the contents of the first field. In the future you can require more complex passwords and apply regex syntax to verify the complexity.

Finally enter a valid password in both fields, you should see a Thank you message.

If you click on the Create Password link in the Welcome email again, the application will redirect you to the Expired view since the original key has been used and deleted.

Log

Logger will have printed the debug messages in the Terminal window. The log has also been written to /logs/application.log in the project’s directory.

Database Records

Launch MongoDB Compass.

When prompted, enter mongodb://localhost as the connection string and click Connect.

Click sample database.

Click on users in the left panel to view the collection. The administrator user should now have a complex random string stored in the hash field.

Click on keys in the left panel. The collection is now empty because the reset key has been deleted.

Click on sessions in the left panel. The collection will contain a session created for the user.

Review

Let’s review the process.

...

When the Create Password link is clicked, SendGrid redirects the request to http://localhost/reset/token. The router matches the URL mapping and invokes the Authentication Controller’s reset method, passing the token. The token is verified by the Key Operations’ validate method and the Reset view is rendered. If the token is invalid, the controller displays the Expired view.

...

Password is entered in both fields in Reset view and the button is pressed. Password strings are checked by the validate method and the hash method is called in the jQuery script. An AJAX request is made to /hash/json carrying the token and password data in JSON format.

The router matches URL syntax to route definition and invokes Authentication Controller's hash_json method. The Authentication Controller’s hash_json method reads the POST request and maps the JSON payload into a UserHashForm class using a JSON Reads converter. User Operations' hash method is invoked passing token and password. If the hash is successful, the updated user is returned and a session token is read from the session object created by Session Operations. Session is stored in the database.

...

During the hashing operation, the key is validated again. If a key is valid, the Hash utility creates a salted hash of the password string. User is read by the email address and then updated, saving the hash. The key is then deleted.

Commit and Push Changes

Select VCS - Commit from the top menu.

Review the files and directories and enter the commit message.

Click the dropdown arrow on the Commit button and select Commit and Push.

Click Push to confirm.

Forgotten Password

Let’s add a way for the user to initiate the password reset process in the case a password was forgotten.

Forgot View

The view provides the email input field and uses the companion jQuery to send a request to the controller, initiating the password reset process.

Ctrl-click views, and select New - HTML File.

Enter forgot.scala.html in the name field.

Double-click HTML 5 file.

If prompted, click Add to add the file to version control.

Browse to the project repository to view the file:

LineDrop Play/Scala Web Application - Forgot View

Copy the contents of the file and paste them into the file replacing the original code.

Controller provides the CSRF token used in the AJAX request. The view uses the Authentication template and the Sign In stylesheet.

Forgot jQuery

Forgot.js is a companion file to the Forgot view.

Expand the public directory and ctrl-click javascripts and select New - File.

Type in forgot.js in the name field and press Enter key.

If prompted, click Add to add the file to version control.

Browse to the project repository to view the file:

LineDrop Play/Scala Web Application - Forgot jQuery

Copy the contents of the file and paste them into forgot.js.

At the top of the file, the button event and Enter key event handlers are defined.

When the Request button is clicked, the validate function verifies the email input. If the input is valid, the forgot function is called.

The forgot function creates a structured AJAX request of POST type to a URL /forgot/json. The payload of the request is a JSON-formatted string carrying the email address. CSRF token is added to the request header. After the request, the button is hidden and the proceed message is displayed.

User Model

Add a case class to handle the JSON data sent by the Forgot jQuery.

Add the send_reset_link method to create a password reset key and email the user.

The method uses the Key Operations to create a password reset key. The key's expires member value is set to true. Later, we will add functionality to delete the keys after a set number of hours as defined in the key.duration configuration setting.

The email is composed and sent using the SendGrid utility.

Example: LineDrop Play/Scala Web Application - User Model

Authentication Controller

Add a JSON Reads converter to map the JSON data to UserForgotForm.

Add a method to display the Forgot view.

Add a method to handle the AJAX request.

The method maps the JSON-formatted data from the request to an instance of the UserForgotForm object.

User is retrieved with the User Operations’ read method. If the user exists, the User Operations’s send_reset_link method is invoked. For security measures, an empty success message is always printed.

Example: LineDrop Play/Scala Web Application - Authentication Controller

Routing

Add new application routes.

Test

Before testing, please make sure that the database and the application are running. Keep the terminal window with SBT visible so you can see the log.

Application Response

Browse to http://localhost:9000/forgot.

Test input validation by entering incorrect email format or leaving the field blank. Enter a valid email and click Request.

The action will trigger the password reset process and, if the user with this email address exists, the system will send an email with password reset instructions.

For security measures, the application will not disclose user information. It will not report if the user does not exist, and it will not print the email address if the user does exist.

Database

Open the keys collection using MongoDB Compass. The newly created key will have a timestamp in the expires field which is some time in the future. This timestamp will be used by an automated task to delete the expired keys.

Email

Open the password assistance email and click the Reset Password link.

Once the Reset Password link is clicked, the password reset process takes over. It is the same process used in creating new passwords.

Log

Logger will have printed debug messages in the Terminal window. The log has also been written to /logs/application.log in the project’s directory.

Review

...

When the Request button is clicked, the input is validated. The Forgot function creates a structured AJAX request of POST type to a URL /forgot/json.

The request is forwarded by the router to the Authentication Controller’s forgot_json method which validates the user. If the user exists, User Operations’ send_reset_link is invoked. The send_reset_link method uses the Key Operations’ create method to create a new key, set to expire after some amount of time. An email, containing the reset link, is composed and sent to the user with the SendGrid utility.

Commit and Push Changes

Select VCS - Commit from the top menu.

Review the files and directories and enter the commit message.

Click the dropdown arrow on the Commit button and select Commit and Push.

Click Push to confirm.


Next: User Authentication