Please note that this project is no longer actively maintained.
This project started as a simple school assignment for some PHP course I was attending. Then I needed this kind of system for some website I was making and decided to start expanding. Word of warning: This project is/will be very opinionated, meaning that I will only add features that I myself find useful or interesting enough to develop.
password_hash()
function to hash and salt the passwords. Usernames are saved as a plain text.
PASSWORD_DEFAULT
which at time of the writing uses BCRYPT.AKA 'Why there are so many files?'
The admin panel will be here. Similarly to many other web applications (like WordPress) the admin panel can be easily accessed by typing [host]/admin.
This folder includes two files that are used to set some config options. More info below.
Just some CSS goodies for the UI.
The automatic installer resides here.
This folder has all .js files needed for the user interface to work.
This folder includes the backbone of the system. It has .php files for connecting to the database, login in and out, registering/creating a new account and modifying existing accounts. There's also scripts.js that has some general JavaScript goodies for the user interface. You need to insert your database info to credentials.php, check the setup section down below for help.
You should read this before using this. It's just a normal MIT license tho.
This file.
This is the account management page. Users can change their username and password here.
This file is here just for the demo. The users can only access this page if they are logged in. Otherwise they are redirected to the login page.
My take on creating a simple login form with Bootstrap. Feel free to modify it to fit your needs.
My take on creating a simple registration form with Bootstrap. Feel free to modify it to fit your needs.
This is used by the automatic installer to remove itself after the installation is complete.
This is the list of the most common English words. It's used by the username suggestor. You can replace the list with your own .txt file. Every word on the list needs to be followed by line break.
Please note that config options are subject to change. Check back often.
If you check the /config folder, you'll notice that there are two different files there. What gives? The main configuration file is config.php. It includes settings that are enforced on the server level. You should be mainly editing this file.
If you are also using the front-end user interface I provide, then you can/must also edit config.js There you can manage how things look for the avarage user. Please remember that these settings are client-side only and not enforced in any way so they can be edited by users.
Option name | Description | Default value | Supported values |
---|---|---|---|
$disableUserSelfRegistration | Prevent users from registering | false | Boolean |
$usernameMinLength | Shortest allowed username | 3 | 1 -> |
$usernameMaxLength | Longest allowed username | 30 | 1 -> |
$passwordMinLength | Minimum length of passwords | 8 | 1 -> |
$usernameRegExp | All usernames must match this regular expression | any regExp | |
$passwordRegExp | All passwords must match this regular expression | any regExp | |
$newAccountAccessLevel | Useful for creating your first admin account | "user" | "user", "admin" |
$debugMode | Allows you to disable dabase connection (for debuging only) | "no" | "no" |
$debugAdminUsername | Allows you to log in while in debug mode | "admin" | any string |
$debugAdminPassword | Allows you to log in while in debug mode | "" | any string |
$debugSkipInstall | This is for debug purposes only | false | false |
$timeout | Time of inactivity (in seconds) required to log user out | 900 | any integer |
$adminPanelTimeout | Time of inactivity required to log user out from admin panel | 450 | any integer |
$errorMessages | Show more verbose error messages.Might leak sensitive info! | default | "default","verbose" |
$allowUsernameChange | Should user's be able to change their username | true | Boolean |
$forceHTTPS | Redirects all non-HTTPS connections to HTTPS and sends HSTS | false | Boolean |
In 2019 it's considered a huge security risk to not use HTTPS when dealing with any kind of sensitive information (like passwords). That's why it's very highly recommended to only use hosting solutions that support it and change this option to true. Nowadays you can even get the SSL certificate completely free from Let's Encrypt, so there's no reasons to not use it. However, on some (badly-configured) environments SERVER["HTTPS"] superglobal is not defined even when HTTPS is in fact used. That results in a endless loop of redirecting. I myself learned that the hard way.
Option name | Description | Default value | Supported values |
---|---|---|---|
disableUserSelfRegistration | Disables any UI elements related to registration | false | true, false |
usernameMinLength | Shortest username that UI accepts | 3 | 1 -> |
usernameMaxLength | Longest username that UI accepts | 30 | 1 -> |
passwordMinLength | Shortest password that UI accepts | 8 | 1 -> |
usernameRules | This string is shown if username didn't match the regExp | any string | |
passwordRules | This string is shown if password didn't match the regExp | any string | |
enableUsernameSuggestions | Allows you to disable or enable username suggestions | true | Boolean |
allowUsernameChange | Should user's be able to change their username (UI only) | true | Boolean |
enableLoginMessage | Display any message on the login page | false | Boolean |
loginMessage | Define the message to show when enableLoginMessage is true | "" | any string |
As stated earlier, you'll need a MySQL database. The database does not require a lot if space (unless you have LOTS of users) and any fairly recent version of MySQl should work. You can set up your database and create admin account manually, or you can use my automatic installer.
During the first step of the installation the installer needs to write a file to your host's drive. If you don't have proper permissions for that, you must do the install manually. See the instructions below.
users
with five colums: username
, password
, accessLevel
, lastLogin
and rememberMeToken
. Use a string data type like CHAR. I personally like to use VARCHAR. For the lastLogin
I recommend INT(11). I would also add a auto incrementing id field as a primary key but that is not strictly required.If you are using VARCHAR or other data type with varying maximum string length, then the table below will be useful.
Field | Required length (minimum) |
---|---|
username | Same as $usernameMaxLength in config.php |
password | I recommend using 255 to be safe (As PHP's default crypting method might change) |
accessLevel | 5 |
lastLogin | 11 |
rememberMeToken | 255 |
Don't worry, something like this should get you covered:
CREATE TABLE IF NOT EXISTS users (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(64) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
accessLevel VARCHAR(10) NOT NULL,
lastLogin INT(11),
rememberMeToken VARCHAR(255)
);
After setting up the database, you'll need to create your first admin account. There's two ways to do that:
admin
and a randomly generated password. (If you followed my SQL-sample above and set UNIQUE constraint to the username field, then the account won't be created if it already exists. You won't get error message) After you have created the admin account, you must remove (or rename) the /install folder. Finally, log into to the newly created account and change the password using the account management page.OR
$newAccountAccessLevel
in config/config.php to "admin" and then create a new account using the normal registration form. You need to remove the /install folder to be able to access the login page. Remember to change the value back to "user" afterwards.IMPORTANT! I remind you again that you MUST delete the /install folder before using this in live production environment. Otherwise anyone can see your database credentials!
After setup you can create new accounts (admin or normal) using the admin panel.
This means that installer noticed that the function random_bytes(int)
does not exist or work properly. If you are using PHP version older than 7.0 you must use 3rd party library that implements that function. For PHP 5.x I recommend this one.
MySQL default is 3306.
This happens when the wizard fails to remove itself. That is usually caused by some restrictive permissions on the host. Fix the problem by manually removing the /install folder.
Check troubleshooting tips in general FAQ below.
The admin panel is in very early stage of the development. Many things might be broken.
More to be added in the future.
/admin
Read config.php more carefully.
That means that PDOException
occured while trying to connect to the database. You can turn more verbose error messages on in /config/config.php. The most common causes are:
PDO_MYSQL
driver is not available or configured properly.PDO_MYSQL
.No, it doesn't and it's been that way far too long, I know. I'm planning to get to it, soon™.
Update: It's working now. Please note that it only remembers the login for 30 days (For security reasons).
Yes, I know that implementing something like that always opens new security holes. However, I'm not forcing users to use it or anything. If the user doesn't check the checkbox, no access token is created so there's no security risk for that user.
random_bytes()
function. That token is saved to the database and two cookies are sent to the browser. The value of the first cookie is the username of the user as a plain text. The second cookie is much more important. It's value is the created token.The main problem is that if "the bad guy" is somehow able to get access to the user's token they can easily forge a cookie and log in as that user. There are two ways for the bad guy to get access to user's token: By somehow (e.g. SQL injection) getting it from the database or by stealing the cookie and/or it's value from the user.
I consider it a security risk as some people would be inclined to use too simple or same passwords. However, I might allow this in future versions via a config option.