Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial OTP implementation #981

Draft
wants to merge 9 commits into
base: 11.next-cake4
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions Docs/Documentation/Code2F.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
Code2F (OTP authentication with phone / email)
=============

The plugin offers an easy way to integrate U2F in the users login flow
of your application.

How does it work
----------------
When the user log-in, he is requested to enter an OTP code sent to an email or phone. The code is requested based on the authentication checker selected.

Enabling
--------

First if you want to use OTP authentication with phone and you want to use builtin TwilioTransport, you need to require twilio/sdk using composer:

```
composer require twilio/sdk:@stable
```

Then add this in your config/users.php file:

```php
'Code2f.enabled' => true,
```

Disabling
---------
You can disable it by adding this in your config/users.php file:

```php
'Code2f.enabled' => false,
```

Configuration
-------------
To configure Code2f you need to set a checker class (see Checkers)
```php
'Code2f' => [
'enabled' => true,
'checker' => \CakeDC\Auth\Authentication\DefaultCode2fTimeBasedAuthenticationChecker::class,
'type' => \CakeDC\Auth\Authentication\Code2fAuthenticationCheckerInterface::CODE2F_TYPE_PHONE,
'config' => 'sms',
'message' => '{0} is your {1} verification code',
'maxSeconds' => 300,
'maxTries' => 3,
'daysBeforeVerifyAgain' => 15
],
```
* `enabled`: Indicates if Code2f is enabled or not.
* `checker`: See Checkers below.
* `type`: Sets the recipient type. Can be `phone` or `email`.
* `config`: Email config to use. It does not matter if type is `phone` or `email` since we are using Email and Transports structure to deliver emails and SMS. (See Email Config)
* `message`: Message to be sent. It will be passed to translation function (__()). `{0}` is replaced with `code` generated and {1} is replaced with `App.name`.
* `maxSeconds`: Validity of the code.
* `maxTries`: Max tries before generating a new code.
* `daysBeforeVerifyAgain`: If using `Code2fTimeBasedAuthenticationChecker` this config sets the days before asking for OTP authentication again.

Email Config
------------
If you are using `email` type and you have an email config and transport configured you don't need to do anything else.

On the other hand if you are using `phone` type you will need a new email config and transport. For the example we use the built-in TwilioTransport that gives you SMS support out-of-the-box.

For `email` type it defaults to `default`. For `phone` it defaults to `sms`.

For Email Config you need to include custom `from` key, so it sets `from` parameter correctly. For Transport Config you need to set the `phonePattern` key to allow mailer to set the correct pattern for phones and avoid the exception thrown by default behavior.

```php
'sms' => [
'transport' => 'twilio',
'from' => '+19876543210', //Complete phone number from your Twilio account
]
```

```php
'twilio' => [
'className' => \CakeDC\Users\Mailer\Transport\TwilioTransport::class,
'phonePattern' => '/^\+[1-9]\d{1,14}$/i' //Phone pattern compatible with Twilio phone numbers
]
```

While `phonePattern` is required, `from` may not be since it depends on the actual transport. Remember you can always implement new transport to send SMS taking `TwilioTransport` as a reference.

Checkers
-------------
We include three different checkers in our `cakedc/auth` plugin:

* \CakeDC\Auth\Authentication\DefaultCode2fAuthenticationChecker: Default authentication checker requires user to enter a new OTP code on each login.
* \CakeDC\Auth\Authentication\Code2fFingerprintAuthenticationChecker: Fingerprint authentication checker requires user to enter a new OTP code when fingerprint changes (fingerprint is calculated from user-agent header and ip)
* \CakeDC\Auth\Authentication\Code2fTimeBasedAuthenticationChecker: Time Based authentication checker requires user to enter a new OTP code after `daysBeforeVerifyAgain` days has passed since last validation.

1 change: 1 addition & 0 deletions Docs/Documentation/Events.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The events in this plugin follow these conventions `<Plugin><Category>.<EventNam
* `Users.Global.afterResetPassword`
* `Users.Global.onExpiredToken`
* `Users.Global.afterResendTokenValidation`
* `Users.Global.afterCode2fVerified`

The events allow you to inject data into the plugin on the before* plugins and use the data for your
own business.
Expand Down
1 change: 1 addition & 0 deletions Docs/Home.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Documentation
* [Social Authentication](Documentation/SocialAuthentication.md)
* [Google Authenticator](Documentation/Two-Factor-Authenticator.md)
* [Yubico U2F](Documentation/Yubico-U2F.md)
* [Code 2F (OTP authentication with phone/email)](Documentation/Code2F.md)
* [UserHelper](Documentation/UserHelper.md)
* [AuthLinkHelper](Documentation/AuthLinkHelper.md)
* [Events](Documentation/Events.md)
Expand Down
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"league/oauth2-instagram": "@stable",
"league/oauth2-google": "@stable",
"league/oauth2-linkedin": "@stable",
"twilio/sdk": "@stable",
"luchianenco/oauth2-amazon": "^1.1",
"google/recaptcha": "@stable",
"robthree/twofactorauth": "^1.6",
Expand All @@ -59,7 +60,8 @@
"league/oauth2-linkedin": "Provides Social Authentication with LinkedIn",
"google/recaptcha": "Provides reCAPTCHA validation for registration form",
"robthree/twofactorauth": "Provides Google Authenticator functionality",
"cakephp/authorization": "Provide authorization for users"
"cakephp/authorization": "Provide authorization for users",
"twilio/sdk": "Provide SMS OTP with Twilio"
},
"autoload": {
"psr-4": {
Expand Down
52 changes: 52 additions & 0 deletions config/Migrations/20220211110249_AddCode2fFields.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);

use Migrations\AbstractMigration;

class AddCode2fFields extends AbstractMigration
{
/**
* Change Method.
*
* More information on this method is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
* @return void
*/
public function change()
{
$this->table('users')
->addColumn('phone', 'string', [
'null' => true,
'default' => null,
'length' => 256
])
->addColumn('phone_verified', 'datetime', [
'null' => true,
'default' => null,
])
->update();
$this->table('otp_codes')
->addColumn('user_id', 'uuid', [
'null' => false,
])
->addColumn('code', 'string', [
'length' => 255,
'null' => false,
'default' => null
])
->addColumn('tries', 'integer', [
'null' => false,
'default' => 0
])
->addColumn('validated', 'datetime', [
'null' => true,
'default' => null,
])
->addColumn('created', 'datetime', [
'null' => false,
'default' => null,
])
->addForeignKey('user_id', 'users', 'id', array('delete' => 'CASCADE', 'update' => 'CASCADE'))
->create();
}
}
11 changes: 11 additions & 0 deletions config/users.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,17 @@
'enabled' => false,
'checker' => \CakeDC\Auth\Authentication\DefaultU2fAuthenticationChecker::class,
],
'Code2f' => [
'enabled' => false,
'checker' => \CakeDC\Auth\Authentication\DefaultCode2fAuthenticationChecker::class,
'type' => \CakeDC\Auth\Authentication\Code2fAuthenticationCheckerInterface::CODE2F_TYPE_EMAIL,
'config' => 'default',
'subject' => '{0}: Your verification code', //Valid for type EMAIL. {0} will be replaced by App.name
'message' => '{0} is your {1} verification code', //{0} will be replaced by code, {1} by App.name
'maxSeconds' => 300,
'maxTries' => 3,
'daysBeforeVerifyAgain' => 15,
],
'Webauthn2fa' => [
'enabled' => false,
'appName' => null,//App must set a valid name here
Expand Down
Loading