Humblee

A humble PHP framework & CMS

Users & Roles

Humblee includes a built-in user system with session-based authentication, role-based access control, optional two-factor authentication, and a persistent remember-me login. User accounts and role assignments are managed entirely through the admin interface.


User Accounts

Creating users

Navigate to Admin > Users and add a new user. Each account requires a unique username and email address. Passwords are hashed with Argon2ID — they are never stored in recoverable form.

Editing users

From the user list, open any account to update the display name, email address, or password. Administrators can reset passwords directly without knowing the current password.

Deactivating users

Removing all roles from a user effectively locks them out — they can still log in, but every role-gated action will return a 403. To fully prevent login, delete the account.


Roles

Roles are the unit of access control in Humblee. A user can hold multiple roles simultaneously, and a role check passes if the user holds any one of the required roles (OR logic). Roles are integers in the database but are referenced in code by their string name.

Built-in roles

Role name What it grants
login Any authenticated session — the baseline role present for every logged-in user
admin Full access to the CMS admin panel: pages, templates, blocks, users, settings
content View and edit page content in the inline editor; save drafts
publish Everything content can do, plus the ability to publish drafts and make content live
developer Equivalent to admin + content + publish combined — intended for developers working in the admin panel
media Access the media library: upload files, manage folders, toggle encryption

Assigning roles

In Admin > Users, open the user record and use the roles section to add or remove roles. Changes take effect immediately — the next request the user makes will reflect the new role set.

Role check behavior

When a role is checked with Core::auth(), the result is cached in the session for the duration of the request. The first check queries the database; subsequent checks in the same request read from memory. There is no role cache that persists between requests.


Authentication

Login

Users log in at /user/login. The login form validates credentials against the user record:

  1. The submitted password is verified with password_verify() using Argon2ID.
  2. If that fails, a legacy BLAKE2b hash check runs (for accounts created before the Argon2ID migration). On success, the password is silently re-hashed to Argon2ID and the login proceeds — this upgrade happens once per account, automatically.
  3. On success, the user's ID, username, and CSRF token are written to $_SESSION[session_key].

Logout

/user/logout clears the session and, if a remember-me cookie exists, invalidates it.

Remember me

When a user checks "Remember me" at login, a signed cookie is set. On future visits where no active session exists, Auth::handle() reads the cookie, validates the signature, and re-populates the session. The cookie is invalidated on logout.

Two-factor authentication (optional)

If the Twilio integration is configured (TWILIO_Enabled => true in the environment file), users with a verified phone number can enable 2FA. After successful password entry, the login flow sends a one-time code via SMS and requires it before completing the session. 2FA is opt-in per user and requires no code changes — it is gated entirely by the Twilio configuration.

To enable:

// humblee/configuration/env_*.php
'TWILIO_Enabled'       => true,
'TWILIO_AccountSID'    => 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'TWILIO_AuthToken'     => '...',
'TWILIO_FromNumber'    => '+15550001234',

If TWILIO_Enabled is false, 2FA prompts and the related UI are suppressed entirely.


Access Log

Every login attempt (successful or not) is recorded in the access log table. The admin interface exposes this log for auditing. Each entry stores the timestamp, username attempted, IP address, and outcome.


Developer Reference

Checking roles in code

// Single role — returns bool
if (!Core::auth('login')) {
    header('Location: ' . _app_path . 'user/login');
    exit;
}

// Multiple roles — passes if the user holds at least one
if (!Core::auth(['admin', 'developer'])) {
    http_response_code(403);
    exit;
}

Common role groupings

Core::auth('login')                             // any logged-in user
Core::auth(['admin', 'developer'])              // admin panel access
Core::auth(['content', 'publish', 'developer']) // content editing
Core::auth('media')                             // media library

Session structure

$_SESSION[session_key] = [
    'user_id'    => 42,
    'username'   => 'alice',
    'has_roles'  => ['admin', 'content', 'developer'], // cached after first check
    'csrf_token' => '...',  // BLAKE2b hash used for HMAC signing
];

session_key is a constant defined in humblee/configuration/env_*.php.

Users model

Users::logIn(string $username, string $password): bool
Users::logOut(): void
Users::addRole(int $user_id, int $role_id): void
Users::removeRole(int $user_id, int $role_id): void
Users::setRememberMeCookie(int $user_id): void
Users::checkRememberMeCookie(): void

Password hashing

When creating a user programmatically, always hash the password with Argon2ID and include the user ID as a pepper:

$hash = password_hash($password . '-' . $user_id, PASSWORD_ARGON2ID);

Do not use the legacy Users::stringToSaltedHash() method — it exists only for the login upgrade path.