mirror of
https://github.com/delight-im/PHP-Auth.git
synced 2025-08-08 09:06:29 +02:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
47afa1c411 | ||
|
26cb41e992 | ||
|
ee485f99ab | ||
|
8fc0b98493 | ||
|
45553afaea | ||
|
7834455e16 | ||
|
e49adf0150 | ||
|
0fb653d6e0 | ||
|
dc233d9d46 | ||
|
7c842f903e | ||
|
0e2279ecda | ||
|
79db94f500 | ||
|
f38d7bd62c | ||
|
04a2e8ef4e | ||
|
59505479a5 | ||
|
fdcfd6f78c | ||
|
20606bc507 | ||
|
89a7af17fe |
56
Database/SQLite.sql
Normal file
56
Database/SQLite.sql
Normal 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
107
README.md
@@ -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
|
||||||
|
|
||||||
|
@@ -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
27
composer.lock
generated
@@ -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": [],
|
||||||
|
@@ -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
|
||||||
|
@@ -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 {}
|
||||||
|
@@ -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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user