1
0
mirror of https://github.com/delight-im/PHP-Auth.git synced 2025-08-08 00:56:28 +02:00

9 Commits

Author SHA1 Message Date
Marco
47afa1c411 Remove enforcement of hard dependency on 'mysqlnd' in code 2017-06-20 02:19:46 +02:00
Marco
26cb41e992 Document support of SQLite 2017-06-12 20:35:07 +02:00
Marco
ee485f99ab Ensure compatibility with SQLite which does not cast to native types 2017-06-12 20:29:58 +02:00
Marco
8fc0b98493 Remove superfluous blank line 2017-06-12 20:28:47 +02:00
prometeusweb
45553afaea Add database schema for SQLite 2017-06-12 20:26:14 +02:00
Marco
7834455e16 Add 'What about password hashing?' to FAQ in README 2017-04-24 21:06:06 +02:00
Marco
e49adf0150 Move 'Custom password requirements' to FAQ in README 2017-04-24 20:58:18 +02:00
Marco
0fb653d6e0 Add section 'Custom password requirements' to README 2017-03-24 17:07:26 +01:00
Marco
dc233d9d46 Remove 'Features' section in README 2017-03-24 16:49:37 +01:00
6 changed files with 103 additions and 50 deletions

56
Database/SQLite.sql Normal file
View File

@@ -0,0 +1,56 @@
-- PHP-Auth (https://github.com/delight-im/PHP-Auth)
-- Copyright (c) delight.im (https://www.delight.im/)
-- Licensed under the MIT License (https://opensource.org/licenses/MIT)
PRAGMA foreign_keys = OFF;
CREATE TABLE "users" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("id" >= 0),
"email" VARCHAR(249) NOT NULL,
"password" VARCHAR(255) NOT NULL,
"username" VARCHAR(100) DEFAULT NULL,
"status" INTEGER NOT NULL CHECK ("status" >= 0) DEFAULT "0",
"verified" INTEGER NOT NULL CHECK ("verified" >= 0) DEFAULT "0",
"registered" INTEGER NOT NULL CHECK ("registered" >= 0),
"last_login" INTEGER CHECK ("last_login" >= 0) DEFAULT NULL,
CONSTRAINT "email" UNIQUE ("email")
);
CREATE TABLE "users_confirmations" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("id" >= 0),
"email" VARCHAR(249) NOT NULL,
"selector" VARCHAR(16) NOT NULL,
"token" VARCHAR(255) NOT NULL,
"expires" INTEGER NOT NULL CHECK ("expires" >= 0),
CONSTRAINT "selector" UNIQUE ("selector")
);
CREATE INDEX "users_confirmations.email_expires" ON "users_confirmations" ("email", "expires");
CREATE TABLE "users_remembered" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("id" >= 0),
"user" INTEGER NOT NULL CHECK ("user" >= 0),
"selector" VARCHAR(24) NOT NULL,
"token" VARCHAR(255) NOT NULL,
"expires" INTEGER NOT NULL CHECK ("expires" >= 0),
CONSTRAINT "selector" UNIQUE ("selector")
);
CREATE INDEX "users_remembered.user" ON "users_remembered" ("user");
CREATE TABLE "users_resets" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("id" >= 0),
"user" INTEGER NOT NULL CHECK ("user" >= 0),
"selector" VARCHAR(20) NOT NULL,
"token" VARCHAR(255) NOT NULL,
"expires" INTEGER NOT NULL CHECK ("expires" >= 0),
CONSTRAINT "selector" UNIQUE ("selector")
);
CREATE INDEX "users_resets.user_expires" ON "users_resets" ("user", "expires");
CREATE TABLE "users_throttling" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK ("id" >= 0),
"action_type" TEXT NOT NULL CHECK ("action_type" IN ("login", "register", "confirm_email")),
"selector" VARCHAR(44) DEFAULT NULL,
"time_bucket" INTEGER NOT NULL CHECK ("time_bucket" >= 0),
"attempts" INTEGER NOT NULL CHECK ("attempts" >= 0) DEFAULT "1",
CONSTRAINT "action_type_selector_time_bucket" UNIQUE ("action_type", "selector", "time_bucket")
);

View File

@@ -18,9 +18,9 @@ Completely framework-agnostic and database-agnostic.
* PHP 5.6.0+
* PDO (PHP Data Objects) extension (`pdo`)
* MySQL Native Driver (`mysqlnd`)
* MySQL Native Driver (`mysqlnd`) **or** SQLite driver (`sqlite`)
* OpenSSL extension (`openssl`)
* MySQL 5.5.3+ **or** MariaDB 5.5.23+ **or** other SQL databases that you create the [schema](Database) for
* MySQL 5.5.3+ **or** MariaDB 5.5.23+ **or** SQLite 3.14.1+ **or** other SQL databases that you create the [schema](Database) for
## Installation
@@ -39,6 +39,7 @@ Completely framework-agnostic and database-agnostic.
1. Set up a database and create the required tables:
* [MySQL](Database/MySQL.sql)
* [SQLite](Database/SQLite.sql)
## Upgrading
@@ -75,7 +76,13 @@ Migrating from an earlier version of this project? See our [upgrade guide](Migra
```php
// $db = new PDO('mysql:dbname=my-database;host=localhost;charset=utf8mb4', 'my-username', 'my-password');
// or
// $db = new PDO('sqlite:../Databases/my-database.sqlite');
// or
// $db = new \Delight\Db\PdoDsn('mysql:dbname=my-database;host=localhost;charset=utf8mb4', 'my-username', 'my-password');
// or
// $db = new \Delight\Db\PdoDsn('sqlite:../Databases/my-database.sqlite');
$auth = new \Delight\Auth\Auth($db);
```
@@ -483,41 +490,41 @@ $uuid = \Delight\Auth\Auth::createUuid();
For detailed information on how to read and write session data conveniently, please refer to [the documentation of the session library](https://github.com/delight-im/PHP-Cookie#reading-and-writing-session-data), which is included by default.
## Features
## Frequently asked questions
* registration
* secure password storage using the bcrypt algorithm
* email verification through message with confirmation link
* assurance of unique email addresses
* customizable password requirements and enforcement
* optional usernames with customizable restrictions
* login
* keeping the user logged in for a long time (beyond expiration of browser session) via secure long-lived token ("remember me")
* account management
* change password
* tracking the time of sign up and last login
* check if user has been logged in via "remember me" cookie
* logout
* full and reliable destruction of session
* session management
* protection against session hijacking via cross-site scripting (XSS)
* do *not* permit script-based access to cookies
* restrict cookies to HTTPS to prevent session hijacking via non-secure HTTP
* protection against session fixation attacks
* protection against cross-site request forgery (CSRF)
* works automatically (i.e. no need for CSRF tokens everywhere)
* do *not* use HTTP `GET` requests for "dangerous" operations
* throttling
* per IP address
* per account
* enhanced HTTP security
* prevents clickjacking
* prevent content sniffing (MIME sniffing)
* disables caching of potentially sensitive data
* miscellaneous
* ready for both IPv4 and IPv6
* works behind proxy servers as well
* privacy-friendly (e.g. does *not* save readable IP addresses)
### What about password hashing?
Any password or authentication token is automatically hashed using the ["bcrypt"](https://en.wikipedia.org/wiki/Bcrypt) function, which is based on the ["Blowfish" cipher](https://en.wikipedia.org/wiki/Blowfish_(cipher)) and (still) considered one of the strongest password hash functions today. "bcrypt" is used with 1,024 iterations, i.e. a "cost" factor of 10. A random ["salt"](https://en.wikipedia.org/wiki/Salt_(cryptography)) is applied automatically as well.
You can verify this configuration by looking at the hashes in your database table `users`. If the above is true with your setup, all password hashes in your `users` table should start with the prefix `$2$10$`, `$2a$10$` or `$2y$10$`.
When new algorithms (such as [Argon2](https://en.wikipedia.org/wiki/Argon2)) may be introduced in the future, this library will automatically take care of "upgrading" your existing password hashes whenever a user signs in or changes their password.
### How can I implement custom password requirements?
Enforcing a minimum length for passwords is usually a good idea. Apart from that, you may want to look up whether a potential password is in some blacklist, which you could manage in a database or in a file, in order to prevent dictionary words or commonly used passwords from being used in your application.
To allow for maximum flexibility and ease of use, this library has been designed so that it does *not* contain any further checks for password requirements itself, but instead allows you to wrap your own checks around the relevant calls to library methods. Example:
```php
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);
}
```
## Exceptions

View File

@@ -584,7 +584,7 @@ final class Auth extends UserManager {
);
// ensure that the account has been verified before initiating a password reset
if ($userData['verified'] !== 1) {
if ((int) $userData['verified'] !== 1) {
throw new EmailNotVerifiedException();
}
@@ -679,7 +679,7 @@ final class Auth extends UserManager {
$this->updatePassword($userData['id'], $password);
}
if ($userData['verified'] === 1) {
if ((int) $userData['verified'] === 1) {
$this->onLoginSuccessful($userData['id'], $userData['email'], $userData['username'], $userData['status'], false);
// continue to support the old parameter format

View File

@@ -40,8 +40,6 @@ class DatabaseError extends AuthError {}
class DatabaseDriverError extends DatabaseError {}
class WrongMysqlDatabaseDriverError extends DatabaseDriverError {}
class MissingCallbackError extends AuthError {}
class HeadersAlreadySentError extends AuthError {}

View File

@@ -66,16 +66,6 @@ abstract class UserManager {
throw new \InvalidArgumentException('The database connection must be an instance of either `PdoDatabase`, `PdoDsn` or `PDO`');
}
$this->db->addOnConnectListener(function (PdoDatabase $db) {
// if a MySQL database is used
if ($db->getDriverName() === 'MySQL') {
// if the required MySQL Native Driver (mysqlnd) is not used (but instead the older MySQL Client Library (libmysqlclient))
if (\extension_loaded('mysqlnd') === false && \stripos($db->getClientVersion(), 'mysqlnd') === false) {
throw new WrongMysqlDatabaseDriverError('You must use PDO with the newer \'mysqlnd\' driver instead of the older \'libmysqlclient\' driver');
}
}
});
}
/**

View File

@@ -20,6 +20,8 @@ header('Content-type: text/html; charset=utf-8');
require __DIR__.'/../vendor/autoload.php';
$db = new PDO('mysql:dbname=php_auth;host=127.0.0.1;charset=utf8mb4', 'root', 'monkey');
// or
// $db = new PDO('sqlite:../Databases/php_auth.sqlite');
$auth = new \Delight\Auth\Auth($db);