User Login Authentication

DSE Version: 6.7

Intro

Exercises

Slides

Resources

We created a couple of user-related tables and inserted some user rows. In this unit, we'll see how to access one of these tables using Java code.

No write up.

Exercise: User Login Authentication

In this exercise, you will:

  • Launch the KillrVideo website and try to login – which won’t work just yet

  • Build database access layer code in a sandbox

  • Integrate the code from the sandbox into the KillrVideo web app

In the previous exercise, we created the user_credentials table which we will use for authenticating users. We also added some users to the table. In this exercise, we’ll see how to access this table from the KillrVideo web app. Note that we are still using plain-text passwords in the table. We know that in production we will use a hash of the password instead of the plain-text, but for now, let’s ignore this detail and focus mainly on accessing the table from the app.

Steps

1) Start the KillrVideo app. In Che, you have already seen that you have a killrvideo project. This project has the code for the reference app with much of the data access layer stubbed out. In these exercises we will begin to fill in the code for these stubs to make the app work.

Let's start by seeing the current state of the app. Run the app by clicking the blue dropdown arrow and selecting Run KillrVideo:

Once you Run KillrVideo, you will see a new tab appear in the lower section of Che that is the console/log for the KillrVideo process. Initially this log indicates Che is building the project. But eventually you will see Che launch the process. Once you see a log message like the following, your web app is up and running and you can proceed to the next step.

2) Access your KillrVideo website. Open another browser tab and access your KillrVideo website. The website has the same IP as your workstation, but it is on port 3000:

http://<your IP address goes here>:3000

When you get there, you will see something like this:

3) Go to the sign in dialog. Click on the SIGN IN tab:

4) Try to sign in. Fill out the Sign In dialog using the credentials for the user you created in the previous exercise and click on the Sign In button at the bottom:

Need to see the users in the user_credentials table? Click here.
If cqlsh is not running, you can launch it with the following (remember the password is KVPassword):
cqlsh --username KVUser --cqlshrc /projects/creds/cqlshrc

In cqlsh execute the following query:

SELECT * FROM killrvideo.user_credentials;

Even though your credentials are correct, you will see the following:

This is because we (meaning you) have not yet implemented the code to access the database and verify your credentials.

5) Open the data layer file. Within the killrvideo project in Che, drill down through the folders and open the UserAccess.java file (killrvideo/src/main/java/killrvideo/dataLayer/UserAccess.java):

6) Compare the code to the log output. Inspect the code in this file. You see this class consists of three stubbed out methods. Notice that each method outputs debug log messages. These messages indicate when the app enters the method.

  public static boolean createNewUser(DseSession session, 
    String password, User user) throws Exception {
    LOGGER.debug("------Start createNewUser------");
    return false;
  }

  public static UUID getAuthenticatedIdByEmailPassword(DseSession session,
    String email, String password) throws Exception {
    LOGGER.debug("------Start getAuthenticatedIdByEmailPassword------");
    return null;
  }

  public static User getUserById(DseSession session, 
    UUID userid) throws Exception {
    LOGGER.debug("------Start getUserById------");
    return null;
  }

Look back at the Run KillrVideo log window. When you inspect the bottom of the log, you will see an entry indicating that the server invoked the getAuthenticatedIdByEmailPassword() when you clicked the Sign In button:

Also notice that the service code provides the DseSession as the first parameter to each method. This is the one and only instance of DseSession in the entire application. Remember...good practices. Yeah. You rock.

7) Set up a scratchpad version of the code. We need to fill in the code for a couple of these methods to make the login functionality work. In this exercise, we'll start with the getAuthenticatedIdByEmailPassword() method. But to simplify our lives, let's create this method first in the scratchpad project. Remember that  ScratchPad.java is over here (scratchpad/src/main/java/com.datastax.academy.cloud.demo/ScratchPad.java):

Once we have getAuthenticatedIdByEmailPassword() working in scratchpad, we will paste it into the killrvideo project. Replace the entire contents of ScratchPad.java with the code below. Notice there are three test cases in main():

  1. Valid user/password (should return a valid userid) 
  2. Invalid user/password (should return null)
  3. Valid user + invalid password (should return null)

Notice two things:

  • If you did not create a moo@you.com user with password moo, adjust the tests accordingly.
  • We also added the SessionManagement code in main() as discussed in the slide deck.
package com.datastax.academy.cloud.demo;

import java.io.File;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import com.datastax.oss.driver.api.core.cql.BatchStatement;
import com.datastax.oss.driver.api.core.cql.BoundStatement;
import com.datastax.oss.driver.api.core.cql.DefaultBatchType;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder;
import com.datastax.dse.driver.api.core.DseSession;
import com.datastax.oss.driver.api.core.DefaultConsistencyLevel;
import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
import com.datastax.oss.driver.api.querybuilder.insert.RegularInsert;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ScratchPad {
  static final Logger LOGGER = LoggerFactory.getLogger(ScratchPad.class);

  public static UUID getAuthenticatedIdByEmailPassword(DseSession session,
    String email, String password) throws Exception {
    LOGGER.debug("------Start getAuthenticatedIdByEmailPassword------");

    //-----------------------------------------------------------------------------
	// Create a string to SELECT the user from user_credentials based on email
	// TBD: Create the SELECT command string that selects from user_credentials:
    //-----------------------------------------------------------------------------
	String command = 
    // Create the SimpleStatement that combines the command with the email:
    SimpleStatement statement = SimpleStatement.newInstance(command, email);
	// Execute the statement and get the result set
	ResultSet meResultSet = session.execute(statement);
	// Get the row from the result set
	Row meRow = meResultSet.one();
	// Create a UUID for the returned user ID and initialize it to null
	UUID userId = null;
	// If the row exists,
	if (meRow != null) {
		// Get the password value from the row
		String passwordFromDB = meRow.getString("password");
		// If the password value from the row equals password parameter,
		if (password.equals(passwordFromDB)) {
			// Set the returned user ID to the row’s user ID
			userId = meRow.getUuid("userid");
		}
	}

	// Return the user ID
	return userId;
  }

  public static void main(String[] args) {
    LOGGER.info("Starting main()...");
    try {
      SessionManagement.initSession();
      DseSession session = SessionManagement.getSession();
        
      // Valid credentials test
      UUID userId1 = getAuthenticatedIdByEmailPassword(session, "moo@you.com", "moo");
      LOGGER.info("userId1 = " + userId1);

      // Invalid user name and invalid password test
      UUID userId2 = getAuthenticatedIdByEmailPassword(session, "foo@you.com", "foo");
      LOGGER.info("userId2 = " + userId2);

      // Valid user name and invalid password test
      UUID userId3 = getAuthenticatedIdByEmailPassword(session, "moo@you.com", "zoo");
      LOGGER.info("userId3 = " + userId3);
    
    }
    catch (Exception e) { e.printStackTrace(); }
    finally { SessionManagement.closeSession(); }
    System.exit(0);
  }
}

8) Fill in the SELECT command. Notice there is a TBD in the getAuthenticatedIdByEmailPassword() method reminding you to complete the initialization of the String command on the following line. The code uses this string in the SimpleStatement to perform a SELECT on the user_credentials table based on the email (which is a parameter to the method). Fill in this rest of this line (remember that you use a ? to indicate where the SimpleStatement will substitute the email value):

Are you a little lost? Click here for the SELECT command code.
String command = "SELECT * FROM killrvideo.user_credentials WHERE email = ?";
Need the entire finished ScratchPad.java file? Click here.
package com.datastax.academy.cloud.demo;

import java.io.File;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import com.datastax.oss.driver.api.core.cql.BatchStatement;
import com.datastax.oss.driver.api.core.cql.BoundStatement;
import com.datastax.oss.driver.api.core.cql.DefaultBatchType;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder;
import com.datastax.dse.driver.api.core.DseSession;
import com.datastax.oss.driver.api.core.DefaultConsistencyLevel;
import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
import com.datastax.oss.driver.api.querybuilder.insert.RegularInsert;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ScratchPad {
  static final Logger LOGGER = LoggerFactory.getLogger(ScratchPad.class);

  public static UUID getAuthenticatedIdByEmailPassword(DseSession session,
    String email, String password) throws Exception {
    LOGGER.debug("------Start getAuthenticatedIdByEmailPassword------");

    //-----------------------------------------------------------------------------
	// Create a string to SELECT the user from user_credentials based on email
	// TBD: Create the SELECT command string that selects from user_credentials:
    //-----------------------------------------------------------------------------
	String command = "SELECT * FROM killrvideo.user_credentials WHERE email = ?"; 
    // Create the SimpleStatement that combines the command with the email:
    SimpleStatement statement = SimpleStatement.newInstance(command, email);
	// Execute the statement and get the result set
	ResultSet meResultSet = session.execute(statement);
	// Get the row from the result set
	Row meRow = meResultSet.one();
	// Create a UUID for the returned user ID and initialize it to null
	UUID userId = null;
	// If the row exists,
	if (meRow != null) {
		// Get the password value from the row
		String passwordFromDB = meRow.getString("password");
		// If the password value from the row equals password parameter,
		if (password.equals(passwordFromDB)) {
			// Set the returned user ID to the row’s user ID
			userId = meRow.getUuid("userid");
		}
	}

	// Return the user ID
	return userId;
  }

  public static void main(String[] args) {
    LOGGER.info("Starting main()...");
    try {
      SessionManagement.initSession();
      DseSession session = SessionManagement.getSession();
        
      // Valid credentials test
      UUID userId1 = getAuthenticatedIdByEmailPassword(session, "moo@you.com", "moo");
      LOGGER.info("userId1 = " + userId1);

      // Invalid user name and invalid password test
      UUID userId2 = getAuthenticatedIdByEmailPassword(session, "foo@you.com", "foo");
      LOGGER.info("userId2 = " + userId2);

      // Valid user name and invalid password test
      UUID userId3 = getAuthenticatedIdByEmailPassword(session, "moo@you.com", "zoo");
      LOGGER.info("userId3 = " + userId3);
    
    }
    catch (Exception e) { e.printStackTrace(); }
    finally { SessionManagement.closeSession(); }
    System.exit(0);
  }
}

9) Run the tests. You should be able to run the scratchpad project and inspect the log results for the three tests. Launch scratchpad as shown:

The first test should log a valid user ID. The second and third tests should log null for the user ID.

10) Move the tested code to production. Once your getAuthenticatedIdByEmailPassword() method is working as shown by your scratchpad project, copy the method back into UserAccess.java in the killrvideo project (replace the getAuthenticatedIdByEmailPassword() method stub with the fully implemented method).

Need the solution code for UserAccess.java? Click here.
package killrvideo.dataLayer;

import java.io.File;
import java.util.UUID;
import java.time.Instant;

import com.datastax.oss.driver.api.core.cql.BatchStatement;
import com.datastax.oss.driver.api.core.cql.BoundStatement;
import com.datastax.oss.driver.api.core.cql.DefaultBatchType;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder;
import com.datastax.dse.driver.api.core.DseSession;
import com.datastax.oss.driver.api.core.DefaultConsistencyLevel;
import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
import com.datastax.oss.driver.api.querybuilder.insert.RegularInsert;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import killrvideo.entity.User;

public class UserAccess {

  private static final Logger LOGGER = LoggerFactory.getLogger(UserAccess.class);
  
  public static boolean createNewUser(DseSession session, 
    String password, User user) throws Exception {
    LOGGER.debug("------Start createNewUser------");
    return false;
  }

  public static UUID getAuthenticatedIdByEmailPassword(DseSession session,
    String email, String password) throws Exception {
    LOGGER.debug("------Start getAuthenticatedIdByEmailPassword------");

    //-----------------------------------------------------------------------------
	// Create a string to SELECT the user from user_credentials based on email
	// TBD: Create the SELECT command string that selects from user_credentials:
    //-----------------------------------------------------------------------------
	String command = "SELECT * FROM killrvideo.user_credentials WHERE email = ?"; 
    // Create the SimpleStatement that combines the command with the email:
    SimpleStatement statement = SimpleStatement.newInstance(command, email);
	// Execute the statement and get the result set
	ResultSet meResultSet = session.execute(statement);
	// Get the row from the result set
	Row meRow = meResultSet.one();
	// Create a UUID for the returned user ID and initialize it to null
	UUID userId = null;
	// If the row exists,
	if (meRow != null) {
		// Get the password value from the row
		String passwordFromDB = meRow.getString("password");
		// If the password value from the row equals password parameter,
		if (password.equals(passwordFromDB)) {
			// Set the returned user ID to the row’s user ID
			userId = meRow.getUuid("userid");
		}
	}

	// Return the user ID
	return userId;
  }
  public static User getUserById(DseSession session, 
    UUID userid) throws Exception {
    LOGGER.debug("------Start getUserById------");
    return null;
  }
}

11) Bounce the web app. Now, we are ready to test our method in the KillrVideo app. We need to bounce (i.e., stop and restart) the web app for our changes to take effect. If the web app is still running, you can stop it by clicking on the X of the console/log window. Then restart the server.

12) Try to sign in again. Use the tab opened to the KillrVideo app (the one accessing port 3000) and refresh the main page of the app. Then try and sign in again with correct credentials. After a while, you should see the image below.

Need to see the users in the user_credentials table? Click here.
If cqlsh is not running, you can launch it with the following (remember the password is KVPassword):
cqlsh --username KVUser --cqlshrc /projects/creds/cqlshrc

In cqlsh execute the following query:

SELECT * FROM killrvideo.user_credentials;

13) Diagnose the problem. Before you worry too much, take a look at the log. You will probably see something like the following:

Notice the log message that says "Boom shakalaka we in!". This tells us that the credentials actually validated correctly. Also notice the message "Start getUserById" followed by the stack trace. You guessed it! This is the next method we (meaning you) need to implement. We'll take a look at this in the next exercise.

END OF EXERCISE

No FAQs.

User login authentication: implement user login authentication for your application.

Comments are closed.