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+ * PHP 5.6.0+
* PDO (PHP Data Objects) extension (`pdo`) * PDO (PHP Data Objects) extension (`pdo`)
* MySQL Native Driver (`mysqlnd`) * MySQL Native Driver (`mysqlnd`) **or** SQLite driver (`sqlite`)
* OpenSSL extension (`openssl`) * 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 ## Installation
@@ -39,6 +39,7 @@ Completely framework-agnostic and database-agnostic.
1. Set up a database and create the required tables: 1. Set up a database and create the required tables:
* [MySQL](Database/MySQL.sql) * [MySQL](Database/MySQL.sql)
* [SQLite](Database/SQLite.sql)
## Upgrading ## Upgrading
@@ -75,7 +76,13 @@ Migrating from an earlier version of this project? See our [upgrade guide](Migra
```php ```php
// $db = new PDO('mysql:dbname=my-database;host=localhost;charset=utf8mb4', 'my-username', 'my-password'); // $db = new PDO('mysql:dbname=my-database;host=localhost;charset=utf8mb4', 'my-username', 'my-password');
// or // 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'); // $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); $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. 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 ### What about password hashing?
* secure password storage using the bcrypt algorithm
* email verification through message with confirmation link 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.
* assurance of unique email addresses
* customizable password requirements and enforcement 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$`.
* optional usernames with customizable restrictions
* login 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.
* keeping the user logged in for a long time (beyond expiration of browser session) via secure long-lived token ("remember me")
* account management ### How can I implement custom password requirements?
* change password
* tracking the time of sign up and last login 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.
* check if user has been logged in via "remember me" cookie
* logout 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:
* full and reliable destruction of session
* session management ```php
* protection against session hijacking via cross-site scripting (XSS) function isPasswordAllowed($password) {
* do *not* permit script-based access to cookies if (strlen($password) < 8) {
* restrict cookies to HTTPS to prevent session hijacking via non-secure HTTP return false;
* protection against session fixation attacks }
* protection against cross-site request forgery (CSRF)
* works automatically (i.e. no need for CSRF tokens everywhere) $blacklist = [ 'password1', '123456', 'qwerty' ];
* do *not* use HTTP `GET` requests for "dangerous" operations
* throttling if (in_array($password, $blacklist)) {
* per IP address return false;
* per account }
* enhanced HTTP security
* prevents clickjacking return true;
* prevent content sniffing (MIME sniffing) }
* disables caching of potentially sensitive data
* miscellaneous if (isPasswordAllowed($password)) {
* ready for both IPv4 and IPv6 $auth->register($email, $password);
* works behind proxy servers as well }
* privacy-friendly (e.g. does *not* save readable IP addresses) ```
## Exceptions ## Exceptions

View File

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

View File

@@ -40,8 +40,6 @@ class DatabaseError extends AuthError {}
class DatabaseDriverError extends DatabaseError {} class DatabaseDriverError extends DatabaseError {}
class WrongMysqlDatabaseDriverError extends DatabaseDriverError {}
class MissingCallbackError extends AuthError {} class MissingCallbackError extends AuthError {}
class HeadersAlreadySentError 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`'); 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'; require __DIR__.'/../vendor/autoload.php';
$db = new PDO('mysql:dbname=php_auth;host=127.0.0.1;charset=utf8mb4', 'root', 'monkey'); $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); $auth = new \Delight\Auth\Auth($db);