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

18 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
Marco
7c842f903e Add 'MySQL Native Driver' as full name of 'mysqlnd' driver in README 2017-03-18 22:35:44 +01:00
Marco
0e2279ecda Document what is required to make library work with other databases 2017-03-18 22:31:30 +01:00
Marco
79db94f500 Add 'mysqlnd' driver for PDO as platform dependency in README 2017-03-18 22:30:19 +01:00
Marco
f38d7bd62c Add PDO as platform dependency in README 2017-03-18 22:25:23 +01:00
Marco
04a2e8ef4e Throw error if 'libmysqlclient' driver is used instead of 'mysqlnd' 2017-03-18 22:21:23 +01:00
Marco
59505479a5 Add class 'WrongMysqlDatabaseDriverError' 2017-03-18 22:13:28 +01:00
Marco
fdcfd6f78c Add class 'DatabaseDriverError' 2017-03-18 22:12:49 +01:00
Marco
20606bc507 Update dependencies 2017-03-18 22:11:05 +01:00
Marco
89a7af17fe Add documentation on how to retrieve status information 2017-02-26 14:15:26 +01:00
7 changed files with 148 additions and 52 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")
);

107
README.md
View File

@@ -17,8 +17,10 @@ Completely framework-agnostic and database-agnostic.
## Requirements ## Requirements
* PHP 5.6.0+ * PHP 5.6.0+
* PDO (PHP Data Objects) extension (`pdo`)
* MySQL Native Driver (`mysqlnd`) **or** SQLite driver (`sqlite`)
* OpenSSL extension (`openssl`) * OpenSSL extension (`openssl`)
* MySQL 5.5.3+ **or** MariaDB 5.5.23+ * 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
@@ -37,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
@@ -73,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);
``` ```
@@ -323,6 +332,34 @@ Remember that usernames are optional and there is only a username if you supplie
If the user is not currently signed in, this returns `null`. If the user is not currently signed in, this returns `null`.
#### Status information
```php
if ($auth->isNormal()) {
// user is in default state
}
if ($auth->isArchived()) {
// user has been archived
}
if ($auth->isBanned()) {
// user has been banned
}
if ($auth->isLocked()) {
// user has been locked
}
if ($auth->isPendingReview()) {
// user is pending review
}
if ($auth->isSuspended()) {
// user has been suspended
}
```
#### Checking whether the user was "remembered" #### Checking whether the user was "remembered"
```php ```php
@@ -453,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

@@ -5,7 +5,7 @@
"php": ">=5.6.0", "php": ">=5.6.0",
"ext-openssl": "*", "ext-openssl": "*",
"delight-im/cookie": "^2.1", "delight-im/cookie": "^2.1",
"delight-im/db": "^1.0" "delight-im/db": "^1.2"
}, },
"type": "library", "type": "library",
"keywords": [ "auth", "authentication", "login", "security" ], "keywords": [ "auth", "authentication", "login", "security" ],

27
composer.lock generated
View File

@@ -4,26 +4,25 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "bd80e3e52b8bd8a4a0c74c7cf9f5bf5e", "content-hash": "c075bec19490fc0e972be01cdd02d59b",
"content-hash": "3f836c43e0ff2293051f2ccb739d23cf",
"packages": [ "packages": [
{ {
"name": "delight-im/cookie", "name": "delight-im/cookie",
"version": "v2.1.0", "version": "v2.1.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/delight-im/PHP-Cookie.git", "url": "https://github.com/delight-im/PHP-Cookie.git",
"reference": "3e41e0d44959b59de98722b5b1b1fb83f9f528f3" "reference": "22f2c19750a6ad3dbf69a8ef3ea0e454a8e064fa"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/delight-im/PHP-Cookie/zipball/3e41e0d44959b59de98722b5b1b1fb83f9f528f3", "url": "https://api.github.com/repos/delight-im/PHP-Cookie/zipball/22f2c19750a6ad3dbf69a8ef3ea0e454a8e064fa",
"reference": "3e41e0d44959b59de98722b5b1b1fb83f9f528f3", "reference": "22f2c19750a6ad3dbf69a8ef3ea0e454a8e064fa",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"delight-im/http": "^2.0", "delight-im/http": "^2.0",
"php": ">=5.3.0" "php": ">=5.6.0"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@@ -46,20 +45,20 @@
"samesite", "samesite",
"xss" "xss"
], ],
"time": "2016-11-23 20:09:42" "time": "2016-12-18T20:22:46+00:00"
}, },
{ {
"name": "delight-im/db", "name": "delight-im/db",
"version": "v1.0.2", "version": "v1.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/delight-im/PHP-DB.git", "url": "https://github.com/delight-im/PHP-DB.git",
"reference": "c8d1eba6583007471d55bf7d88eb3c9d87ea849d" "reference": "df99ef7c2e86c7ce206647ffe8ba74447c075b57"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/delight-im/PHP-DB/zipball/c8d1eba6583007471d55bf7d88eb3c9d87ea849d", "url": "https://api.github.com/repos/delight-im/PHP-DB/zipball/df99ef7c2e86c7ce206647ffe8ba74447c075b57",
"reference": "c8d1eba6583007471d55bf7d88eb3c9d87ea849d", "reference": "df99ef7c2e86c7ce206647ffe8ba74447c075b57",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -87,7 +86,7 @@
"sql", "sql",
"sqlite" "sqlite"
], ],
"time": "2016-12-01 12:40:36" "time": "2017-03-18T20:51:59+00:00"
}, },
{ {
"name": "delight-im/http", "name": "delight-im/http",
@@ -123,7 +122,7 @@
"http", "http",
"https" "https"
], ],
"time": "2016-07-21 15:05:01" "time": "2016-07-21T15:05:01+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],

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

@@ -38,6 +38,8 @@ class AuthError extends \Exception {}
class DatabaseError extends AuthError {} class DatabaseError extends AuthError {}
class DatabaseDriverError extends DatabaseError {}
class MissingCallbackError extends AuthError {} class MissingCallbackError extends AuthError {}
class HeadersAlreadySentError extends AuthError {} class HeadersAlreadySentError extends AuthError {}

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);