Lesson 1
Introduction to User Authentication with Bcrypt
Introduction to User Authentication with Bcrypt

Welcome to an exciting new unit where we will delve into user authentication using Bcrypt within a Laravel application. In this lesson, you will learn how to securely manage user credentials, which is a crucial step in safeguarding any application. Remember, this is fundamental to building secure web applications, and it extends what you may have seen in previous lessons where we've worked with Laravel for developing web functionalities.

What You'll Learn

In this section, we will focus on implementing user authentication through Bcrypt password hashing. This is essential for securing user data and ensuring that only authorized users can access sensitive information.

We'll discuss this topic in this and the next units. This unit solely focuses on the model and database setup, as well as defining routes for registration and login. In the next unit, we'll implement the controller and views to complete the authentication process and protect routes that require authentication.

Let's start by setting up the database and creating the necessary models.

We start by defining the model for the user. This model will be used to interact with the database and manage user data:

php
1// app/app/Models/User.php 2 3<?php 4namespace App\Models; 5use Illuminate\Foundation\Auth\User as Authenticatable; 6use Illuminate\Notifications\Notifiable; 7use Illuminate\Database\Eloquent\Factories\HasFactory; 8 9class User extends Authenticatable 10{ 11 use HasFactory, Notifiable; 12 13 protected $fillable = ['username', 'password']; 14 protected $hidden = ['password']; 15}

The User model extends the Authenticatable class, which provides the necessary methods for user authentication. The fillable property specifies the fields that can be mass-assigned, while the hidden property specifies the fields that should be hidden when the model is serialized.

Next, let's define the migration for the users table:

php
1// app/database/migrations/2014_10_12_000000_create_users_table.php 2 3<?php 4use Illuminate\Database\Migrations\Migration; 5use Illuminate\Database\Schema\Blueprint; 6use Illuminate\Support\Facades\Schema; 7 8class CreateUsersTable extends Migration 9{ 10 public function up() 11 { 12 Schema::create('users', function (Blueprint $table) { 13 $table->id(); 14 $table->string('username')->unique(); 15 $table->string('password'); 16 $table->timestamps(); 17 }); 18 } 19 20 public function down() 21 { 22 Schema::dropIfExists('users'); 23 } 24}

This migration creates a users table with columns for id, username, password, and timestamps.

Finally, let's define the controller and routes for user registration and login:

php
1// app/app/Http/Controllers/UserController.php 2<?php 3namespace App\Http\Controllers; 4use App\Models\User; 5use Illuminate\Http\Request; 6use Illuminate\Support\Facades\Auth; 7use Illuminate\Support\Facades\Hash; 8 9class UserController extends Controller 10{ 11 public function register(Request $request) 12 { 13 $request->validate([ 14 'username' => 'required|unique:users,username', 15 'password' => 'required|min:6', 16 ]); 17 18 $user = User::create([ 19 'username' => $request->username, 20 'password' => Hash::make($request->password), 21 ]); 22 23 Auth::login($user); 24 25 return response()->json(['message' => 'User registered successfully'], 201); 26 } 27 28 public function login(Request $request) 29 { 30 $request->validate([ 31 'username' => 'required', 32 'password' => 'required', 33 ]); 34 35 if (Auth::attempt($request->only('username', 'password'))) { 36 return response()->json(['message' => 'Login successful'], 200); 37 } 38 39 return response()->json(['message' => 'Invalid credentials'], 401); 40 } 41}

Let's examine both methods in the UserController:

  1. The register method validates the request data, creates a new user with the hashed password, logs in the user, and returns a success message.
  2. The login method validates the request data, attempts to authenticate the user, and returns a success message if successful or an error message if the credentials are invalid. The Auth::attempt method is used to authenticate the user. If the user is successfully authenticated, the method returns true, and the user is logged in. Note, that Auth::attempt automatically hashes the password before comparing it to the hashed password stored in the database - and it works with the table column names username and password by default. An alternative approach is to take the user with the provided username and then use the Hash::check method to compare the provided password with the hashed password stored in the database like so:
php
1$user = User::where('username', $request->username)->first(); // get the user with the provided username 2Hash::check($request->password, $user->password); // returns true if the passwords match

Additionally, its important to know that Laravel uses Bcrypt under the hood through its Hash facade. By default, Bcrypt is configured with a cost factor of 10 (2^10 rounds or 1024 iterations), which balances security and performance. You can adjust the cost factor in Laravel's config/hashing.php file.

php
1 'driver' => 'bcrypt', 2 3 'bcrypt' => [ 4 'rounds' => env('BCRYPT_ROUNDS', 10), 5 ]

Rounds: The number of iterations Bcrypt performs during hashing. Increasing this value increases the time required to hash (and verify) a password. Bcrypt applies a unique salt to every password, so even if two users have the same password, their Bcrypt-hashed passwords will be entirely different due to the salt.

Finally, the routes for user registration and login are defined in the routes/web.php file:

php
1// app/routes/web.php 2<?php 3use Illuminate\Support\Facades\Route; 4use App\Http\Controllers\UserController; 5 6Route::get('/register', function () { 7 return view('register'); 8}); 9 10Route::get('/login', function () { 11 return view('login'); 12}); 13 14Route::post('/register', [UserController::class, 'register']); 15Route::post('/login', [UserController::class, 'login']);

Notice, that we have defined routes for /register and /login that render the register and login views respectively. The routes for user registration and login are defined as POST requests that are handled by the register and login methods in the UserController.

Let's additionally take a look at the views for user registration and login:

HTML, XML
1<!-- resources/views/register.blade.php --> 2<!DOCTYPE html> 3<html lang="en"> 4<head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Register</title> 8</head> 9<body class="bg-gray-100"> 10 <div class="container mx-auto mt-10"> 11 <div class="max-w-md mx-auto bg-white p-5 rounded shadow"> 12 <h2 class="text-2xl mb-4">Register</h2> 13 <form action="/register" method="POST"> 14 @csrf 15 <div class="mb-4"> 16 <label for="username" class="block text-sm font-medium text-gray-700">Username</label> 17 <input type="text" name="username" id="username" class="mt-1 block w-full border-gray-300 rounded-md shadow-sm" required> 18 </div> 19 <div class="mb-4"> 20 <label for="password" class="block text-sm font-medium text-gray-700">Password</label> 21 <input type="password" name="password" id="password" class="mt-1 block w-full border-gray-300 rounded-md shadow-sm" required> 22 </div> 23 <div class="mb-4"> 24 <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded">Register</button> 25 </div> 26 </form> 27 </div> 28 </div> 29</body> 30</html>
HTML, XML
1<!-- resources/views/regisloginter.blade.php --> 2<!DOCTYPE html> 3<html lang="en"> 4<head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Login</title> 8</head> 9<body class="bg-gray-100"> 10 <div class="container mx-auto mt-10"> 11 <div class="max-w-md mx-auto bg-white p-5 rounded shadow"> 12 <h2 class="text-2xl mb-4">Login</h2> 13 <form action="/login" method="POST"> 14 @csrf 15 <div class="mb-4"> 16 <label for="username" class="block text-sm font-medium text-gray-700">Username</label> 17 <input type="text" name="username" id="username" class="mt-1 block w-full border-gray-300 rounded-md shadow-sm" required> 18 </div> 19 <div class="mb-4"> 20 <label for="password" class="block text-sm font-medium text-gray-700">Password</label> 21 <input type="password" name="password" id="password" class="mt-1 block w-full border-gray-300 rounded-md shadow-sm" required> 22 </div> 23 <div class="mb-4"> 24 <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded">Login</button> 25 </div> 26 </form> 27 </div> 28 </div> 29</body> 30</html>

Laravel includes CSRF protection by default. The @csrf directive generates a hidden input field with a CSRF token equivalent to this:

HTML, XML
1<input type="hidden" name="_token" value="{{ csrf_token() }}">

This token helps protect your application against Cross-Site Request Forgery (CSRF) attacks, which trick users into performing unauthorized actions. The server validates this token to ensure that form submissions come from trusted sources.

These views are simple forms that allow users to register and login. The forms submit data to the /register and /login routes respectively. With these views, users can register and login to the application.

Why It's Important

Understanding how to implement secure authentication is fundamental for any developer working with web applications. Protecting user data and ensuring that access to sensitive information is only granted to authorized users is critical. By learning how to use Bcrypt with Laravel, you're taking a significant step in developing secure applications. Not only will it safeguard user information, but it will also enhance the overall trustworthiness of your application.

Excited to put this into practice? Let's jump into the exercise section and start implementing what we've learned!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.