
Table Of Content
1 Prerequisites
- PHP 8.1 or higher — Shield requires PHP 8.1+ for full compatibility. You can check your version by running
php -vin your terminal. - Composer — PHP dependency manager. Install it from getcomposer.org if you haven't already.
- MySQL / MariaDB — Any recent version will work. Shield creates its own database tables via migrations.
- CodeIgniter 4.5+ (latest stable) — We'll install this fresh using Composer in the next step.
- A code editor — VS Code, PhpStorm, or any editor of your choice.
If you're new to CodeIgniter 4, you may want to read our guide on how to build a simple blog in CodeIgniter 4 first to familiarize yourself with the framework basics.
2 Introduction
2.1 Why Use CodeIgniter Shield?
- Built-in bcrypt password hashing — passwords are never stored in plain text
- Session-based authentication with remember-me (persistent cookie) support
- Optional 2FA, magic links, and email activation — easily enable when needed
- Group & permission-based authorization (RBAC) — perfect for role-based login systems
- Easy integration with route filters — protect any route with a single config line
- Actively maintained in 2026 with the latest security patches and PHP 8.x support
Compared to building everything manually, Shield saves hours of development time while being significantly more secure. It handles edge cases like session fixation, brute-force protection, and token management that are easy to get wrong in a custom implementation.
If you're comparing frameworks, see our detailed comparison of CodeIgniter 4 vs Laravel 12 for small projects — CodeIgniter's lightweight approach combined with Shield makes it an excellent choice for authentication-heavy applications.
Why Shield instead of Myth/Auth? Myth/Auth was deprecated in 2022 and is no longer maintained. Shield is the official replacement, actively maintained by the CodeIgniter team with regular security updates. If you're starting a new project in 2026, Shield is the only recommended option. For more on CodeIgniter 4 security, check out our article on securing CodeIgniter 4 from SQL injection.
2.2 What You'll Build
- User Registration — with username, email, and secure password storage (bcrypt)
- User Login — with email/password credentials and "Remember Me" support
- Protected Dashboard — accessible only to authenticated users
- Logout — proper session destruction and redirect
- Flash Messages — success and error feedback for all actions
- CSRF Protection — built-in form security against cross-site request forgery
- Route Filtering — Shield's login filter to protect routes automatically
We'll use Bootstrap 5 for a clean, responsive UI so you can focus on the authentication logic rather than CSS. The complete folder structure and all source code files are provided below.
3 Create / Install a Codeigniter 4 Project
3.1 Install Codeigniter 4 Project
Use the following command to install new Codeigniter Project.
composer create-project codeigniter4/appstarter ci4-auth
Then, navigate to your project directory:
cd ci4-auth
3.2 Configure Environment and MySql Database
env file (in your project root) to .env and update the following settings:Step 1: Set the environment to development mode so you can see detailed error messages during development:
# CI_ENVIRONMENT = production
CI_ENVIRONMENT = development
Step 2: Configure your MySQL database connection:
database.default.hostname = localhost
database.default.database = ci4_auth
database.default.username = root
database.default.password =
database.default.DBDriver = MySQLi
Step 3: Create the database ci4_auth in MySQL using phpMyAdmin, MySQL Workbench, or the command line:
CREATE DATABASE ci4_auth;
Important: Make sure to replace the username and password values with your actual MySQL credentials. Never use root with an empty password in production. For more on securing your CodeIgniter application, read our guide on preventing SQL injection in CodeIgniter 4. 4 Install CodeIgniter Shield
ci4-auth/):
composer require codeigniter4/shield
This downloads Shield and all its dependencies into the vendor/ folder. Composer will also update your composer.json and composer.lock files automatically.Note: Make sure you're running this command inside the
ci4-auth directory, not in your web server root. If you see a "Package not found" error, check your PHP version — Shield requires PHP 8.1 or higher. 5 Publish Shield Configuration & Migrations
php spark shield:setup
This command does several things automatically:
- Publishes Auth.php configuration to
app/Config/ - Updates your
app/Config/Autoload.phpwith Shield's helpers - Adds Shield's auth filter to
app/Config/Filters.php
Next, run the database migrations to create the required tables:
php spark migrate
This creates the necessary tables: users, auth_identities, auth_token_logins, auth_remember_tokens, auth_groups_users, and others.Tip: If the migration fails, double-check that your
.env database settings are correct and that the ci4_auth database exists. You can verify by running php spark db:table after migration to list all created tables. 6 Configure Shield (app/Config/Auth.php)
public array $actions = [
'register' => null, // or EmailActivator::class for email verification
'login' => null,
];
public array $authentication = [
'session' => [
'allowRemembering' => true,
],
];
What these settings mean:$actions['register'] = null— Users can register and login immediately. Set this toEmailActivator::classif you want to require email verification before allowing login.$actions['login'] = null— No additional action after login (like 2FA). Set toEmail2FA::classto enable two-factor authentication.allowRemembering = true— Enables the "Remember Me" checkbox functionality using secure cookies.
Security Tip: In a production application, you should also review the password validation rules in the same config file. Shield enforces minimum password length and common password checks by default — these are good defaults that you should keep enabled.
7 Create Register & Login Controllers
Create app/Controllers/AuthController.php:
php spark make:controller AuthController
Now replace the generated file content with the following code:
<?php
namespace App\Controllers;
use CodeIgniter\Shield\Entities\User;
class AuthController extends BaseController
{
public function register()
{
if ($this->request->getMethod() === 'post') {
// ── Validation rules ──
$rules = [
'username' => 'required|min_length[3]|max_length[30]|alpha_numeric_space',
'email' => 'required|valid_email',
'password' => 'required|min_length[8]|max_length[255]',
'password_confirm' => 'required|matches[password]',
];
// ── Custom error messages (optional but recommended) ──
$messages = [
'username' => [
'required' => 'Username is required.',
'min_length' => 'Username must be at least 3 characters.',
'max_length' => 'Username cannot exceed 30 characters.',
],
'email' => [
'required' => 'Email address is required.',
'valid_email' => 'Please enter a valid email address.',
],
'password' => [
'required' => 'Password is required.',
'min_length' => 'Password must be at least 8 characters.',
],
'password_confirm' => [
'required' => 'Please confirm your password.',
'matches' => 'Password confirmation does not match.',
],
];
if (! $this->validate($rules, $messages)) {
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
// ── Create user ──
$users = auth()->getProvider();
$user = new User([
'username' => $this->request->getPost('username'),
'email' => $this->request->getPost('email'),
]);
$user->fill([
'password' => $this->request->getPost('password'),
]);
if (! $users->save($user)) {
return redirect()->back()->withInput()->with('errors', $users->errors());
}
// Retrieve saved user (with ID) before adding to group
$user = $users->findById($users->getInsertID());
// Add to default group
$users->addToDefaultGroup($user);
return redirect()->to('/login')->with('message', 'Registration successful! Please login.');
}
return view('auth/register');
}
public function login()
{
if ($this->request->getMethod() === 'post') {
$credentials = [
'email' => $this->request->getPost('email'),
'password' => $this->request->getPost('password'),
];
$remember = (bool) $this->request->getPost('remember');
if (auth()->attempt($credentials, $remember)) {
return redirect()->to('/dashboard');
}
return redirect()->back()->withInput()->with('error', auth()->error() ?? 'Invalid credentials');
}
return view('auth/login');
}
public function logout()
{
auth()->logout();
return redirect()->to('/login')->with('message', 'You have been logged out');
}
}
?>
Why validate explicitly? Shield's User entity has its own internal validation, but it only covers database-level constraints (unique email, required fields). Adding CodeIgniter's validation layer gives you control over password confirmation, custom error messages, and input length limits — all before the data reaches Shield. This is a defense-in-depth approach: even if one layer fails, the other catches the problem.Important: Notice the
$users->findById($users->getInsertID()) call after saving. The $user object doesn't automatically receive a database ID after save(), so we need to fetch the saved user before calling addToDefaultGroup(). Without this step, the group assignment would silently fail. Key points about AuthController:
register()— Creates a newUserentity, fills in the password (Shield auto-hashes it with bcrypt), saves it via the user provider, and redirects to login on success.login()— Usesauth()->attempt()which verifies the email/password against the database. The optional$rememberflag enables persistent login cookies.logout()— Destroys the session and any remember-me tokens, then redirects to the login page.
Now create app/Controllers/DashboardController.php:
php spark make:controller DashboardController
<?php
namespace App\Controllers;
class DashboardController extends BaseController
{
public function index()
{
if (! auth()->loggedIn()) {
return redirect()->to('/login');
}
return view('dashboard');
}
}
?>
Note: The auth()->loggedIn() check in the Dashboard controller is an extra safety measure. Since we already apply Shield's login filter on this route (see the Routes section below), the filter handles redirection automatically. However, having the check in the controller provides defense-in-depth — a good security practice. 8 Create Views (Using Bootstrap 5 for styling)
extends the main layout and fills named sections for the title and content.8.1 Main Layout (app/Views/layout.php)
auth()->loggedIn() to conditionally show Login/Register or Dashboard/Logout links in the navigation.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CodeIgniter 4 Auth Demo <?= $this->renderSection('title') ? ' - ' . $this->renderSection('title') : '' ?></title>
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<!-- Optional: Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<style>
body { background-color: #f8f9fa; }
.card { border-radius: 1rem; box-shadow: 0 4px 20px rgba(0,0,0,0.08); }
.navbar-brand { font-weight: 700; }
</style>
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="/">CI4 Auth Demo</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<?php if (auth()->loggedIn()): ?>
<li class="nav-item">
<a class="nav-link" href="<?= url_to('dashboard') ?>">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link text-danger" href="<?= url_to('logout') ?>">Logout</a>
</li>
<?php else: ?>
<li class="nav-item">
<a class="nav-link" href="<?= url_to('login') ?>">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?= url_to('register') ?>">Register</a>
</li>
<?php endif; ?>
</ul>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="container mt-5 mb-5">
<?= $this->include('auth/partials/messages') ?>
<?= $this->renderSection('content') ?>
</main>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
8.2 Flash Messages Partial (app/Views/auth/partials/messages.php)
$this->include() so every page automatically shows flash feedback. Notice the use of esc() around all output — this prevents XSS attacks if an error message contains user input.
<?php if (session()->has('message')): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<?= esc(session('message')) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<?php if (session()->has('error')): ?>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<?= esc(session('error')) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<?php if (session()->has('errors')): ?>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<ul class="mb-0">
<?php foreach (session('errors') as $error): ?>
<li><?= esc($error) ?></li>
<?php endforeach; ?>
</ul>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
8.3 Registration Form (app/Views/auth/register.php)
csrf_field() generates a hidden CSRF token, and old() repopulates fields after validation failures so users don't have to retype everything.
<?= $this->extend('layout') ?>
<?= $this->section('title') ?>Register<?= $this->endSection() ?>
<?= $this->section('content') ?>
<div class="row justify-content-center">
<div class="col-md-6 col-lg-5">
<div class="card">
<div class="card-header bg-primary text-white text-center py-4">
<h3 class="mb-0">Create Account</h3>
</div>
<div class="card-body p-4 p-md-5">
<form action="<?= url_to('register') ?>" method="post">
<?= csrf_field() ?>
<div class="mb-4">
<label for="username" class="form-label">Username</label>
<input type="text" name="username" id="username" class="form-control form-control-lg"
value="<?= old('username') ?>" required autofocus>
</div>
<div class="mb-4">
<label for="email" class="form-label">Email address</label>
<input type="email" name="email" id="email" class="form-control form-control-lg"
value="<?= old('email') ?>" required>
</div>
<div class="mb-4">
<label for="password" class="form-label">Password</label>
<input type="password" name="password" id="password" class="form-control form-control-lg" required>
<div class="form-text">Minimum 8 characters</div>
</div>
<div class="mb-4">
<label for="password_confirm" class="form-label">Confirm Password</label>
<input type="password" name="password_confirm" id="password_confirm" class="form-control form-control-lg" required>
</div>
<button type="submit" class="btn btn-primary btn-lg w-100 mb-4">Register</button>
<div class="text-center">
Already have an account? <a href="<?= url_to('login') ?>" class="text-primary fw-bold">Login here</a>
</div>
</form>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
8.4 Login Form (app/Views/auth/login.php)
<?= $this->extend('layout') ?>
<?= $this->section('title') ?>Login<?= $this->endSection() ?>
<?= $this->section('content') ?>
<div class="row justify-content-center">
<div class="col-md-6 col-lg-5">
<div class="card">
<div class="card-header bg-success text-white text-center py-4">
<h3 class="mb-0">Sign In</h3>
</div>
<div class="card-body p-4 p-md-5">
<form action="<?= url_to('login') ?>" method="post">
<?= csrf_field() ?>
<div class="mb-4">
<label for="email" class="form-label">Email address</label>
<input type="email" name="email" id="email" class="form-control form-control-lg"
value="<?= old('email') ?>" required autofocus>
</div>
<div class="mb-4">
<label for="password" class="form-label">Password</label>
<input type="password" name="password" id="password" class="form-control form-control-lg" required>
</div>
<div class="mb-4 form-check">
<input type="checkbox" name="remember" id="remember" class="form-check-input" value="1">
<label class="form-check-label" for="remember">Remember me</label>
</div>
<button type="submit" class="btn btn-success btn-lg w-100 mb-4">Login</button>
<div class="text-center">
Don't have an account? <a href="<?= url_to('register') ?>" class="text-success fw-bold">Register here</a>
</div>
</form>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
8.5 Protected Dashboard (app/Views/dashboard.php)
auth()->user() to access the current user entity and auth()->id() for the user ID. The created_at->humanize() call formats the timestamp as a human-readable string like "2 days ago".
<?= $this->extend('layout') ?>
<?= $this->section('title') ?>Dashboard<?= $this->endSection() ?>
<?= $this->section('content') ?>
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card border-0 shadow-sm">
<div class="card-header bg-info text-white">
<h4 class="mb-0">Welcome, <?= esc(auth()->user()->username ?? auth()->user()->email) ?>!</h4>
</div>
<div class="card-body">
<h5 class="mb-4">You are successfully logged in.</h5>
<div class="alert alert-info">
<strong>User ID:</strong> <?= auth()->id() ?><br>
<strong>Email:</strong> <?= esc(auth()->user()->email) ?><br>
<strong>Created:</strong> <?= auth()->user()->created_at->humanize() ?>
</div>
<a href="<?= url_to('logout') ?>" class="btn btn-outline-danger btn-lg">Logout</a>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
9 Define Routes (app/Config/Routes.php)
$routes->get('/register', 'AuthController::register', ['as' => 'register']);
$routes->post('/register', 'AuthController::register');
$routes->get('/login', 'AuthController::login', ['as' => 'login']);
$routes->post('/login', 'AuthController::login');
$routes->get('/logout', 'AuthController::logout', ['as' => 'logout']);
$routes->get('/dashboard', 'DashboardController::index', ['as' => 'dashboard', 'filter' => 'login']);
How this works:- Each route uses named routes (the
asparameter) so we can reference them withurl_to()in views — making URL management cleaner. - Both
GETandPOSTmethods are defined for/registerand/login— GET displays the form, POST processes the submission. - The
/dashboardroute uses Shield's built-in login filter. This automatically redirects unauthenticated users to the login page — no manual check needed in the controller. - The
/logoutroute only needs GET since it simply destroys the session and redirects.
Tip: You can also group protected routes using
$routes->group() with the login filter to protect multiple pages at once. This is especially useful when building role-based access systems in CodeIgniter 4. 10 Folder Structure
11 Run and Test
php spark serve
By default, this starts the server at http://localhost:8080. Now let's verify that every part of the authentication system works correctly:1. Open your browser and go to:
http://localhost:8080/register2. Register a new user
- Username: testuser
- Email: test@example.com
- Password: Test@123456
→ You should see "Registration successful! Please login." and be redirected to /login
3. Log in with the new account
- Go to
/login - Email: test@example.com
- Password: Test@123456
→ Successful login → Redirected to
/dashboard showing a welcome message with your username4. Test invalid credentials
- Wrong password → You should see an "Invalid credentials" error
- Non-existing email → Same error message (this prevents user enumeration attacks)
5. Test "Remember Me" functionality
- Check the "Remember me" box during login
- Close the browser tab completely, then reopen the dashboard URL → You should still be logged in (cookie-based session)
6. Test protected route without login
- Log out by visiting
/logout - Manually visit
/dashboard → Should redirect back to /login automatically7. Test CSRF protection
- Temporarily remove
<?= csrf_field() ?> from the login form - Submit the form → Should get a CSRF error (403 Forbidden)
- Don't forget to add the CSRF field back after testing!
If all seven tests pass, your authentication system is working correctly and is ready for further development. You can now extend it with features like role-based access control or build a full application on top of it, such as a blog system or a REST API.
12 Common Errors & Troubleshooting
1. "Whoops! We seem to have hit a snag" or database connection error
Your
.env database credentials are incorrect. Verify the hostname, database name, username, and password. Test the connection by running:
php spark db:table
If this command fails, your database configuration is the problem — not your code.2. "Class CodeIgniter\Shield\Entities\User not found"
Shield wasn't installed correctly or the autoloader is stale. Run:
composer dump-autoload
If the error persists, verify Shield is installed by checking composer.json for the codeigniter4/shield entry, then re-run composer install.3. "The action you requested is not allowed" (403 CSRF error)
You're missing
<?= csrf_field() ?> inside your <form> tag. Every POST form in CodeIgniter 4 must include this hidden field. Also make sure CSRF protection is enabled in app/Config/Filters.php (Shield's setup command enables this automatically).4. Login always fails with "Invalid credentials" even with correct password
Check the
auth_identities table in your database. Shield stores email credentials in this table, not in the users table directly. If the table is empty or the email doesn't appear there, the registration didn't save the identity correctly. Try registering a new user and check both tables.5. "Remember Me" doesn't persist across browser restarts
Verify that
allowRemembering is set to true in app/Config/Auth.php. Also check that the auth_remember_tokens table exists (run php spark migrate if missing). Some browsers clear cookies on close by default — test in a different browser to rule this out.6. Migration fails with "Table already exists"
This happens if you ran migrations partially before. Either drop all Shield tables manually and re-run
php spark migrate, or run:
php spark migrate:rollback
php spark migrate
7. Route filter not redirecting — dashboard accessible without loginMake sure
php spark shield:setup was run successfully. This command registers Shield's filters in app/Config/Filters.php. Open that file and verify the login alias exists in the $aliases array. If it's missing, add it manually:
'login' => \CodeIgniter\Shield\Filters\SessionFilter::class,
If you encounter an issue not listed here, check the official Shield documentation or the CodeIgniter community forums.
Related CodeIgniter 4 Tutorials
Continue Learning CodeIgniter 4
If you found this authentication tutorial helpful, explore more of our CodeIgniter 4 guides to build complete, production-ready applications:- Role-Based Login System in CodeIgniter 4: Admin & User Roles — Take this authentication system further by adding admin/user roles and permission-based access control.
- How to Build a Simple Blog in CodeIgniter 4 with MySQL and Bootstrap — Learn CRUD operations, database models, and view templating by building a complete blog application.
- CodeIgniter 4 REST API CRUD Example with Postman — Build a RESTful API and test it with Postman. Shield also supports API token authentication for securing API endpoints.
- How to Secure Your CodeIgniter 4 Application from SQL Injection — Essential security guide covering query binding, input validation, and XSS prevention.
- Boost CodeIgniter 4 Performance and Page Speed — Optimize your application for faster load times with caching, query optimization, and asset management.
- Build a Multi-Language Website in CodeIgniter 4 — Add internationalization (i18n) support to your CodeIgniter application for a global audience.
- CodeIgniter 4 vs Laravel 12: Which is Better for Small Projects? — An in-depth comparison to help you choose the right PHP framework for your next project.
13 Conclusion : Build Secure Authentication the Modern Way in CodeIgniter 4
By following this tutorial, your application now has:
- Secure password storage with bcrypt hashing (no plain text passwords)
- Built-in CSRF protection and session management
- Remember-me functionality using secure cookies
- Automatic route protection with Shield's login filter
- Clean separation of concerns (controllers, views, Shield entities)
- Extensibility for email verification, password reset, 2FA, role-based access control, and API token authentication
Shield eliminates most of the security pitfalls that come with writing custom auth systems from scratch, while keeping your codebase lightweight and maintainable — exactly what CodeIgniter has always stood for.
Next Steps: Now that you have authentication in place, here are some recommended tutorials to continue building your application:
- Add Role-Based Access Control (Admin & User Roles) — Extend this auth system with user groups and permissions
- Build a Simple Blog in CodeIgniter 4 — Create a full CRUD application with the auth system protecting your admin panel
- Build a REST API with CodeIgniter 4 — Add API token authentication using Shield's built-in token support
- Secure Your App from SQL Injection — Essential security practices for any CodeIgniter 4 project
- Optimize CodeIgniter 4 Performance — Speed up your application for production deployment
Written by Revathi M
PHP Developer & Technical Writer · 10+ years building web applications with CodeIgniter and Laravel
Revathi specializes in PHP backend development, authentication systems, and REST API design. She writes practical, production-tested tutorials at Get Sample Code to help developers build secure applications faster.
Frequently Asked Questions
CodeIgniter Shield is the official authentication and authorization library for CodeIgniter 4, maintained by the same team that builds the framework. It provides secure login and registration with bcrypt password hashing, session-based authentication with remember-me cookies, optional two-factor authentication (2FA), magic link login, email activation, and a full group and permission system (RBAC) for role-based access control — all out of the box with minimal configuration.
No, you should not use Myth/Auth in 2026. Myth/Auth was deprecated in 2022 and is no longer receiving security updates or bug fixes. The official replacement is CodeIgniter Shield, which offers better security features, active maintenance, and full compatibility with the latest versions of CodeIgniter 4 and PHP 8.x. If you have an existing project using Myth/Auth, it is recommended to migrate to Shield as soon as possible.
There are two ways to protect routes in CodeIgniter 4 with Shield. First, you can use the auth() helper in your controller: if (! auth()->loggedIn()) { return redirect()->to('/login'); }. Second, and more recommended, you can apply Shield's built-in login filter directly in your routes configuration: $routes->get('dashboard', 'DashboardController::index', ['filter' => 'login']); — this automatically redirects unauthenticated users without any code in the controller. You can also apply the filter to route groups to protect multiple routes at once.
Yes, Shield handles password hashing automatically using the bcrypt algorithm by default. When you set a password on a User entity (via $user->fill(['password' => $value])), Shield hashes it before storing it in the database. You never need to call password_hash() manually. Shield also handles password verification automatically when using auth()->attempt() during login. The hashing algorithm and cost factor can be customized in app/Config/Auth.php if needed.
Yes, Shield includes built-in email verification support. To enable it, open app/Config/Auth.php and set the register action to the EmailActivator class: 'register' => \CodeIgniter\Shield\Authentication\Actions\EmailActivator::class. You also need to configure your email settings in app/Config/Email.php with your SMTP server details (host, port, username, password). Once enabled, new users will receive a verification email with a unique link after registration, and they won't be able to log in until they click the link to verify their email address.
