Skip to main content

CSC105 Backend: Authentication & Cookie

From the last session, we all have done the API endpoint implementation using Node.js to do database operations from API calling. Did we spot some missing part of our API? How can we separate operations and permission based on each user?

Authentication

Most of website needs authentication for user to signup their account and sign back in at anytime they want to access the data. Most of authentication terminology is use Username (oe Email) and Password as credential to login to the account. However, there's other authentication mechanism to learn more here:

  • OAuth (e.g. Sign-in with Facebook/Google/etc.)
  • Signle-Sign-On (SSO)
    • Suggested learn more keywords:
      • what's sso
      • ldap benefit for organization
      • ldap vs saml
      • what's active directory
    • Examples:
    • Key informations:
      • OAuth use redirection of user to the provider's website for grant the access of user's profile and access to the user data (as you seen in the Google Consent Dialog before logging in to the system)
      • SSO is mostly used in organization which user credential are stored on only single source and many of trusted website inside organization can implement the login to access the same account data in the organization without needing to create an account for each service.
      • You can see that many of KMUTT websites (that are in the example) are using SSO for you to use only one KMUTT Account for all these service. And the login flow is done within the website without needing to open Consent Dialog like Signin with Google or any other OAuth login flows.
  • Passwordless Signin

This is just the guide if you interested and want to learn more on alternative authentication mechanism. But the next step after user has authenticated, how can store that this user in this browser has logged in, and who is it. The answer is Cookie

To learn more on what's cookie, just googling "what's web cookie". Next, let's see the cookie in action.

  1. Try to open any website in the browser. For example, https://sqlworkshop.bsthun.com/.
  2. Open DevTools windows (by pressing F12 or Control + Shift + I (Windows) or Option + Command + I (macOS)) and choose Application tab.

    Screenshot 2023-05-01 at 4.57.39 AM.png

  3. Choosing the website origin in the cookie tab, and then you will see what cookie data are stored for that website.

    Screenshot 2023-05-01 at 5.00.57 AM.png

  4. Let's signin and navigate to the website's user-only zone (for example, enrollment page https://sqlworkshop.bsthun.com/enrollment) try delete the user cookie cookie (by click at the user cookie, then press delete on the keyboard) and refresh the website.

    Screenshot 2023-05-01 at 5.05.44 AM.pngYou'll see the error prompt to you that there's some error, and you will not able to get to your account again until re-signin again. It shows that the user cookie stores the logged in session and when you delete it. The system will not able to proof that's you anymore.

  5. Try signin to the website again, the user cookie should show up again. This is the action from the authentication process what after the credential has verified, it will store a cookie for storing the login session on your browser.

Username & Password Login

For backend development, let's continue creating signin endpoints from the lab last week.

Lab Setup

    1. Back to SQL Playground, again enroll the To-Do Dataset (2)Screenshot 2023-05-01 at 7.11.35 AM.png
    2.  Browse the database you enrolled, in the table users you will see pre mock-up password and hashedPassword like this:

      Screenshot 2023-05-01 at 7.08.21 AM.png

    3. Let setup Node.js project for connecting to the database which the connection details provided in the lab enrollment of the todo02 lab, and have express.js setup with a simple endpoint (e.g. GET / and get a Hello World response). Use last week's guide to start a new node.js project. The setup checklist:
      • Have some neccessary dependencies properly installed (express, mysql2, body-parser)
        • const express = require("express");
          const mysql = require("mysql2");
          const bodyParser = require("body-parser");

      • Have MySQL connection initialization and connection
        • const connection = mysql.createConnection({
          	host: "server2.....",
          	port: "61..",
          	user: "lab....",
          	password: "<use your own password>",
          	database: "lab....",
          });
          
          connection.connect(() => {
          	console.log("Database is connected");
          });
      • Have express app instance setup
        • const port = 3000;
          const app = express();
          
          app.use(bodyParser.json({ type: "application/json" }));
          
          app.get("/", (req, res) => {
          	res.send("Hello World!");
          });

    4. Try running your boilerplate of Node.js project setup from step above. Make sure the code can properly connect to the database and start express web server on http://localhost:3000. The common problem you may face is:
      • MODULE_NOT_FOUND: You may forget to install dependencies to your newly created Node.js project. Try running npm i express mysql2 body-parser
      • App shows only database is connected and immediately stopped: You just copied the code from step 3 which is not a complete setup. Go to the Node.js express project guide and see what's missing (Hint: did your app start listening for HTTP connection?).
      • If you still facing problem please call mentor!
    5. If your app is started properly and able to get Hello World response from http://localhost:3000, you're now ready to go!

Basic Authentication

Then, after we have the boilerplate of Node.js Express application, we're going to implement the simple username/password endpoint.

Login implementation

We're implementing the login endpoint functionality by creating a new POST endpoint at the path /login/basic. The concept are shown like this figure:

Untitled-1.png

There's only few steps of the endpoint functionality. The concept is:

  1. Recieve the username and password parameter from the user (as body payload, in this case).
  2. Use the username and password to construct SQL statement.
  3. Query the SQL statement and use the query result for determine whether login credential is match users in the database or not.
    • Hint: try googling "

       

      "
    • Hint #2: when you get the number of rows returned, use that value for if else condition to check whether 0 or 1 rows returned. If 0 row returned, do res.send("Login credential is incorrect"). If 1 row returned,  res.send("Login credential correct").

Let's try implement this login into your login endpoint by yourself first. If you really doubt about it then see the code example: (again, please try on your own first before see the code example!)

app.post("/basic/login", (req, res) => {
	const username = req.body.username;
	const password = req.body.password;

	var sql = mysql.format(
		"SELECT * FROM users WHERE username = ? AND password = ?",
		[username, password]
	);
	console.log("DEBUG: /basic/login => " + sql);
	connection.query(sql, (err, rows) => {
		if (err) {
			return res.json({
				success: false,
				data: null,
				error: err.message,
			});
		}

		numRows = rows.length;
		if (numRows == 0) {
			res.json({
				success: false,
				message: "Login credential is incorrect",
			});
		} else {
			res.json({
				success: true,
				message: "Login credential is correct",
				user: rows[0],
			});
		}
	});
});

Common problem you may found:

  1. The request lasts forever: The database credential or SQL statement may incorrect, so the database query takes forever. Double check your database connection and SQL statement.Screenshot 2023-05-01 at 12.23.28 PM.png
  2. The SQL statement format as null value: the body variable and the body sent from the postman may have different name, so the code read variable that does not exist. Screenshot 2023-05-01 at 12.26.02 PM.png

The result if you properly implement the functionality:

  • Case of incorrect credentialScreenshot 2023-05-01 at 12.21.52 PM.png
  • Case of correct credentialScreenshot 2023-05-01 at 12.29.55 PM.png

Remark: the whole user information should not be returned in the response like this in the real project implementation. We just shown this information just for an example of quering user data after logged in.

Password-hashed Authentication

After we have implemented a basic login endpoint. Have you spot the one security issue in the database?

"Database administrator can view everyone's password"

Practically, the password of a user should keep secret. But as we need to store password of a user for checking at the login endpoint. How can we securly store this information but seal it from database admin to view? The answer is hashing.

Hashing

Again, try googling "what's hashing", you'll know the concept of how hashing works. The brief concept is hashing transform the plain text into a hash value which is irreversible. So database administrator cannot use stored hash value to transform back to original password. However, we can still check for correctness of the password by one property of a hash function, the same input password will be hashed into the same hash value.

image.pngThere's various of hashing algorithm to use for hashing password, you can learn more as follows:

  • MD5
  • Bcrypt
  • Argon
  • Google "password hashing algorithms"

Let's try hashing by example. Getting started with the most simplest one, MD5.

  1. Go to https://emn178.github.io/online-tools/md5.html which is online hashing demonstration tool.
  2. Try insert any original text, for example "HelloWorld".Screenshot 2023-05-01 at 1.02.52 PM.png
  3. You will see "HelloWorld" will be hashed into "68e109f0f40ca72a15e05cc22786f8e6" and there's no way to decode back from the hashed value into "HelloWorld", except you're bruteforcing (Google "what's bruteforce hash value")
  4. If you hash the value of "HelloWorld" again, it should hashed into the same value "68e109f0f40ca72a15e05cc22786f8e6" which we can use for check at the database that the hash value is matched or not.

Hashing Login Implementation

Instead of using the username and password for querying at the database directly, we're query the hashed information from the database and use them for comparing with the recieved password in the body payload.

Untitled-1.pngWe have created some proof-of-concept code of password hashing using bcrypt

const bcrypt = require("bcrypt");

// Hash 12345678
const example = async () => {
	const salt1 = await bcrypt.genSalt(10);
	console.log("Salt #1: ", salt1);
	const hash1 = await bcrypt.hash("12345678", salt1);
	console.log("Hash #1: ", hash1);

	const salt2 = await bcrypt.genSalt(10);
	console.log("Salt #2: ", salt2);
	const hash2 = await bcrypt.hash("asdf12123", salt1);
	console.log("Hash #2: ", hash2);

	const valid1 = await bcrypt.compare(
		"12345679",
		"$2b$10$fwkjdMXyeLb7DGaU2UKwTecPJfC7i3ktBP5pFwC3ov71dMSsehus2"
	);
	console.log("Validation #1: ", valid1);

	const valid2 = await bcrypt.compare(
		"12345679",
		"$2b$10$fwkjdMXyeLb7DGaU2UKwTecPJfC7i3ktBP5pFwC3ov71dMSsehus3" // Modify last charactor a little bit
	);
	console.log("Validation #2: ", valid2);

	const valid3 = await bcrypt.compare(
		"asdf12123",
		hash2 // Previously hgenerated hash
	);
	console.log("Validation #3: ", valid3);
};

example();

Let's run on your own machine and see the relation of console output and the code!

Screenshot 2023-05-01 at 1.30.43 PM.png

Remark: hashed value will not be the same as bcrypt use salt for making hash more secure (Google "bcrypt vs md5") but the validation will we something like this.

Implementation Task

Lastly, try implement new login endpoint using hashed password from the database and payload! it would be better if 

Suggested resources are: