php-auth

repository·master·Indexed 22 days ago

https://github.com/delight-im/php-auth

A simple, lightweight, and secure authentication library for PHP that is framework-agnostic and database-agnostic. It supports user registration, login with persistent sessions, email verification, password resets, and account status management. Compatible with PHP 5.6.0+ and supports MySQL 5.5.3+, MariaDB 5.5.23+, PostgreSQL 9.5.10+, and SQLite 3.14.1+ via PDO.

Tokens
14.8K
Snippets
35
Records
55
Agent score
78%

What's inside php-auth

  1. Use the Admin interface to manage users

    master

    The administrative interface is accessible via $auth->admin(). This interface allows you to perform sensitive operations like creating, deleting, and modifying users.

    Security Warning: You must implement your own secure access control (e.g., checking for an administrator role) before exposing any code that calls $auth->admin().

  2. Password security and limitations

    master

    Password Hashing

    Passwords and authentication tokens are automatically hashed using computationally expensive functions like bcrypt or newer algorithms (e.g., Argon2). The library automatically applies a random salt and handles upgrading hashes to newer algorithms when a user signs in or changes their password.

    Password Constraints

    • Minimum length: Not enforced by the library; implement your own check.
    • Maximum length: 2048 bytes (approx. 2048 ASCII characters or 512–2048 UTF-8 characters).
    • Content: Any text, including null bytes, is permitted.
    • Empty passwords: Not allowed.
  3. Manage user roles and permissions

    master

    Users can have zero, one, or multiple roles. You can use roles for authorization or implement a permission-based system by wrapping role checks in helper functions.

    // Checking specific roles
    if ($auth->hasRole(\Delight\Auth\Role::SUPER_MODERATOR)) {
        echo 'The user is a super moderator';
    }
    
    // Checking if user has ANY of these roles
    if ($auth->hasAnyRole(\Delight\Auth\Role::DEVELOPER, \Delight\Auth\Role::MANAGER)) {
        echo 'The user is either a developer, or a manager, or both';
    }
    
    // Checking if user has ALL of these roles
    if ($auth->hasAllRoles(\Delight\Auth\Role::DEVELOPER, \Delight\Auth\Role::MANAGER)) {
        echo 'The user is both a developer and a manager';
    }
    
    // Getting all assigned roles
    $roles = $auth->getRoles();
    
    // Recommended: Implement a permission-based wrapper
    function canEditArticle(\Delight\Auth\Auth $auth) {
        return $auth->hasAnyRole(
            \Delight\Auth\Role::MODERATOR,
            \Delight\Auth\Role::SUPER_MODERATOR,
            \Delight\Auth\Role::ADMIN,
            \Delight\Auth\Role::SUPER_ADMIN
        );
    }
  4. Handle SecondFactorRequiredException during login

    master

    When using Auth#login, Auth#loginWithUsername, Auth#confirmEmailAndSignIn, or Auth#resetPasswordAndSignIn, you must catch \Delight\Auth\SecondFactorRequiredException if the user has 2FA enabled.

    When this exception is caught, use the exception instance $e to determine which 2FA method to prompt the user for:

    • $e->hasTotpOption(): Prompt for an authenticator app code.
    • $e->hasSmsOption(): Use $e->getSmsRecipientMasked() for the UI and send $e->getSmsOtpValue() to $e->getSmsRecipient() via your SMS provider.
    • $e->hasEmailOption(): Use $e->getEmailRecipientMasked() for the UI and send $e->getEmailOtpValue() to $e->getEmailRecipient() via your email provider.

    To complete the login, call Auth#provideOneTimePasswordAsSecondFactor with the user's entered code.

    try {
        $auth->login($username, $password);
    } catch (\Delight\Auth\SecondFactorRequiredException $e) {
        if ($e->hasTotpOption()) {
            echo 'Please open your authenticator application and enter the code that is shown';
        }
    
        if ($e->hasSmsOption()) {
            // Send '$e->getSmsOtpValue()' to '$e->getSmsRecipient()' via text message
            echo 'Please enter the one-time password that has been sent to you via text message at ' . $e->getSmsRecipientMasked();
        }
    
        if ($e->hasEmailOption()) {
            // Send '$e->getEmailOtpValue()' to '$e->getEmailRecipient()' via email
            echo 'Please enter the one-time password that has been sent to you via email at ' . $e->getEmailRecipientMasked();
        }
    }
    
    // After user enters code:
    try {
        $auth->provideOneTimePasswordAsSecondFactor($_POST['oneTimePassword']);
        echo 'You are now signed in';
    } catch (\Delight\Auth\InvalidOneTimePasswordException $e) {
        echo 'Your one-time password has not been correct';
    }
  5. Change the current user's email address

    master

    To change a logged-in user's email address, follow this pattern:

    1. Reconfirm password: Call reconfirmPassword($password). This returns true if the password is correct.
    2. Request change: If reconfirmation succeeds, call changeEmail($newEmail, $callback). The callback provides a $selector and $token to be sent to the new email address for verification.
    3. Verification: The user must click the link sent to the new address to finalize the change.

    Important Notes:

    • Changes take effect in the local session immediately, but may take up to five minutes to sync to other active sessions (this interval is controlled by $sessionResyncInterval in the Auth constructor).
    • If the user has 2FA enabled via email, you should disable it and prompt them to re-enable it after the email change, potentially by calling prepareTwoFactorViaEmail immediately after the change.
    • Always notify the user's previous email address about the change as an out-of-band security notification.
    try {
        if ($auth->reconfirmPassword($_POST['password'])) {
            $auth->changeEmail($_POST['newEmail'], function ($selector, $token) {
                $url = 'https://www.example.com/verify_email?selector=' . \urlencode($selector) . '&token=' . \urlencode($token);
                // Send $url to the NEW email address
            });
            echo 'The change will take effect as soon as the new email address has been confirmed';
        }
    } catch (\Delight\Auth\InvalidEmailException $e) { /* ... */ }
  6. Configure authentication cookies

    master

    The library uses two cookies: a mandatory session cookie and an optional persistent login cookie.

    You can rename the session cookie to avoid conflicts. The name of the persistent login cookie will automatically follow the session cookie name.

    Recommended methods (in order):

    1. Set session.name in php.ini.
    2. Call \ini_set('session.name', 'new_name'); before creating the Auth instance.
    3. Call \session_name('new_name'); before creating the Auth instance.

    Note: session.auto_start must be set to 0 in php.ini for these to work.

    To share authentication state between subdomains (e.g., example.com and www.example.com), set the session.cookie_domain attribute.

    Recommended methods:

    1. Set session.cookie_domain in php.ini.
    2. Call \ini_set('session.cookie_domain', 'example.com'); before creating the Auth instance.

    To restrict cookies to specific directories, set the session.cookie_path attribute (e.g., /path/to/subfolder). The default is / (root).

  7. Create a new Auth instance

    master

    To use the library, instantiate the \Delight\Auth\Auth class by passing a PDO database connection.

    Constructor Parameters:

    1. $db (required): A PDO instance or a \Delight\Db\PdoDatabase instance. The database user needs SELECT, INSERT, UPDATE, and DELETE privileges.
    2. $ipAddress (optional): The user's real IP address. Use this if your server is behind a proxy and $_SERVER['REMOTE_ADDR'] returns the proxy IP.
    3. $dbTablePrefix (optional): A string prefix for all database tables (e.g., my_). Defaults to an empty string.
    4. $throttling (optional): A boolean to enable or disable request limiting/throttling. Defaults to true. Set to false during development to disable.
    5. $sessionResyncInterval (optional): An integer representing the interval in seconds to resynchronize user data from the database. Defaults to 300 seconds (5 minutes).
    6. $dbSchema (optional): A string for the database name, schema name, or other qualifier.
    $db = new \PDO('mysql:dbname=my-database;host=localhost;charset=utf8mb4', 'my-username', 'my-password');
    $auth = new \Delight\Auth\Auth($db);
  8. Implement password reset ("forgot password") flow

    master

    The password reset process consists of three steps:

    1. Initiating the request: Call forgotPassword($email, $callback). The callback provides a $selector and $token which you must use to build a URL (e.g., https://example.com/reset?selector=...&token=...) and send to the user via email or SMS.
    2. Verifying the attempt: When the user clicks the link, extract the selector and token from the URL. Use canResetPassword($selector, $token) to check validity or canResetPasswordOrThrow($selector, $token) to validate and throw exceptions if the token is invalid or expired.
    3. Updating the password: Once the user provides a new password, call resetPassword($selector, $token, $password).

    Tips:

    • To log the user in automatically after a successful reset, use resetPasswordAndSignIn($selector, $token, $password).
    • resetPassword returns an array containing ['id' => $userId, 'email' => $userEmail] upon success.
    • You can specify a custom expiration interval for the reset request using the third parameter of forgotPassword.
  9. Migrate from v1.x.x to v2.x.x (Database Schema Update)

    master

    When upgrading from v1.x.x to v2.x.x, the MySQL schema changes from charset utf8 (collation utf8_general_ci) to utf8mb4 (collation utf8mb4_unicode_ci). You must update your database schema using the provided SQL statements to ensure compatibility with the new character set requirements.

    ALTER TABLE `users` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
    ALTER TABLE `users_confirmations` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
    
    -- ALTER DATABASE `<DATABASE_NAME>` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
    
    ALTER TABLE `users` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    ALTER TABLE `users_confirmations` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    ALTER TABLE `users_remembered` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    ALTER TABLE `users_resets` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    ALTER TABLE `users_throttling` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    
    ALTER TABLE `users` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
    ALTER TABLE `users` CHANGE `username` `username` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL;
    
    ALTER TABLE `users_confirmations` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
    
    ALTER TABLE `users_throttling` CHANGE `action_type` `action_type` ENUM('login','register','confirm_email') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
    
    REPAIR TABLE users;
    OPTIMIZE TABLE users;
    REPAIR TABLE users_confirmations;
    OPTIMIZE TABLE users_confirmations;
    REPAIR TABLE users_remembered;
    OPTIMIZE TABLE users_remembered;
    REPAIR TABLE users_resets;
    OPTIMIZE TABLE users_resets;
    REPAIR TABLE users_throttling;
    OPTIMIZE TABLE users_throttling;
  10. Implement custom password requirements

    master

    The library does not enforce specific password complexity rules (like minimum length or blacklists) internally. Instead, you should wrap your own validation logic around the library's registration and login methods to ensure passwords meet your application's security standards.

    function isPasswordAllowed($password) {
        if (\strlen($password) < 8) {
            return false;
        }
    
        $blacklist = [ 'password1', '123456', 'qwerty' ];
    
        if (\in_array($password, $blacklist)) {
            return false;
        }
    
        return true;
    }
    
    if (isPasswordAllowed($password)) {
        $auth->register($email, $password);
    }
  11. Migrate from v8.x.x to v9.x.x

    master

    Upgrading to v9.x.x requires a database schema update. You must create three new tables in your database schema:

    • users_2fa
    • users_otps
    • users_audit_log

    Refer to the project's SQL files for the specific definitions for MySQL, PostgreSQL, or SQLite.

  12. Requirements for php-auth

    master

    Before using the library, ensure your environment meets the following requirements:

    PHP Requirements

    • PHP 5.6.0 or higher
    • pdo (PHP Data Objects) extension
    • One of the following database drivers: mysqlnd, pgsql, or sqlite
    • openssl extension

    Database Requirements

    • MySQL 5.5.3+ or MariaDB 5.5.23+ or PostgreSQL 9.5.10+ or SQLite 3.14.1+ (or other supported SQL databases)