Skip to content

Commit 51bf070

Browse files
authored
Merge pull request #97 from CakeDC/feature/magic-link
Add OneTimetokenAuthenticator as part of the magic link feature
2 parents 0f8efc8 + 03f427c commit 51bf070

7 files changed

Lines changed: 78 additions & 23 deletions

File tree

.github/workflows/ci.yml

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,35 @@ on:
77

88
jobs:
99
testsuite:
10-
runs-on: ubuntu-22.04
10+
runs-on: ubuntu-24.04
1111
strategy:
1212
fail-fast: false
1313
matrix:
14-
php-version: ['8.1', '8.2']
14+
php-version: ['8.1', '8.2', '8.3', '8.4']
1515
db-type: [sqlite, mysql, pgsql]
1616
prefer-lowest: ['']
1717

1818
steps:
19+
- uses: actions/checkout@v4
20+
with:
21+
persist-credentials: false
22+
1923
- name: Setup MySQL latest
2024
if: matrix.db-type == 'mysql'
21-
run: docker run --rm --name=mysqld -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=cakephp -p 3306:3306 -d mysql --default-authentication-plugin=mysql_native_password --disable-log-bin
25+
run: |
26+
sudo service mysql start
27+
mysql -h 127.0.0.1 -u root -proot -e 'CREATE DATABASE cakephp;'
28+
2229
2330
- name: Setup PostgreSQL latest
2431
if: matrix.db-type == 'pgsql'
2532
run: docker run --rm --name=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=cakephp -p 5432:5432 -d postgres
2633

27-
- uses: actions/checkout@v2
28-
2934
- name: Setup PHP
3035
uses: shivammathur/setup-php@v2
3136
with:
3237
php-version: ${{ matrix.php-version }}
33-
extensions: mbstring, intl, apcu, memcached, redis, pdo_${{ matrix.db-type }}
38+
extensions: mbstring, intl, apcu, memcached, redis, pdo_${{ matrix.db-type }}, ${{ matrix.db-type }}
3439
ini-values: apc.enable_cli = 1
3540
coverage: pcov
3641

@@ -43,7 +48,7 @@ jobs:
4348
run: echo "::set-output name=date::$(date +'%Y-%m')"
4449

4550
- name: Cache composer dependencies
46-
uses: actions/cache@v1
51+
uses: actions/cache@v4
4752
with:
4853
path: ${{ steps.composer-cache.outputs.dir }}
4954
key: ${{ runner.os }}-composer-${{ steps.key-date.outputs.date }}-${{ hashFiles('composer.json') }}-${{ matrix.prefer-lowest }}
@@ -57,22 +62,22 @@ jobs:
5762
fi
5863
5964
- name: Setup problem matchers for PHPUnit
60-
if: matrix.php-version == '8.1' && matrix.db-type == 'mysql'
65+
if: matrix.php-version == '8.2' && matrix.db-type == 'mysql'
6166
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
6267

6368
- name: Run PHPUnit
6469
run: |
6570
if [[ ${{ matrix.db-type }} == 'sqlite' ]]; then export DB_URL='sqlite:///:memory:'; fi
66-
if [[ ${{ matrix.db-type }} == 'mysql' ]]; then export DB_URL='mysql://root:root@127.0.0.1/cakephp'; fi
71+
if [[ ${{ matrix.db-type }} == 'mysql' ]]; then export DB_URL='mysql://root:root@127.0.0.1/cakephp?encoding=utf8'; fi
6772
if [[ ${{ matrix.db-type }} == 'pgsql' ]]; then export DB_URL='postgres://postgres:postgres@127.0.0.1/postgres'; fi
68-
if [[ ${{ matrix.php-version }} == '8.1' ]]; then
73+
if [[ ${{ matrix.php-version }} == '8.2' ]]; then
6974
export CODECOVERAGE=1 && vendor/bin/phpunit --coverage-clover=coverage.xml
7075
else
7176
vendor/bin/phpunit
7277
fi
7378
7479
- name: Submit code coverage
75-
if: matrix.php-version == '8.1'
80+
if: matrix.php-version == '8.2'
7681
uses: codecov/codecov-action@v1
7782

7883
cs-stan:
@@ -85,7 +90,7 @@ jobs:
8590
- name: Setup PHP
8691
uses: shivammathur/setup-php@v2
8792
with:
88-
php-version: '8.1'
93+
php-version: '8.2'
8994
extensions: mbstring, intl, apcu, memcached, redis
9095
tools: cs2pr
9196
coverage: none
@@ -99,7 +104,7 @@ jobs:
99104
run: echo "::set-output name=date::$(date +'%Y-%m')"
100105

101106
- name: Cache composer dependencies
102-
uses: actions/cache@v1
107+
uses: actions/cache@v4
103108
with:
104109
path: ${{ steps.composer-cache.outputs.dir }}
105110
key: ${{ runner.os }}-composer-${{ steps.key-date.outputs.date }}-${{ hashFiles('composer.json') }}-${{ matrix.prefer-lowest }}

Docs/Documentation/TwoFactor.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ Configuration
88

99
Processors defined as Configure storage with key `TwoFactorProcessors`
1010

11+
By default `\RobThree\Auth\Providers\Qr\EndroidQrCodeProvider` is used.
12+
13+
You can disable it by adding this to any config file:
14+
15+
`OneTimePasswordAuthenticator.qrcodeprovider` => `YOUR QR CODE PROVIDER`
16+
17+
To get a list of available providers please visit [RobThree/TwoFactorAuth](https://robthree.github.io/TwoFactorAuth/qr-codes.html) documentation.
18+
1119

1220
Processors
1321
-------------

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,19 @@
2626
"source": "https://github.com/CakeDC/auth"
2727
},
2828
"require": {
29-
"php": ">=8.1.0",
29+
"php": ">=8.1",
3030
"cakephp/cakephp": "^5.0"
3131
},
3232
"require-dev": {
3333
"phpunit/phpunit": "^10.0",
34+
"endroid/qr-code": "^6.0 || ^5.0",
3435
"league/oauth2-facebook": "@stable",
3536
"league/oauth2-instagram": "@stable",
3637
"league/oauth2-google": "@stable",
3738
"league/oauth2-linkedin": "@stable",
3839
"luchianenco/oauth2-amazon": "^1.1",
3940
"google/recaptcha": "@stable",
40-
"robthree/twofactorauth": "^2.0",
41+
"robthree/twofactorauth": "^3.0 || ^2.0",
4142
"league/oauth1-client": "^1.7",
4243
"cakephp/authorization": "^3.0",
4344
"cakephp/cakephp-codesniffer": "^5.0",

config/auth.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@
108108
// The algorithm used
109109
'algorithm' => \RobThree\Auth\Algorithm::Sha1,
110110
// QR-code provider (more on this later)
111-
'qrcodeprovider' => null,
111+
'qrcodeprovider' => new \RobThree\Auth\Providers\Qr\EndroidQrCodeProvider(),
112112
// Random Number Generator provider (more on this later)
113113
'rngprovider' => null
114114
],
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace CakeDC\Auth\Authenticator;
5+
6+
use Authentication\Authenticator\AbstractAuthenticator;
7+
use Authentication\Authenticator\AuthenticatorInterface;
8+
use Authentication\Authenticator\Result;
9+
use Authentication\Authenticator\ResultInterface;
10+
use Cake\Core\Configure;
11+
use Cake\ORM\TableRegistry;
12+
use Psr\Http\Message\ServerRequestInterface;
13+
14+
class OneTimeTokenAuthenticator extends AbstractAuthenticator implements AuthenticatorInterface
15+
{
16+
/**
17+
* @inheritDoc
18+
*/
19+
public function authenticate(ServerRequestInterface $request): ResultInterface
20+
{
21+
/** @var \Cake\Http\ServerRequest $request */
22+
$token = $request->getQuery('token') ?: $request->getData('token');
23+
if (is_array($token)) {
24+
$token = join($token);
25+
}
26+
27+
if (!$token) {
28+
return new Result(null, Result::FAILURE_CREDENTIALS_MISSING);
29+
}
30+
31+
$usersTable = TableRegistry::getTableLocator()->get(Configure::read('Users.table'));
32+
33+
$user = $usersTable->loginWithToken($token);
34+
35+
if (!$user) {
36+
return new Result(null, Result::FAILURE_CREDENTIALS_MISSING);
37+
}
38+
39+
return new Result($user, Result::SUCCESS);
40+
}
41+
}

src/Controller/Component/OneTimePasswordAuthenticatorComponent.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ public function initialize(array $config): void
4141

4242
if (Configure::read('OneTimePasswordAuthenticator.login')) {
4343
$this->tfa = new TwoFactorAuth(
44-
Configure::read('OneTimePasswordAuthenticator.issuer'),
45-
Configure::read('OneTimePasswordAuthenticator.digits'),
46-
Configure::read('OneTimePasswordAuthenticator.period'),
47-
Configure::read('OneTimePasswordAuthenticator.algorithm'),
48-
Configure::read('OneTimePasswordAuthenticator.qrcodeprovider'),
49-
Configure::read('OneTimePasswordAuthenticator.rngprovider')
44+
qrcodeprovider: Configure::read('OneTimePasswordAuthenticator.qrcodeprovider'),
45+
issuer: Configure::read('OneTimePasswordAuthenticator.issuer'),
46+
digits: Configure::read('OneTimePasswordAuthenticator.digits'),
47+
period: Configure::read('OneTimePasswordAuthenticator.period'),
48+
algorithm: Configure::read('OneTimePasswordAuthenticator.algorithm'),
49+
rngprovider: Configure::read('OneTimePasswordAuthenticator.rngprovider')
5050
);
5151
}
5252
}

tests/TestCase/Authenticator/CookieAuthenticatorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public function testPersistIdentity($setCookie, $field, array $post, array $sess
8282
$this->assertInstanceOf(RequestInterface::class, $result['request']);
8383
$this->assertInstanceOf(ResponseInterface::class, $result['response']);
8484
if ($setCookie) {
85-
$this->assertStringContainsString('CookieAuth=%5B%22johndoe%22%2C%22%242y%2410%24', $result['response']->getHeaderLine('Set-Cookie'));
85+
$this->assertStringContainsString('CookieAuth=%5B%22johndoe%22%2C%22%242y%24', $result['response']->getHeaderLine('Set-Cookie'));
8686
} else {
8787
$this->assertStringNotContainsString('CookieAuth', $result['response']->getHeaderLine('Set-Cookie'));
8888
}

0 commit comments

Comments
 (0)