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.
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:
php1// 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:
php1// 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:
php1// 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
:
- The
register
method validates the request data, creates a new user with the hashed password, logs in the user, and returns a success message. - 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. TheAuth::attempt
method is used to authenticate the user. If the user is successfully authenticated, the method returnstrue
, and the user is logged in. Note, thatAuth::attempt
automatically hashes the password before comparing it to the hashed password stored in the database - and it works with the table column namesusername
andpassword
by default. An alternative approach is to take the user with the provided username and then use theHash::check
method to compare the provided password with the hashed password stored in the database like so:
php1$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.
php1 '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:
php1// 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, XML1<!-- 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, XML1<!-- 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, XML1<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.
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!