Custom PHP MVC authentication system built with Composer, a lightweight router, and role-based access control.
- Custom MVC architecture —
App\Core\Router, abstractControllerandModel, PSR-4 autoloading via Composer - Secure login with bcrypt password hashing (
password_hash()/password_verify()) - Persistent login via Remember Me —
HttpOnly/SameSite=Strictcookie; token stored as SHA-256 hash in DB - Automatic session timeout on inactivity with remember cookie cleanup
- Password recovery via email with expiring single-use tokens (PHPMailer + STARTTLS)
- Admin user management — full CRUD with role-based access control (
AuthMiddleware) App\Config\Databasesingleton — single\mysqliconnection per request- File-based cache for the users listing with automatic invalidation on writes
- Integration test suite — 28 PHPUnit tests against a real MySQL DB; CI via GitHub Actions
- SweetAlert2 toast notifications for all CRUD and authentication actions
- Per-page asset injection —
$pageStyles/$pageScriptsarrays in shared layouts - Shared layout system —
header.php/footer.phpaccept$pageTitle,$favicon,$bodyClass,$useDataTables - App version displayed in footer via
APP_VERSIONenv var
- PHP >= 8.2
- MySQL / MariaDB
- Apache with
mod_rewrite(XAMPP recommended) - Composer
- Gmail account with an App Password (or any SMTP provider)
- Clone the repository:
git clone https://github.com/Jandres25/Encriptacion_PHP.git
cd Encriptacion_PHP- Install dependencies:
composer install- Copy and configure the environment file:
cp .env.example .envEdit .env with your credentials:
DB_HOST=localhost
DB_USERNAME=root
DB_PASSWORD=
DB_DATABASE=login
SMTP_HOST=smtp.gmail.com
SMTP_USERNAME=your@gmail.com
SMTP_PASSWORD=your_app_password
SMTP_PORT=587
APP_URL=http://localhost/Encriptacion_PHP/public
APP_TIMEZONE=America/Bogota
APP_VERSION=1.5.0
CACHE_ENABLED=true
CACHE_TTL_USERS=60
REMEMBER_ME_ENABLED=true
REMEMBER_ME_TTL=2592000
SESSION_TIMEOUT=1800
- Import the database schema:
mysql -u root -p < database/schema.sql- (Optional) Load sample data:
mysql -u root -p < database/seeds.sql- Place the project in your server's web root (e.g.
htdocs/in XAMPP) and openAPP_URLin your browser.
├── app/
│ ├── Config/
│ │ ├── autoload.php # Bootstrap: timezone, cache, DB, session, restoreFromCookie()
│ │ ├── cache.php # Cache bootstrap + appCache() helper
│ │ ├── config.php # Loads .env via phpdotenv; defines APP_URL + env()
│ │ └── database.php # Database singleton — Database::getConnection()
│ ├── Controller/
│ │ ├── AuthController.php # login, logout, forgotPassword, resetPassword
│ │ ├── HomeController.php # Dashboard — applies timeout + auth middleware
│ │ └── UserController.php # Full user CRUD — guarded by admin middleware
│ ├── Core/
│ │ ├── Auth.php # Credential verify, remember-me tokens, password reset tokens
│ │ ├── Controller.php # Abstract base — render() and redirect()
│ │ ├── Model.php # Abstract base — holds protected \mysqli $db
│ │ └── Router.php # GET/POST route registration and dispatch
│ ├── Middleware/
│ │ └── AuthMiddleware.php # Static guards: auth(), admin(), timeout()
│ ├── Model/
│ │ └── User.php # All DB queries via MySQLi prepared statements
│ └── Service/
│ └── MailerService.php # PHPMailer encapsulation — SMTP via STARTTLS
├── database/
│ ├── schema.sql # Table definitions (users + password_resets)
│ ├── schema_test.sql # Table-only schema for test DB (no CREATE DATABASE)
│ └── seeds.sql # Sample data with bcrypt-hashed passwords
├── libs/
│ └── Cache/ # File-based cache implementation
├── public/
│ ├── css/ # bootstrap.css, estilo.css, all.min.css, layout-protected.css
│ ├── DataTables/ # DataTables JS bundle + Bootstrap 4 skin
│ ├── img/ # Images and icons
│ ├── js/ # jQuery, Bootstrap JS, Popper, SweetAlert2, users-*.js
│ ├── webfonts/ # FontAwesome webfonts
│ ├── .htaccess # Apache rewrite rules for clean URLs
│ └── index.php # Front controller
├── routes/
│ └── web.php # All route definitions
├── storage/
│ └── cache/ # Runtime cache files (*.cache)
├── views/
│ ├── auth/ # login, forgot_password, reset_password (standalone)
│ ├── home/ # index.php — dashboard content (wrapped by shared layout)
│ ├── layouts/ # header.php, footer.php, messages.php
│ └── user/ # index, create, edit (wrapped by shared layout)
├── tests/
│ ├── bootstrap.php # Test bootstrap — loads .env.testing, never starts session
│ ├── TestCase.php # Abstract base — DB connection, truncate, createUser()
│ ├── Unit/
│ │ └── UserTest.php # 14 integration tests for App\Model\User
│ └── Integration/
│ └── AuthTest.php # 14 integration tests for App\Core\Auth
├── .env.example # Environment variable template
├── phpunit.xml # PHPUnit 11 configuration
└── composer.json # Composer dependencies and PSR-4 autoload
- Open
http://localhost/Encriptacion_PHP/public/in your browser - Log in with a seeded user (e.g. username
Admin, passwordAdmin1234) - Admin users (
is_admin = 1) see the Users link in the nav → full CRUD - To recover a password, click "Forgot your password?" on the login page
All routes are declared in routes/web.php and dispatched by App\Core\Router:
| URL | Controller method |
|---|---|
/ |
HomeController::index() |
/login |
AuthController::login() |
/logout |
AuthController::logout() |
/forgot-password |
AuthController::forgotPassword() |
/reset-password?token=... |
AuthController::resetPassword() |
/users |
UserController::index() |
/users/create |
UserController::create() |
/users/edit?id=X |
UserController::edit() |
/users/delete?id=X |
UserController::delete() |
- Passwords hashed with bcrypt (
PASSWORD_DEFAULT) - Session set only after successful
password_verify() - Reset tokens: 256-bit (
bin2hex(random_bytes(32))), 1-hour expiry, single-use - All DB queries via MySQLi prepared statements
- Email validated with
filter_var()before DB lookup - SMTP with STARTTLS (port 587)
- Remember-me: raw token in cookie, SHA-256 hash in DB — cookie is
HttpOnly,SameSite=Strict,Secureon HTTPS - Session timeout enforced on every protected request; clears remember cookie to prevent silent re-login
- Cached endpoint:
/userslisting (App\Model\User::getAll()) - Cache key:
users.all - Invalidation: on create, edit, delete, and password update
- Controls:
CACHE_ENABLED=true|false,CACHE_TTL_USERS=<seconds> - Storage:
storage/cache/*.cache - If the directory is not writable, cache is disabled for the request and a warning is logged (no HTTP 500)
The project includes an integration test suite (PHPUnit 11) that runs against a real MySQL database.
# 1. Create the test database
mysql -u root -p -e "CREATE DATABASE login_test;"
mysql -u root -p login_test < database/schema_test.sql
# 2. Copy and configure the test environment
cp .env.testing.example .env.testing # or create it manually from .env.testing section in docs
# Set DB_HOST, DB_USERNAME, DB_PASSWORD, DB_DATABASE=login_test
# 3. Run all tests
composer test
# Run by suite
composer test:unit # App\Model\User — 14 tests
composer test:integration # App\Core\Auth — 14 testsTests run automatically on every push and PR to master via GitHub Actions (.github/workflows/tests.yml).
- Fork the project
- Create a feature branch (
git checkout -b feature/my-feature) - Commit your changes following Conventional Commits
- Push and open a Pull Request
MIT License — see the LICENSE file for details.
Jandres25 — jandrespb4@gmail.com
Project link: https://github.com/Jandres25/Encriptacion_PHP