v1.0 Initial commit of project
This commit is contained in:
12
vendor/robthree/twofactorauth/.gitattributes
vendored
Normal file
12
vendor/robthree/twofactorauth/.gitattributes
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/.github/ export-ignore
|
||||
/demo/ export-ignore
|
||||
/docs/ export-ignore
|
||||
/tests/ export-ignore
|
||||
/testsDependency/ export-ignore
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
/.php-cs-fixer.dist.php export-ignore
|
||||
/logo.png export-ignore
|
||||
/multifactorauthforeveryone.png export-ignore
|
||||
/phpstan.neon export-ignore
|
||||
/phpunit.xml export-ignore
|
||||
1
vendor/robthree/twofactorauth/.github/CODE_OF_CONDUCT.md
vendored
Normal file
1
vendor/robthree/twofactorauth/.github/CODE_OF_CONDUCT.md
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Don't be a jerk!
|
||||
4
vendor/robthree/twofactorauth/.github/FUNDING.yml
vendored
Normal file
4
vendor/robthree/twofactorauth/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [RobThree]
|
||||
custom: ["https://paypal.me/robiii"]
|
||||
31
vendor/robthree/twofactorauth/.github/workflows/test-bacon.yml
vendored
Normal file
31
vendor/robthree/twofactorauth/.github/workflows/test-bacon.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Test Bacon QR Code Provider
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2', '8.3']
|
||||
bacon-version: ['^2', '^3']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
coverage: xdebug
|
||||
ini-values: error_reporting=E_ALL
|
||||
|
||||
- uses: ramsey/composer-install@v3
|
||||
|
||||
- run: composer require bacon/bacon-qr-code:${{ matrix.bacon-version }}
|
||||
|
||||
- run: composer lint-ci
|
||||
- run: composer test testsDependency/BaconQRCodeTest.php
|
||||
31
vendor/robthree/twofactorauth/.github/workflows/test-endroid.yml
vendored
Normal file
31
vendor/robthree/twofactorauth/.github/workflows/test-endroid.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Test Endroid QR Code Provider
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2', '8.3']
|
||||
endroid-version: ["^3","^4","^5","^6"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
coverage: xdebug
|
||||
ini-values: error_reporting=E_ALL
|
||||
|
||||
- uses: ramsey/composer-install@v3
|
||||
|
||||
- run: composer require endroid/qrcode:${{ matrix.endroid-version }} -W
|
||||
|
||||
- run: composer lint-ci
|
||||
- run: composer test testsDependency/EndroidQRCodeTest.php
|
||||
29
vendor/robthree/twofactorauth/.github/workflows/test.yml
vendored
Normal file
29
vendor/robthree/twofactorauth/.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2', '8.3']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
coverage: xdebug
|
||||
ini-values: error_reporting=E_ALL
|
||||
|
||||
- uses: ramsey/composer-install@v3
|
||||
|
||||
- run: composer lint-ci
|
||||
- run: composer phpstan
|
||||
- run: composer test
|
||||
193
vendor/robthree/twofactorauth/.gitignore
vendored
Normal file
193
vendor/robthree/twofactorauth/.gitignore
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# Roslyn cache directories
|
||||
*.ide/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
#NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding addin-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# If using the old MSBuild-Integrated Package Restore, uncomment this:
|
||||
#!**/packages/repositories.config
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# Composer
|
||||
/vendor
|
||||
composer.lock
|
||||
|
||||
# .vs
|
||||
.vs/
|
||||
|
||||
.phpunit.result.cache
|
||||
.php-cs-fixer.cache
|
||||
60
vendor/robthree/twofactorauth/.php-cs-fixer.dist.php
vendored
Normal file
60
vendor/robthree/twofactorauth/.php-cs-fixer.dist.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* PHP-CS-Fixer config for RobThree/TwoFactorAuth
|
||||
*/
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->name('/\.php|\.php.dist$/')
|
||||
->exclude('build')
|
||||
->exclude('demo')
|
||||
->exclude('docs')
|
||||
->in(['lib', 'tests', 'testsDependency'])
|
||||
;
|
||||
|
||||
$config = new PhpCsFixer\Config();
|
||||
|
||||
return $config->setRules(array(
|
||||
'@PSR2' => true,
|
||||
'@PSR12' => true,
|
||||
'@PHP82Migration' => true,
|
||||
'array_syntax' => ['syntax' => 'long'],
|
||||
'class_attributes_separation' => true,
|
||||
'declare_strict_types' => true,
|
||||
'dir_constant' => true,
|
||||
'is_null' => true,
|
||||
'no_homoglyph_names' => true,
|
||||
'no_null_property_initialization' => true,
|
||||
'no_php4_constructor' => true,
|
||||
'no_unused_imports' => true,
|
||||
'no_useless_else' => true,
|
||||
'non_printable_character' => true,
|
||||
'ordered_imports' => true,
|
||||
'ordered_class_elements' => true,
|
||||
'php_unit_construct' => true,
|
||||
'pow_to_exponentiation' => true,
|
||||
'psr_autoloading' => true,
|
||||
'random_api_migration' => true,
|
||||
'return_assignment' => true,
|
||||
'self_accessor' => true,
|
||||
'semicolon_after_instruction' => true,
|
||||
'short_scalar_cast' => true,
|
||||
'simplified_null_return' => true,
|
||||
'single_class_element_per_statement' => true,
|
||||
'single_line_comment_style' => true,
|
||||
'single_quote' => true,
|
||||
'space_after_semicolon' => true,
|
||||
'standardize_not_equals' => true,
|
||||
'strict_param' => true,
|
||||
'ternary_operator_spaces' => true,
|
||||
'trailing_comma_in_multiline' => true,
|
||||
'trim_array_spaces' => true,
|
||||
'unary_operator_spaces' => true,
|
||||
'global_namespace_import' => [
|
||||
'import_classes' => true,
|
||||
'import_functions' => true,
|
||||
'import_constants' => true,
|
||||
],
|
||||
))
|
||||
->setFinder($finder)
|
||||
->setRiskyAllowed(true)
|
||||
;
|
||||
71
vendor/robthree/twofactorauth/CHANGELOG.md
vendored
Normal file
71
vendor/robthree/twofactorauth/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
# RobThree\TwoFactorAuth changelog
|
||||
|
||||
# Version 3.x
|
||||
|
||||
## Breaking changes
|
||||
|
||||
### PHP Version
|
||||
|
||||
Version 3.x requires at least PHP 8.2.
|
||||
|
||||
### Constructor signature change
|
||||
|
||||
In order to ensure users of this library make a conscious choice of QR Code Provider, the QR Code Provider is now a mandatory argument, in first place.
|
||||
|
||||
If you didn't provide one explicitly before, you can get the old behavior with:
|
||||
|
||||
~~~php
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
use RobThree\Auth\Providers\Qr\QRServerProvider;
|
||||
$tfa = new TwoFactorAuth(new QRServerProvider());
|
||||
~~~
|
||||
|
||||
If you provided one before, the order of the parameters have been changed, so simply move the QRCodeProvider argument to the first place or use named arguments.
|
||||
|
||||
Documentation on selecting a QR Code Provider is available here: [QR Code Provider documentation](https://robthree.github.io/TwoFactorAuth/qr-codes.html).
|
||||
|
||||
### Default secret length
|
||||
|
||||
The default secret length has been increased from 80 bits to 160 bits (RFC4226) PR [#117](https://github.com/RobThree/TwoFactorAuth/pull/117). This might cause an issue in your application if you were previously storing secrets in a column with restricted size. This change doesn't impact existing secrets, only new ones will get longer.
|
||||
|
||||
Previously a secret was 16 characters, now it needs to be stored in a 32 characters width column.
|
||||
|
||||
You can keep the old behavior by setting `80` as argument to `createSecret()` (not recommended, see [#117](https://github.com/RobThree/TwoFactorAuth/pull/117) for further discussion).
|
||||
|
||||
## Other changes
|
||||
|
||||
* The new PHP attribute [SensitiveParameter](https://www.php.net/manual/en/class.sensitiveparameter.php) was added to the code, to prevent accidental leak of secrets in stack traces.
|
||||
* Likely not breaking anything, but now all external QR Code providers use HTTPS with a verified certificate. PR [#126](https://github.com/RobThree/TwoFactorAuth/pull/126).
|
||||
* The CSPRNG is now exclusively using `random_bytes()` PHP function. Previously a fallback to `openssl` or non cryptographically secure PRNG existed, they have been removed. PR [#122](https://github.com/RobThree/TwoFactorAuth/pull/122).
|
||||
* If an external QR code provider is used and the HTTP request results in an error, it will throw a `QRException`. Previously the error was ignored. PR [#130](https://github.com/RobThree/TwoFactorAuth/pull/130), fixes [#129](https://github.com/RobThree/TwoFactorAuth/issues/129).
|
||||
|
||||
# Version 2.x
|
||||
|
||||
## Breaking changes
|
||||
|
||||
### PHP Version
|
||||
|
||||
Version 2.x requires at least PHP 8.1.
|
||||
|
||||
### Constructor signature
|
||||
|
||||
With version 2.x, the `algorithm` parameter of `RobThree\Auth\TwoFactorAuth` constructor is now an `enum`.
|
||||
|
||||
On version 1.x:
|
||||
|
||||
~~~php
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
|
||||
$lib = new TwoFactorAuth('issuer-name', 6, 30, 'sha1');
|
||||
~~~
|
||||
|
||||
On version 2.x, simple change the algorithm from a `string` to the correct `enum`:
|
||||
|
||||
~~~php
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
use RobThree\Auth\Algorithm;
|
||||
|
||||
$lib = new TwoFactorAuth('issuer-name', 6, 30, Algorithm::Sha1);
|
||||
~~~
|
||||
|
||||
See the [Algorithm.php](./lib/Algorithm.php) file to see available algorithms.
|
||||
22
vendor/robthree/twofactorauth/LICENSE
vendored
Normal file
22
vendor/robthree/twofactorauth/LICENSE
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2021 Rob Janssen and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
43
vendor/robthree/twofactorauth/README.md
vendored
Normal file
43
vendor/robthree/twofactorauth/README.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
#  PHP library for Two Factor Authentication
|
||||
|
||||
[](https://github.com/RobThree/TwoFactorAuth/actions?query=branch%3Amaster) [](https://packagist.org/packages/robthree/twofactorauth) [](LICENSE) [](https://packagist.org/packages/robthree/twofactorauth) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6MB5M2SQLP636 "Keep me off the streets")
|
||||
|
||||
PHP library for [two-factor (or multi-factor) authentication](http://en.wikipedia.org/wiki/Multi-factor_authentication) using [TOTP](http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) and [QR-codes](http://en.wikipedia.org/wiki/QR_code). Inspired by, based on but most importantly an *improvement* on '[PHPGangsta/GoogleAuthenticator](https://github.com/PHPGangsta/GoogleAuthenticator)'. There's a [.Net implementation](https://github.com/RobThree/TwoFactorAuth.Net) of this library as well.
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/RobThree/TwoFactorAuth/master/multifactorauthforeveryone.png">
|
||||
</p>
|
||||
|
||||
## Requirements
|
||||
|
||||
* Requires PHP version >=8.2
|
||||
|
||||
Optionally, you may need:
|
||||
|
||||
* [sockets](https://www.php.net/manual/en/book.sockets.php) if you are using `NTPTimeProvider`
|
||||
* [endroid/qr-code](https://github.com/endroid/qr-code) if using `EndroidQrCodeProvider` or `EndroidQrCodeWithLogoProvider`.
|
||||
* [bacon/bacon-qr-code](https://github.com/Bacon/BaconQrCode) if using `BaconQrCodeProvider`.
|
||||
* [php-curl library](http://php.net/manual/en/book.curl.php) when using an external QR Code provider such as `QRServerProvider`, `ImageChartsQRCodeProvider`, `QRicketProvider` or any other custom provider connecting to an external service.
|
||||
|
||||
## Installation
|
||||
|
||||
The best way of installing this library is with composer:
|
||||
|
||||
`php composer.phar require robthree/twofactorauth`
|
||||
|
||||
## Usage
|
||||
|
||||
For a quick start, have a look at the [getting started](https://robthree.github.io/TwoFactorAuth/getting-started.html) page or try out the [demo](demo/demo.php).
|
||||
|
||||
If you need more in-depth information about the configuration available then you can read through the rest of [documentation](https://robthree.github.io/TwoFactorAuth).
|
||||
|
||||
## Integrations
|
||||
|
||||
- [CakePHP 3](https://github.com/andrej-griniuk/cakephp-two-factor-auth)
|
||||
- [CI4-Auth: a user, group, role and permission management library for Codeigniter 4](https://github.com/glewe/ci4-auth)
|
||||
|
||||
## License
|
||||
|
||||
Licensed under MIT license. See [LICENSE](./LICENSE) for details.
|
||||
|
||||
[Logo / icon](http://www.iconmay.com/Simple/Travel_and_Tourism_Part_2/luggage_lock_safety_baggage_keys_cylinder_lock_hotel_travel_tourism_luggage_lock_icon_465) under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication ([Archived page](http://riii.nl/tm7ap))
|
||||
51
vendor/robthree/twofactorauth/demo/demo.php
vendored
Normal file
51
vendor/robthree/twofactorauth/demo/demo.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<ol>
|
||||
<?php
|
||||
// in practice you would require the composer loader if it was not already part of your framework or project
|
||||
spl_autoload_register(function ($className) {
|
||||
include_once str_replace(array('RobThree\\Auth', '\\'), array(__DIR__.'/../lib', '/'), $className) . '.php';
|
||||
});
|
||||
|
||||
// substitute your company or app name here
|
||||
$tfa = new RobThree\Auth\TwoFactorAuth(new RobThree\Auth\Providers\Qr\QRServerProvider());
|
||||
?>
|
||||
<li>First create a secret and associate it with a user</li>
|
||||
<?php
|
||||
$secret = $tfa->createSecret();
|
||||
?>
|
||||
<li>
|
||||
Next create a QR code and let the user scan it:<br>
|
||||
<img src="<?php echo $tfa->getQRCodeImageAsDataUri('Demo', $secret); ?>"><br>
|
||||
...or display the secret to the user for manual entry:
|
||||
<?php echo chunk_split($secret, 4, ' '); ?>
|
||||
</li>
|
||||
<?php
|
||||
$code = $tfa->getCode($secret);
|
||||
?>
|
||||
<li>Next, have the user verify the code; at this time the code displayed by a 2FA-app would be: <span style="color:#00c"><?php echo $code; ?></span> (but that changes periodically)</li>
|
||||
<li>When the code checks out, 2FA can be / is enabled; store (encrypted?) secret with user and have the user verify a code each time a new session is started.</li>
|
||||
<li>
|
||||
When aforementioned code (<?php echo $code; ?>) was entered, the result would be:
|
||||
<?php if ($tfa->verifyCode($secret, $code) === true) { ?>
|
||||
<span style="color:#0c0">OK</span>
|
||||
<?php } else { ?>
|
||||
<span style="color:#c00">FAIL</span>
|
||||
<?php } ?>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Note: Make sure your server-time is <a href="http://en.wikipedia.org/wiki/Network_Time_Protocol">NTP-synced</a>! Depending on the $discrepancy allowed your time cannot drift too much from the users' time!</p>
|
||||
<?php
|
||||
try {
|
||||
$tfa->ensureCorrectTime();
|
||||
echo 'Your hosts time seems to be correct / within margin';
|
||||
} catch (RobThree\Auth\TwoFactorAuthException $ex) {
|
||||
echo '<b>Warning:</b> Your hosts time seems to be off: ' . $ex->getMessage();
|
||||
}
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
3
vendor/robthree/twofactorauth/docs/_config.yml
vendored
Normal file
3
vendor/robthree/twofactorauth/docs/_config.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
theme: jekyll-theme-minimal
|
||||
|
||||
logo: https://raw.githubusercontent.com/RobThree/TwoFactorAuth/master/multifactorauthforeveryone.png
|
||||
9
vendor/robthree/twofactorauth/docs/_layouts/post.html
vendored
Normal file
9
vendor/robthree/twofactorauth/docs/_layouts/post.html
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
|
||||
<a href="{{ site.baseurl }}">← contents</a>
|
||||
|
||||
<h1>{{ page.title }}</h1>
|
||||
|
||||
{{ content }}
|
||||
54
vendor/robthree/twofactorauth/docs/assets/css/style.scss
vendored
Normal file
54
vendor/robthree/twofactorauth/docs/assets/css/style.scss
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
---
|
||||
|
||||
@import "{{ site.theme }}";
|
||||
|
||||
// undo some of the theme to allow code samples to be wider
|
||||
header {
|
||||
padding-right: 0;
|
||||
}
|
||||
@media print, screen and (min-width: 961px) {
|
||||
header {
|
||||
border: 1px solid #e5e5e5;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 30px;
|
||||
margin-right: 30px;
|
||||
padding-top: 20px;
|
||||
position: static;
|
||||
text-align: center;
|
||||
}
|
||||
section {
|
||||
float: none;
|
||||
width: auto;
|
||||
}
|
||||
footer {
|
||||
float: none;
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure code samples can be really wide
|
||||
.language-php.highlighter-rouge {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
// add missing consistency
|
||||
header img {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
// quick navigation hack needs some spacing
|
||||
section > a:first-child {
|
||||
display: block;
|
||||
margin-bottom:45px;
|
||||
}
|
||||
|
||||
// 100% width is treated like clear which makes it look bad
|
||||
table {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
// reset document block whatever so the bullets aren't disturbed by the float
|
||||
ul {
|
||||
overflow: hidden;
|
||||
}
|
||||
63
vendor/robthree/twofactorauth/docs/getting-started.md
vendored
Normal file
63
vendor/robthree/twofactorauth/docs/getting-started.md
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
layout: post
|
||||
title: Getting Started
|
||||
---
|
||||
|
||||
## 1. Installation
|
||||
|
||||
The best way of making use of this project is by installing it with [composer](https://getcomposer.org/doc/01-basic-usage.md).
|
||||
|
||||
```
|
||||
composer require robthree/twofactorauth
|
||||
```
|
||||
|
||||
## 2. Create an instance
|
||||
|
||||
`TwoFactorAuth` constructor requires an object able to provide a QR Code image. It is the only mandatory argument. This lets you select your preferred QR Code generator/library.
|
||||
|
||||
See [QR code providers documentation](qr-codes.md) for more information about the different possibilites.
|
||||
|
||||
Example code:
|
||||
|
||||
```php
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
use RobThree\Auth\Providers\Qr\BaconQrCodeProvider; // if using Bacon
|
||||
use RobThree\Auth\Providers\Qr\EndroidQrCodeProvider; // if using Endroid
|
||||
|
||||
// using Bacon
|
||||
$tfa = new TwoFactorAuth(new BaconQrCodeProvider());
|
||||
// using Endroid
|
||||
$tfa = new TwoFactorAuth(new EndroidQrCodeProvider());
|
||||
// using a custom object implementing IQRCodeProvider interface
|
||||
$tfa = new TwoFactorAuth(new MyQrCodeProvider());
|
||||
// using named argument and a variable
|
||||
$tfa = new TwoFactorAuth(qrcodeprovider: $qrGenerator);
|
||||
```
|
||||
|
||||
## 3. Shared secrets
|
||||
|
||||
When your user is setting up two-factor, or multi-factor, authentication in your project, you can create a secret from the instance.
|
||||
|
||||
```php
|
||||
$secret = $tfa->createSecret();
|
||||
```
|
||||
|
||||
Once you have a secret, it can be communicated to the user however you wish.
|
||||
|
||||
```php
|
||||
<p>Please enter the following code in your app: '<?php echo $secret; ?>'</p>
|
||||
```
|
||||
|
||||
**Note:** until you have verified the user is able to use the secret properly, you should store the secret as part of the current session and not save the secret against your user record.
|
||||
|
||||
## 4. Verifying
|
||||
|
||||
Having provided the user with the secret, the best practice is to verify their authenticator app can create the appropriate code.
|
||||
|
||||
```php
|
||||
$result = $tfa->verifyCode($secret, $_POST['verification']);
|
||||
```
|
||||
|
||||
If `$result` is `true` then your user has been able to successfully record the `$secret` in their authenticator app and it has generated an appropriate code.
|
||||
|
||||
You can now save the `$secret` to your user record and use the same `verifyCode` method each time they log in.
|
||||
32
vendor/robthree/twofactorauth/docs/improved-code-verification.md
vendored
Normal file
32
vendor/robthree/twofactorauth/docs/improved-code-verification.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
layout: post
|
||||
title: Improved Code Verification
|
||||
---
|
||||
|
||||
When verifying codes that a user has entered, there are other optional arguments which can improve verification of the code.
|
||||
|
||||
```php
|
||||
$result = $tfa->verifyCode($secret, $_POST['verification'], $discrepancy, $time, &$timeslice);
|
||||
```
|
||||
|
||||
## Discrepancy (default 1)
|
||||
|
||||
As the codes that are generated and accepted are consistent within a certain time window (i.e. a timeslice, 30 seconds long by default), it is very important that the server (and the users authenticator app) have the correct time (and date).
|
||||
|
||||
The value of `$discrepancy` is the number of timeslices checked in **both** directions of the current one. So when the current time is `14:34:21`, the 'current timeslice' is `14:34:00` to `14:34:30`. If the default is left unchanged, we also verify the code against the timeslice of `14:33:30` to `14:34:00` and for `14:34:30` to `14:35:00`.
|
||||
|
||||
This should be sufficient for most cases however you can increase it if you wish. It would be unwise for this to be too high as it could allow a code to be valid for long enough that it could be used fraudulently.
|
||||
|
||||
## Time (default null)
|
||||
|
||||
The second, `$time`, allows you to check a code for a specific point in time. This argument has no real practical use but can be handy for unit testing. The default value, `null`, means: use the current time.
|
||||
|
||||
## Timeslice
|
||||
|
||||
`$timeslice` returns a value by reference. The value returned is the timeslice that matched the code (if any) or `0`.
|
||||
|
||||
You can store a timeslice alongside the secret and verify that any new timeslice is greater than the existing one.
|
||||
|
||||
i.e. if `verifyCode` returns true _and_ the returned timeslice is greater than the last used timeslice for this user/secret then this is the first time the code has been used and you should now store the higher timeslice to verify that the user.
|
||||
|
||||
This is an effective defense against a [replay attack](https://en.wikipedia.org/wiki/Replay_attack).
|
||||
18
vendor/robthree/twofactorauth/docs/index.md
vendored
Normal file
18
vendor/robthree/twofactorauth/docs/index.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: Contents
|
||||
---
|
||||
|
||||
## [The Basics - Getting Started](getting-started.html)
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
[QR Codes](qr-codes.html)
|
||||
- [QRServerProvider](qr-codes/qr-server.html)
|
||||
- [ImageChartsQRCodeProvider](qr-codes/image-charts.html)
|
||||
- [QRicketProvider](qr-codes/qrickit.html)
|
||||
- [EndroidQrCodeProvider](qr-codes/endroid.html) (and EndroidQrCodeWithLogoProvider)
|
||||
- [BaconQRCodeProvider](qr-codes/bacon.html)
|
||||
|
||||
[Improved Code Verification](improved-code-verification.html)
|
||||
|
||||
[Other Optional Configuration](optional-configuration.html)
|
||||
47
vendor/robthree/twofactorauth/docs/optional-configuration.md
vendored
Normal file
47
vendor/robthree/twofactorauth/docs/optional-configuration.md
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
layout: post
|
||||
title: Optional Configuration
|
||||
---
|
||||
|
||||
## Instance Configuration
|
||||
|
||||
The instance (`new TwoFactorAuth()`) can only be configured by the constructor with the following optional arguments
|
||||
|
||||
Argument | Default value | Use
|
||||
------------------|-------------------|-----
|
||||
`$issuer` | `null` | Will be displayed in the users app as the default issuer name when using QR code to import the secret
|
||||
`$digits` | `6` | The number of digits the resulting codes will be
|
||||
`$period` | `30` | The number of seconds a code will be valid
|
||||
`$algorithm` | `Algorithm::Sha1` | The algorithm used (one of `Algorithm::Sha1`, `Algorithm::Sha256`, `Algorithm::Sha512`, `Algorithm::Md5`)
|
||||
`$qrcodeprovider` | `null` | QR-code provider
|
||||
`$rngprovider` | `null` | Random Number Generator provider
|
||||
`$timeprovider` | `null` | Time provider
|
||||
|
||||
**Note:** the default values for `$digits`, `$period`, and `$algorithm` provide the widest variety of support amongst common authenticator apps such as Google Authenticator. If you choose to use different values for these arguments you will likely have to instruct your users to use a specific app which supports your chosen configuration.
|
||||
|
||||
### RNG providers
|
||||
|
||||
Should you feel the need to use a CSPRNG different than `random_bytes()`, you can use the `rngprovider` argument of the constructor to provide an object implementing the [`IRNGProvider`](https://github.com/RobThree/TwoFactorAuth/blob/master/lib/Providers/Rng/IRNGProvider.php) interface.
|
||||
|
||||
### Time providers
|
||||
|
||||
These allow the TwoFactorAuth library to ensure the servers time is correct (or at least within a margin).
|
||||
|
||||
You can use the `ensureCorrectTime()` method to ensure the hosts time is correct. By default this method will compare the hosts time (returned by calling `time()` on the `LocalMachineTimeProvider`) to the default `NTPTimeProvider` and `HttpTimeProvider`.
|
||||
|
||||
**Note:** the `NTPTimeProvider` requires your PHP to have the ability to create sockets. If you do not have that ability and wish to use this function, you should pass an array with only an instance of `HttpTimeProvider`.
|
||||
|
||||
Alternatively, you can pass an array of classes that implement the [`ITimeProvider` interface](https://github.com/RobThree/TwoFactorAuth/blob/master/lib/Providers/Time/ITimeProvider.php) to change this and specify the second argument, leniency in seconds (default: 5). An exception will be thrown if the time difference is greater than the leniency.
|
||||
|
||||
Ordinarily, you should not need to monitor that the time on the server is correct in this way however if you choose to, we advise to call this method sparingly when relying on 3rd parties (which both the `HttpTimeProvider` and `NTPTimeProvider` do) or, if you need to ensure time is correct on a (very) regular basis to implement an `ITimeProvider` that is more efficient than the built-in ones (making use of a GPS signal for example).
|
||||
|
||||
## Secret Configuration
|
||||
|
||||
Secrets can be optionally configured with the following optional arguments
|
||||
|
||||
Argument | Default value | Use
|
||||
-----------------------|---------------|-----
|
||||
`$bits` | `80` | The number of bits (related to the length of the secret)
|
||||
`$requirecryptosecure` | `true` | Whether you want to require a cryptographically secure source of random numbers
|
||||
|
||||
**Note:** as above, these values provide the widest variety of support amongst common authenticator apps however you may choose to increase the value of `$bits` (160 or higher is recommended, see [RFC 4226 - Algorithm Requirements](https://tools.ietf.org/html/rfc4226#section-4)) as long as it is set to a multiple of 8.
|
||||
58
vendor/robthree/twofactorauth/docs/qr-codes.md
vendored
Normal file
58
vendor/robthree/twofactorauth/docs/qr-codes.md
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
layout: post
|
||||
title: QR Codes
|
||||
---
|
||||
|
||||
An alternative way of communicating the secret to the user is through the use of [QR Codes](http://en.wikipedia.org/wiki/QR_code) which most if not all authenticator mobile apps can scan.
|
||||
|
||||
This can avoid accidental typing errors and also pre-set some text values within the two factor authentication mobile application.
|
||||
|
||||
You can display the QR Code as a base64 encoded image using the instance as follows, supplying the users name or other public identifier as the first argument
|
||||
|
||||
````php
|
||||
<p>Scan the following image with your app:</p>
|
||||
<img src="<?php echo $tfa->getQRCodeImageAsDataUri('Bob Ross', $secret); ?>">
|
||||
````
|
||||
|
||||
You can also specify a size as a third argument which is 200 by default.
|
||||
|
||||
## Offline Providers
|
||||
|
||||
[EndroidQrCodeProvider](qr-codes/endroid.md) and EndroidQrCodeWithLogoProvider
|
||||
|
||||
[BaconQRCodeProvider](qr-codes/bacon.md)
|
||||
|
||||
**Note:** offline providers may have additional PHP requirements in order to function, you should study what is required before trying to make use of them.
|
||||
|
||||
## Custom Provider
|
||||
|
||||
If you wish to make your own QR Code provider to reference another service or library, it must implement the [IQRCodeProvider interface](../lib/Providers/Qr/IQRCodeProvider.php).
|
||||
|
||||
It is recommended to use similar constructor arguments as the included providers to avoid big shifts when trying different providers.
|
||||
|
||||
Example:
|
||||
|
||||
```php
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
// using a custom object implementing IQRCodeProvider
|
||||
$tfa = new TwoFactorAuth(new MyQrCodeProvider());
|
||||
// using named argument and a variable
|
||||
$tfa = new TwoFactorAuth(qrcodeprovider: $qrGenerator);
|
||||
```
|
||||
|
||||
## Online Providers
|
||||
|
||||
**Warning:** Using an external service for generating QR codes encoding authentication secrets is **not** recommended! You should instead make use of the included offline providers listed above.
|
||||
|
||||
* Gogr.me: [QRServerProvider](qr-codes/qr-server.md)
|
||||
* Image Charts: [ImageChartsQRCodeProvider](qr-codes/image-charts.md)
|
||||
* Qrickit: [QRicketProvider](qr-codes/qrickit.md)
|
||||
* Google Charts: [GoogleChartsQrCodeProvider](qr-codes/google-charts.md)
|
||||
|
||||
Example:
|
||||
|
||||
```php
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
use RobThree\Auth\Providers\Qr\GoogleChartsQrCodeProvider;
|
||||
$tfa = new TwoFactorAuth(new GoogleChartsQrCodeProvider());
|
||||
```
|
||||
23
vendor/robthree/twofactorauth/docs/qr-codes/bacon.md
vendored
Normal file
23
vendor/robthree/twofactorauth/docs/qr-codes/bacon.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
layout: post
|
||||
title: bacon/bacon-qr-code
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
In order to use this provider, you will need to install the library at version 2 (or later) and its dependencies
|
||||
|
||||
```
|
||||
composer require bacon/bacon-qr-code ^2.0
|
||||
```
|
||||
|
||||
You will also need the PHP imagick extension **if** you aren't using the SVG format.
|
||||
|
||||
## Optional Configuration
|
||||
|
||||
Argument | Default value
|
||||
--------------------|---------------
|
||||
`$borderWidth` | `4`
|
||||
`$backgroundColour` | `'#ffffff'`
|
||||
`$foregroundColour` | `'#000000'`
|
||||
`$format` | `'png'`
|
||||
37
vendor/robthree/twofactorauth/docs/qr-codes/endroid.md
vendored
Normal file
37
vendor/robthree/twofactorauth/docs/qr-codes/endroid.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
layout: post
|
||||
title: endroid/qr-code
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
In order to use this provider, you will need to install the library at version 3 and its dependencies
|
||||
|
||||
```
|
||||
composer require endroid/qr-code ^3.0
|
||||
```
|
||||
|
||||
You will also need the PHP gd extension installing.
|
||||
|
||||
## Optional Configuration
|
||||
|
||||
Argument | Default value
|
||||
------------------------|---------------
|
||||
`$bgcolor` | `'ffffff'`
|
||||
`$color` | `'000000'`
|
||||
`$margin` | `0`
|
||||
`$errorcorrectionlevel` | `'H'`
|
||||
|
||||
## Logo
|
||||
|
||||
If you make use of `EndroidQrCodeWithLogoProvider` then you have access to the `setLogo` function on the provider so you may add a logo to the centre of your QR code.
|
||||
|
||||
```php
|
||||
use RobThree\Auth\Providers\Qr\EndroidQrCodeWithLogoProvider;
|
||||
|
||||
$qrCodeProvider = new EndroidQrCodeWithLogoProvider();
|
||||
|
||||
$qrCodeProvider->setLogo('/path/to/your/image');
|
||||
```
|
||||
|
||||
You can see how to also set the size of the logo in the [source code](https://github.com/RobThree/TwoFactorAuth/blob/master/lib/Providers/Qr/EndroidQrCodeWithLogoProvider.php).
|
||||
15
vendor/robthree/twofactorauth/docs/qr-codes/google-charts.md
vendored
Normal file
15
vendor/robthree/twofactorauth/docs/qr-codes/google-charts.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
layout: post
|
||||
title: QR GoogleCharts
|
||||
---
|
||||
|
||||
See: https://developers.google.com/chart/infographics/docs/qr_codes
|
||||
|
||||
## Optional Configuration
|
||||
|
||||
Argument | Default value
|
||||
------------------------|---------------
|
||||
`$verifyssl` | `false`
|
||||
`$errorcorrectionlevel` | `'L'`
|
||||
`$margin` | `4`
|
||||
`$encoding` | `'UTF-8'`
|
||||
16
vendor/robthree/twofactorauth/docs/qr-codes/image-charts.md
vendored
Normal file
16
vendor/robthree/twofactorauth/docs/qr-codes/image-charts.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
layout: post
|
||||
title: Image-Charts
|
||||
---
|
||||
|
||||
## Optional Configuration
|
||||
|
||||
Argument | Default value
|
||||
------------------------|---------------
|
||||
`$verifyssl` | `true`
|
||||
`$errorcorrectionlevel` | `'L'`
|
||||
`$margin` | `4`
|
||||
|
||||
`$verifyssl` is used internally to help guarantee the security of the connection. It is possible that where you are running the code from will have problems verifying an SSL connection so if you know this is not the case, you can supply `true`.
|
||||
|
||||
The other parameters are passed to [Image-Charts](https://documentation.image-charts.com/qr-codes/) so you can refer to them for more detail on how the values are used.
|
||||
20
vendor/robthree/twofactorauth/docs/qr-codes/qr-server.md
vendored
Normal file
20
vendor/robthree/twofactorauth/docs/qr-codes/qr-server.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
layout: post
|
||||
title: QR Server
|
||||
---
|
||||
|
||||
## Optional Configuration
|
||||
|
||||
Argument | Default value
|
||||
------------------------|---------------
|
||||
`$verifyssl` | `true`
|
||||
`$errorcorrectionlevel` | `'L'`
|
||||
`$margin` | `4`
|
||||
`$qzone` | `1`
|
||||
`$bgcolor` | `'ffffff'`
|
||||
`$color` | `'000000'`
|
||||
`$format` | `'png'`
|
||||
|
||||
`$verifyssl` is used internally to help guarantee the security of the connection. It is possible that where you are running the code from will have problems verifying an SSL connection so if you know this is not the case, you can supply `true`.
|
||||
|
||||
The other parameters are passed to [goqr.me](http://goqr.me/api/doc/create-qr-code/) so you can refer to them for more detail on how the values are used.
|
||||
15
vendor/robthree/twofactorauth/docs/qr-codes/qrickit.md
vendored
Normal file
15
vendor/robthree/twofactorauth/docs/qr-codes/qrickit.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
layout: post
|
||||
title: QRickit
|
||||
---
|
||||
|
||||
## Optional Configuration
|
||||
|
||||
Argument | Default value
|
||||
------------------------|---------------
|
||||
`$errorcorrectionlevel` | `'L'`
|
||||
`$bgcolor` | `'ffffff'`
|
||||
`$color` | `'000000'`
|
||||
`$format` | `'png'`
|
||||
|
||||
The parameters are passed to [QRickit](http://qrickit.com/qrickit_apps/qrickit_api.php) so you can refer to them for more detail on how the values are used.
|
||||
16
vendor/robthree/twofactorauth/lib/Algorithm.php
vendored
Normal file
16
vendor/robthree/twofactorauth/lib/Algorithm.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth;
|
||||
|
||||
/**
|
||||
* List of supported cryptographic algorithms
|
||||
*/
|
||||
enum Algorithm: string
|
||||
{
|
||||
case Md5 = 'md5';
|
||||
case Sha1 = 'sha1';
|
||||
case Sha256 = 'sha256';
|
||||
case Sha512 = 'sha512';
|
||||
}
|
||||
141
vendor/robthree/twofactorauth/lib/Providers/Qr/BaconQrCodeProvider.php
vendored
Normal file
141
vendor/robthree/twofactorauth/lib/Providers/Qr/BaconQrCodeProvider.php
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Qr;
|
||||
|
||||
use BaconQrCode\Renderer\Color\Rgb;
|
||||
use BaconQrCode\Renderer\Image\EpsImageBackEnd;
|
||||
use BaconQrCode\Renderer\Image\ImageBackEndInterface;
|
||||
use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
|
||||
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
|
||||
use BaconQrCode\Renderer\ImageRenderer;
|
||||
use BaconQrCode\Renderer\RendererStyle\EyeFill;
|
||||
use BaconQrCode\Renderer\RendererStyle\Fill;
|
||||
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
|
||||
use BaconQrCode\Writer;
|
||||
use RuntimeException;
|
||||
|
||||
class BaconQrCodeProvider implements IQRCodeProvider
|
||||
{
|
||||
/**
|
||||
* Ensure we using the latest Bacon QR Code and specify default options
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly int $borderWidth = 4,
|
||||
private string|array $backgroundColour = '#ffffff',
|
||||
private string|array $foregroundColour = '#000000',
|
||||
private string $format = 'png',
|
||||
) {
|
||||
$this->backgroundColour = $this->handleColour($this->backgroundColour);
|
||||
$this->foregroundColour = $this->handleColour($this->foregroundColour);
|
||||
$this->format = strtolower($this->format);
|
||||
}
|
||||
|
||||
public function getMimeType(): string
|
||||
{
|
||||
switch ($this->format) {
|
||||
case 'png':
|
||||
return 'image/png';
|
||||
case 'gif':
|
||||
return 'image/gif';
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
return 'image/jpeg';
|
||||
case 'svg':
|
||||
return 'image/svg+xml';
|
||||
case 'eps':
|
||||
return 'application/postscript';
|
||||
}
|
||||
|
||||
throw new RuntimeException(sprintf('Unknown MIME-type: %s', $this->format));
|
||||
}
|
||||
|
||||
public function getQRCodeImage(string $qrText, int $size): string
|
||||
{
|
||||
$backend = match ($this->format) {
|
||||
'svg' => new SvgImageBackEnd(),
|
||||
'eps' => new EpsImageBackEnd(),
|
||||
default => new ImagickImageBackEnd($this->format),
|
||||
};
|
||||
|
||||
$output = $this->getQRCodeByBackend($qrText, $size, $backend);
|
||||
|
||||
if ($this->format === 'svg') {
|
||||
$svg = explode("\n", $output);
|
||||
return $svg[1];
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract QR code generation function
|
||||
* providing colour changing support
|
||||
*/
|
||||
private function getQRCodeByBackend($qrText, $size, ImageBackEndInterface $backend)
|
||||
{
|
||||
$rendererStyleArgs = array($size, $this->borderWidth);
|
||||
|
||||
if (is_array($this->foregroundColour) && is_array($this->backgroundColour)) {
|
||||
$rendererStyleArgs = array(...$rendererStyleArgs, ...array(
|
||||
null,
|
||||
null,
|
||||
Fill::withForegroundColor(
|
||||
new Rgb(...$this->backgroundColour),
|
||||
new Rgb(...$this->foregroundColour),
|
||||
new EyeFill(null, null),
|
||||
new EyeFill(null, null),
|
||||
new EyeFill(null, null)
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
$writer = new Writer(new ImageRenderer(
|
||||
new RendererStyle(...$rendererStyleArgs),
|
||||
$backend
|
||||
));
|
||||
|
||||
return $writer->writeString($qrText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure colour is an array of three values but also
|
||||
* accept a string and assume its a 3 or 6 character hex
|
||||
*/
|
||||
private function handleColour(array|string $colour): array|string
|
||||
{
|
||||
if (is_string($colour) && $colour[0] == '#') {
|
||||
$hexToRGB = static function ($input) {
|
||||
// ensure input no longer has a # for more predictable division
|
||||
// PHP 8.1 does not like implicitly casting a float to an int
|
||||
$input = trim($input, '#');
|
||||
|
||||
if (strlen($input) != 3 && strlen($input) != 6) {
|
||||
throw new RuntimeException('Colour should be a 3 or 6 character value after the #');
|
||||
}
|
||||
|
||||
// split the array into three chunks
|
||||
$split = str_split($input, strlen($input) / 3);
|
||||
|
||||
// cope with three character hex reference
|
||||
if (strlen($input) == 3) {
|
||||
array_walk($split, static function (&$character) {
|
||||
$character = str_repeat($character, 2);
|
||||
});
|
||||
}
|
||||
|
||||
// convert hex to rgb
|
||||
return array_map('hexdec', $split);
|
||||
};
|
||||
|
||||
return $hexToRGB($colour);
|
||||
}
|
||||
|
||||
if (is_array($colour) && count($colour) == 3) {
|
||||
return $colour;
|
||||
}
|
||||
|
||||
throw new RuntimeException('Invalid colour value');
|
||||
}
|
||||
}
|
||||
32
vendor/robthree/twofactorauth/lib/Providers/Qr/BaseHTTPQRCodeProvider.php
vendored
Normal file
32
vendor/robthree/twofactorauth/lib/Providers/Qr/BaseHTTPQRCodeProvider.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Qr;
|
||||
|
||||
abstract class BaseHTTPQRCodeProvider implements IQRCodeProvider
|
||||
{
|
||||
protected bool $verifyssl = true;
|
||||
|
||||
protected function getContent(string $url): string
|
||||
{
|
||||
$curlhandle = curl_init();
|
||||
|
||||
curl_setopt_array($curlhandle, array(
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 10,
|
||||
CURLOPT_DNS_CACHE_TIMEOUT => 10,
|
||||
CURLOPT_TIMEOUT => 10,
|
||||
CURLOPT_SSL_VERIFYPEER => $this->verifyssl,
|
||||
CURLOPT_USERAGENT => 'TwoFactorAuth',
|
||||
));
|
||||
$data = curl_exec($curlhandle);
|
||||
if ($data === false) {
|
||||
throw new QRException(curl_error($curlhandle));
|
||||
}
|
||||
|
||||
curl_close($curlhandle);
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
123
vendor/robthree/twofactorauth/lib/Providers/Qr/EndroidQrCodeProvider.php
vendored
Normal file
123
vendor/robthree/twofactorauth/lib/Providers/Qr/EndroidQrCodeProvider.php
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Qr;
|
||||
|
||||
use Endroid\QrCode\Color\Color;
|
||||
use Endroid\QrCode\ErrorCorrectionLevel;
|
||||
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh;
|
||||
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelInterface;
|
||||
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelLow;
|
||||
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelMedium;
|
||||
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelQuartile;
|
||||
use Endroid\QrCode\QrCode;
|
||||
use Endroid\QrCode\Writer\PngWriter;
|
||||
|
||||
class EndroidQrCodeProvider implements IQRCodeProvider
|
||||
{
|
||||
public $bgcolor;
|
||||
|
||||
public $color;
|
||||
|
||||
public $margin;
|
||||
|
||||
public $errorcorrectionlevel;
|
||||
|
||||
protected $endroid4 = false;
|
||||
|
||||
protected $endroid5 = false;
|
||||
|
||||
protected $endroid6 = false;
|
||||
|
||||
public function __construct($bgcolor = 'ffffff', $color = '000000', $margin = 0, $errorcorrectionlevel = 'H')
|
||||
{
|
||||
$this->endroid5 = enum_exists(ErrorCorrectionLevel::class);
|
||||
$this->endroid6 = $this->endroid5 && !method_exists(QrCode::class, 'setSize');
|
||||
$this->endroid4 = $this->endroid6 || method_exists(QrCode::class, 'create');
|
||||
|
||||
$this->bgcolor = $this->handleColor($bgcolor);
|
||||
$this->color = $this->handleColor($color);
|
||||
$this->margin = $margin;
|
||||
$this->errorcorrectionlevel = $this->handleErrorCorrectionLevel($errorcorrectionlevel);
|
||||
}
|
||||
|
||||
public function getMimeType(): string
|
||||
{
|
||||
return 'image/png';
|
||||
}
|
||||
|
||||
public function getQRCodeImage(string $qrText, int $size): string
|
||||
{
|
||||
if (!$this->endroid4) {
|
||||
return $this->qrCodeInstance($qrText, $size)->writeString();
|
||||
}
|
||||
|
||||
$writer = new PngWriter();
|
||||
return $writer->write($this->qrCodeInstance($qrText, $size))->getString();
|
||||
}
|
||||
|
||||
protected function qrCodeInstance(string $qrText, int $size): QrCode
|
||||
{
|
||||
if ($this->endroid6) {
|
||||
return new QrCode(
|
||||
data: $qrText,
|
||||
errorCorrectionLevel: $this->errorcorrectionlevel,
|
||||
size: $size,
|
||||
margin: $this->margin,
|
||||
foregroundColor: $this->color,
|
||||
backgroundColor: $this->bgcolor
|
||||
);
|
||||
}
|
||||
|
||||
$qrCode = new QrCode($qrText);
|
||||
$qrCode->setSize($size);
|
||||
|
||||
$qrCode->setErrorCorrectionLevel($this->errorcorrectionlevel);
|
||||
$qrCode->setMargin($this->margin);
|
||||
$qrCode->setBackgroundColor($this->bgcolor);
|
||||
$qrCode->setForegroundColor($this->color);
|
||||
return $qrCode;
|
||||
}
|
||||
|
||||
private function handleColor(string $color): Color|array
|
||||
{
|
||||
$split = str_split($color, 2);
|
||||
$r = hexdec($split[0]);
|
||||
$g = hexdec($split[1]);
|
||||
$b = hexdec($split[2]);
|
||||
|
||||
return $this->endroid4 ? new Color($r, $g, $b, 0) : array('r' => $r, 'g' => $g, 'b' => $b, 'a' => 0);
|
||||
}
|
||||
|
||||
private function handleErrorCorrectionLevel(string $level): ErrorCorrectionLevelInterface|ErrorCorrectionLevel
|
||||
{
|
||||
// First check for version 5 (using enums)
|
||||
if ($this->endroid5) {
|
||||
return match ($level) {
|
||||
'L' => ErrorCorrectionLevel::Low,
|
||||
'M' => ErrorCorrectionLevel::Medium,
|
||||
'Q' => ErrorCorrectionLevel::Quartile,
|
||||
default => ErrorCorrectionLevel::High,
|
||||
};
|
||||
}
|
||||
|
||||
// If not check for version 4 (using classes)
|
||||
if ($this->endroid4) {
|
||||
return match ($level) {
|
||||
'L' => new ErrorCorrectionLevelLow(),
|
||||
'M' => new ErrorCorrectionLevelMedium(),
|
||||
'Q' => new ErrorCorrectionLevelQuartile(),
|
||||
default => new ErrorCorrectionLevelHigh(),
|
||||
};
|
||||
}
|
||||
|
||||
// Any other version will be using strings
|
||||
return match ($level) {
|
||||
'L' => ErrorCorrectionLevel::LOW(),
|
||||
'M' => ErrorCorrectionLevel::MEDIUM(),
|
||||
'Q' => ErrorCorrectionLevel::QUARTILE(),
|
||||
default => ErrorCorrectionLevel::HIGH(),
|
||||
};
|
||||
}
|
||||
}
|
||||
65
vendor/robthree/twofactorauth/lib/Providers/Qr/EndroidQrCodeWithLogoProvider.php
vendored
Normal file
65
vendor/robthree/twofactorauth/lib/Providers/Qr/EndroidQrCodeWithLogoProvider.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Qr;
|
||||
|
||||
use Endroid\QrCode\Logo\Logo;
|
||||
use Endroid\QrCode\QrCode;
|
||||
use Endroid\QrCode\Writer\PngWriter;
|
||||
|
||||
class EndroidQrCodeWithLogoProvider extends EndroidQrCodeProvider
|
||||
{
|
||||
protected $logoPath;
|
||||
|
||||
protected $logoSize;
|
||||
|
||||
/**
|
||||
* Adds an image to the middle of the QR Code.
|
||||
* @param string $path Path to an image file
|
||||
* @param array|int $size Just the width, or [width, height]
|
||||
*/
|
||||
public function setLogo($path, $size = null)
|
||||
{
|
||||
$this->logoPath = $path;
|
||||
$this->logoSize = (array)$size;
|
||||
}
|
||||
|
||||
public function getQRCodeImage(string $qrText, int $size): string
|
||||
{
|
||||
if (!$this->endroid4) {
|
||||
return $this->qrCodeInstance($qrText, $size)->writeString();
|
||||
}
|
||||
|
||||
$logo = null;
|
||||
if ($this->logoPath) {
|
||||
if ($this->endroid6) {
|
||||
$logo = new Logo($this->logoPath, ...$this->logoSize);
|
||||
} else {
|
||||
$logo = Logo::create($this->logoPath);
|
||||
if ($this->logoSize) {
|
||||
$logo->setResizeToWidth($this->logoSize[0]);
|
||||
if (isset($this->logoSize[1])) {
|
||||
$logo->setResizeToHeight($this->logoSize[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$writer = new PngWriter();
|
||||
return $writer->write($this->qrCodeInstance($qrText, $size), $logo)->getString();
|
||||
}
|
||||
|
||||
protected function qrCodeInstance(string $qrText, int $size): QrCode
|
||||
{
|
||||
$qrCode = parent::qrCodeInstance($qrText, $size);
|
||||
|
||||
if (!$this->endroid4 && $this->logoPath) {
|
||||
$qrCode->setLogoPath($this->logoPath);
|
||||
if ($this->logoSize) {
|
||||
$qrCode->setLogoSize($this->logoSize[0], $this->logoSize[1] ?? null);
|
||||
}
|
||||
}
|
||||
|
||||
return $qrCode;
|
||||
}
|
||||
}
|
||||
36
vendor/robthree/twofactorauth/lib/Providers/Qr/GoogleChartsQrCodeProvider.php
vendored
Normal file
36
vendor/robthree/twofactorauth/lib/Providers/Qr/GoogleChartsQrCodeProvider.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Qr;
|
||||
|
||||
// https://developers.google.com/chart/infographics/docs/qr_codes
|
||||
class GoogleChartsQrCodeProvider extends BaseHTTPQRCodeProvider
|
||||
{
|
||||
public function __construct(protected bool $verifyssl = true, public string $errorcorrectionlevel = 'L', public int $margin = 4, public string $encoding = 'UTF-8')
|
||||
{
|
||||
}
|
||||
|
||||
public function getMimeType(): string
|
||||
{
|
||||
return 'image/png';
|
||||
}
|
||||
|
||||
public function getQRCodeImage(string $qrText, int $size): string
|
||||
{
|
||||
return $this->getContent($this->getUrl($qrText, $size));
|
||||
}
|
||||
|
||||
public function getUrl(string $qrText, int $size): string
|
||||
{
|
||||
$queryParameters = array(
|
||||
'chs' => $size . 'x' . $size,
|
||||
'chld' => strtoupper($this->errorcorrectionlevel) . '|' . $this->margin,
|
||||
'cht' => 'qr',
|
||||
'choe' => $this->encoding,
|
||||
'chl' => $qrText,
|
||||
);
|
||||
|
||||
return 'https://chart.googleapis.com/chart?' . http_build_query($queryParameters);
|
||||
}
|
||||
}
|
||||
27
vendor/robthree/twofactorauth/lib/Providers/Qr/HandlesDataUri.php
vendored
Normal file
27
vendor/robthree/twofactorauth/lib/Providers/Qr/HandlesDataUri.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Qr;
|
||||
|
||||
use function base64_decode;
|
||||
use function preg_match;
|
||||
|
||||
trait HandlesDataUri
|
||||
{
|
||||
/**
|
||||
* @return array<string, string>|null
|
||||
*/
|
||||
private function DecodeDataUri(string $datauri): ?array
|
||||
{
|
||||
if (preg_match('/data:(?P<mimetype>[\w\.\-\+\/]+);(?P<encoding>\w+),(?P<data>.*)/', $datauri, $m) === 1) {
|
||||
return array(
|
||||
'mimetype' => $m['mimetype'],
|
||||
'encoding' => $m['encoding'],
|
||||
'data' => base64_decode($m['data'], true),
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
24
vendor/robthree/twofactorauth/lib/Providers/Qr/IQRCodeProvider.php
vendored
Normal file
24
vendor/robthree/twofactorauth/lib/Providers/Qr/IQRCodeProvider.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Qr;
|
||||
|
||||
interface IQRCodeProvider
|
||||
{
|
||||
/**
|
||||
* Generate and return the QR code to embed in a web page
|
||||
*
|
||||
* @param string $qrText the value to encode in the QR code
|
||||
* @param int $size the desired size of the QR code
|
||||
*
|
||||
* @return string file contents of the QR code
|
||||
*/
|
||||
public function getQRCodeImage(string $qrText, int $size): string;
|
||||
|
||||
/**
|
||||
* Returns the appropriate mime type for the QR code
|
||||
* that will be generated
|
||||
*/
|
||||
public function getMimeType(): string;
|
||||
}
|
||||
37
vendor/robthree/twofactorauth/lib/Providers/Qr/ImageChartsQRCodeProvider.php
vendored
Normal file
37
vendor/robthree/twofactorauth/lib/Providers/Qr/ImageChartsQRCodeProvider.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Qr;
|
||||
|
||||
/**
|
||||
* Use https://image-charts.com to provide a QR code
|
||||
*/
|
||||
class ImageChartsQRCodeProvider extends BaseHTTPQRCodeProvider
|
||||
{
|
||||
public function __construct(protected bool $verifyssl = true, public string $errorcorrectionlevel = 'L', public int $margin = 1)
|
||||
{
|
||||
}
|
||||
|
||||
public function getMimeType(): string
|
||||
{
|
||||
return 'image/png';
|
||||
}
|
||||
|
||||
public function getQRCodeImage(string $qrText, int $size): string
|
||||
{
|
||||
return $this->getContent($this->getUrl($qrText, $size));
|
||||
}
|
||||
|
||||
public function getUrl(string $qrText, int $size): string
|
||||
{
|
||||
$queryParameters = array(
|
||||
'cht' => 'qr',
|
||||
'chs' => ceil($size / 2) . 'x' . ceil($size / 2),
|
||||
'chld' => $this->errorcorrectionlevel . '|' . $this->margin,
|
||||
'chl' => $qrText,
|
||||
);
|
||||
|
||||
return 'https://image-charts.com/chart?' . http_build_query($queryParameters);
|
||||
}
|
||||
}
|
||||
11
vendor/robthree/twofactorauth/lib/Providers/Qr/QRException.php
vendored
Normal file
11
vendor/robthree/twofactorauth/lib/Providers/Qr/QRException.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Qr;
|
||||
|
||||
use RobThree\Auth\TwoFactorAuthException;
|
||||
|
||||
class QRException extends TwoFactorAuthException
|
||||
{
|
||||
}
|
||||
59
vendor/robthree/twofactorauth/lib/Providers/Qr/QRServerProvider.php
vendored
Normal file
59
vendor/robthree/twofactorauth/lib/Providers/Qr/QRServerProvider.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Qr;
|
||||
|
||||
/**
|
||||
* Use https://goqr.me/api/doc/create-qr-code/ to get QR code
|
||||
*/
|
||||
class QRServerProvider extends BaseHTTPQRCodeProvider
|
||||
{
|
||||
public function __construct(protected bool $verifyssl = true, public string $errorcorrectionlevel = 'L', public int $margin = 4, public int $qzone = 1, public string $bgcolor = 'ffffff', public string $color = '000000', public string $format = 'png')
|
||||
{
|
||||
}
|
||||
|
||||
public function getMimeType(): string
|
||||
{
|
||||
switch (strtolower($this->format)) {
|
||||
case 'png':
|
||||
return 'image/png';
|
||||
case 'gif':
|
||||
return 'image/gif';
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
return 'image/jpeg';
|
||||
case 'svg':
|
||||
return 'image/svg+xml';
|
||||
case 'eps':
|
||||
return 'application/postscript';
|
||||
}
|
||||
throw new QRException(sprintf('Unknown MIME-type: %s', $this->format));
|
||||
}
|
||||
|
||||
public function getQRCodeImage(string $qrText, int $size): string
|
||||
{
|
||||
return $this->getContent($this->getUrl($qrText, $size));
|
||||
}
|
||||
|
||||
public function getUrl(string $qrText, int $size): string
|
||||
{
|
||||
$queryParameters = array(
|
||||
'size' => $size . 'x' . $size,
|
||||
'ecc' => strtoupper($this->errorcorrectionlevel),
|
||||
'margin' => $this->margin,
|
||||
'qzone' => $this->qzone,
|
||||
'bgcolor' => $this->decodeColor($this->bgcolor),
|
||||
'color' => $this->decodeColor($this->color),
|
||||
'format' => strtolower($this->format),
|
||||
'data' => $qrText,
|
||||
);
|
||||
|
||||
return 'https://api.qrserver.com/v1/create-qr-code/?' . http_build_query($queryParameters);
|
||||
}
|
||||
|
||||
private function decodeColor(string $value): string
|
||||
{
|
||||
return vsprintf('%d-%d-%d', sscanf($value, '%02x%02x%02x'));
|
||||
}
|
||||
}
|
||||
47
vendor/robthree/twofactorauth/lib/Providers/Qr/QRicketProvider.php
vendored
Normal file
47
vendor/robthree/twofactorauth/lib/Providers/Qr/QRicketProvider.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Qr;
|
||||
|
||||
/**
|
||||
* Use http://qrickit.com/qrickit_apps/qrickit_api.php to provide a QR code
|
||||
*/
|
||||
class QRicketProvider extends BaseHTTPQRCodeProvider
|
||||
{
|
||||
public function __construct(protected bool $verifyssl = true, public string $errorcorrectionlevel = 'L', public string $bgcolor = 'ffffff', public string $color = '000000', public string $format = 'p')
|
||||
{
|
||||
}
|
||||
|
||||
public function getMimeType(): string
|
||||
{
|
||||
switch (strtolower($this->format)) {
|
||||
case 'p':
|
||||
return 'image/png';
|
||||
case 'g':
|
||||
return 'image/gif';
|
||||
case 'j':
|
||||
return 'image/jpeg';
|
||||
}
|
||||
throw new QRException(sprintf('Unknown MIME-type: %s', $this->format));
|
||||
}
|
||||
|
||||
public function getQRCodeImage(string $qrText, int $size): string
|
||||
{
|
||||
return $this->getContent($this->getUrl($qrText, $size));
|
||||
}
|
||||
|
||||
public function getUrl(string $qrText, int $size): string
|
||||
{
|
||||
$queryParameters = array(
|
||||
'qrsize' => $size,
|
||||
'e' => strtolower($this->errorcorrectionlevel),
|
||||
'bgdcolor' => $this->bgcolor,
|
||||
'fgdcolor' => $this->color,
|
||||
't' => strtolower($this->format),
|
||||
'd' => $qrText,
|
||||
);
|
||||
|
||||
return 'https://qrickit.com/api/qr?' . http_build_query($queryParameters);
|
||||
}
|
||||
}
|
||||
16
vendor/robthree/twofactorauth/lib/Providers/Rng/CSRNGProvider.php
vendored
Normal file
16
vendor/robthree/twofactorauth/lib/Providers/Rng/CSRNGProvider.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Rng;
|
||||
|
||||
class CSRNGProvider implements IRNGProvider
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRandomBytes(int $bytecount): string
|
||||
{
|
||||
return random_bytes($bytecount); // PHP7+
|
||||
}
|
||||
}
|
||||
10
vendor/robthree/twofactorauth/lib/Providers/Rng/IRNGProvider.php
vendored
Normal file
10
vendor/robthree/twofactorauth/lib/Providers/Rng/IRNGProvider.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Rng;
|
||||
|
||||
interface IRNGProvider
|
||||
{
|
||||
public function getRandomBytes(int $bytecount): string;
|
||||
}
|
||||
11
vendor/robthree/twofactorauth/lib/Providers/Rng/RNGException.php
vendored
Normal file
11
vendor/robthree/twofactorauth/lib/Providers/Rng/RNGException.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Rng;
|
||||
|
||||
use RobThree\Auth\TwoFactorAuthException;
|
||||
|
||||
class RNGException extends TwoFactorAuthException
|
||||
{
|
||||
}
|
||||
62
vendor/robthree/twofactorauth/lib/Providers/Time/HttpTimeProvider.php
vendored
Normal file
62
vendor/robthree/twofactorauth/lib/Providers/Time/HttpTimeProvider.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Time;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Takes the time from any webserver by doing a HEAD request on the specified URL and extracting the 'Date:' header
|
||||
*/
|
||||
class HttpTimeProvider implements ITimeProvider
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function __construct(
|
||||
public string $url = 'https://google.com',
|
||||
public string $expectedtimeformat = 'D, d M Y H:i:s O+',
|
||||
public ?array $options = null,
|
||||
) {
|
||||
if ($this->options === null) {
|
||||
$this->options = array(
|
||||
'http' => array(
|
||||
'method' => 'HEAD',
|
||||
'follow_location' => false,
|
||||
'ignore_errors' => true,
|
||||
'max_redirects' => 0,
|
||||
'request_fulluri' => true,
|
||||
'header' => array(
|
||||
'Connection: close',
|
||||
'User-agent: TwoFactorAuth HttpTimeProvider (https://github.com/RobThree/TwoFactorAuth)',
|
||||
'Cache-Control: no-cache',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTime()
|
||||
{
|
||||
try {
|
||||
$context = stream_context_create($this->options);
|
||||
$fd = fopen($this->url, 'rb', false, $context);
|
||||
$headers = stream_get_meta_data($fd);
|
||||
fclose($fd);
|
||||
|
||||
foreach ($headers['wrapper_data'] as $h) {
|
||||
if (strcasecmp(substr($h, 0, 5), 'Date:') === 0) {
|
||||
return DateTime::createFromFormat($this->expectedtimeformat, trim(substr($h, 5)))->getTimestamp();
|
||||
}
|
||||
}
|
||||
throw new Exception('Invalid or no "Date:" header found');
|
||||
} catch (Exception $ex) {
|
||||
throw new TimeException(sprintf('Unable to retrieve time from %s (%s)', $this->url, $ex->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
13
vendor/robthree/twofactorauth/lib/Providers/Time/ITimeProvider.php
vendored
Normal file
13
vendor/robthree/twofactorauth/lib/Providers/Time/ITimeProvider.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Time;
|
||||
|
||||
interface ITimeProvider
|
||||
{
|
||||
/**
|
||||
* @return int the current timestamp according to this provider
|
||||
*/
|
||||
public function getTime();
|
||||
}
|
||||
13
vendor/robthree/twofactorauth/lib/Providers/Time/LocalMachineTimeProvider.php
vendored
Normal file
13
vendor/robthree/twofactorauth/lib/Providers/Time/LocalMachineTimeProvider.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Time;
|
||||
|
||||
class LocalMachineTimeProvider implements ITimeProvider
|
||||
{
|
||||
public function getTime()
|
||||
{
|
||||
return time();
|
||||
}
|
||||
}
|
||||
58
vendor/robthree/twofactorauth/lib/Providers/Time/NTPTimeProvider.php
vendored
Normal file
58
vendor/robthree/twofactorauth/lib/Providers/Time/NTPTimeProvider.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Time;
|
||||
|
||||
use Exception;
|
||||
|
||||
use function socket_create;
|
||||
|
||||
/**
|
||||
* Takes the time from any NTP server
|
||||
*/
|
||||
class NTPTimeProvider implements ITimeProvider
|
||||
{
|
||||
public function __construct(public string $host = 'time.google.com', public int $port = 123, public int $timeout = 1)
|
||||
{
|
||||
if ($this->port <= 0 || $this->port > 65535) {
|
||||
throw new TimeException('Port must be 0 < port < 65535');
|
||||
}
|
||||
|
||||
if ($this->timeout < 0) {
|
||||
throw new TimeException('Timeout must be >= 0');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTime()
|
||||
{
|
||||
try {
|
||||
// Create a socket and connect to NTP server
|
||||
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
|
||||
socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $this->timeout, 'usec' => 0));
|
||||
socket_connect($sock, $this->host, $this->port);
|
||||
|
||||
// Send request
|
||||
$msg = "\010" . str_repeat("\0", 47);
|
||||
socket_send($sock, $msg, strlen($msg), 0);
|
||||
|
||||
// Receive response and close socket
|
||||
if (socket_recv($sock, $recv, 48, MSG_WAITALL) === false) {
|
||||
throw new Exception(socket_strerror(socket_last_error($sock)));
|
||||
}
|
||||
socket_close($sock);
|
||||
|
||||
// Interpret response
|
||||
$data = unpack('N12', $recv);
|
||||
$timestamp = (int)sprintf('%u', $data[9]);
|
||||
|
||||
// NTP is number of seconds since 0000 UT on 1 January 1900 Unix time is seconds since 0000 UT on 1 January 1970
|
||||
return $timestamp - 2208988800;
|
||||
} catch (Exception $ex) {
|
||||
throw new TimeException(sprintf('Unable to retrieve time from %s (%s)', $this->host, $ex->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
11
vendor/robthree/twofactorauth/lib/Providers/Time/TimeException.php
vendored
Normal file
11
vendor/robthree/twofactorauth/lib/Providers/Time/TimeException.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth\Providers\Time;
|
||||
|
||||
use RobThree\Auth\TwoFactorAuthException;
|
||||
|
||||
class TimeException extends TwoFactorAuthException
|
||||
{
|
||||
}
|
||||
211
vendor/robthree/twofactorauth/lib/TwoFactorAuth.php
vendored
Normal file
211
vendor/robthree/twofactorauth/lib/TwoFactorAuth.php
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth;
|
||||
|
||||
use function hash_equals;
|
||||
|
||||
use RobThree\Auth\Providers\Qr\IQRCodeProvider;
|
||||
use RobThree\Auth\Providers\Rng\CSRNGProvider;
|
||||
use RobThree\Auth\Providers\Rng\IRNGProvider;
|
||||
use RobThree\Auth\Providers\Time\HttpTimeProvider;
|
||||
use RobThree\Auth\Providers\Time\ITimeProvider;
|
||||
use RobThree\Auth\Providers\Time\LocalMachineTimeProvider;
|
||||
use RobThree\Auth\Providers\Time\NTPTimeProvider;
|
||||
use SensitiveParameter;
|
||||
|
||||
// Based on / inspired by: https://github.com/PHPGangsta/GoogleAuthenticator
|
||||
// Algorithms, digits, period etc. explained: https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
||||
class TwoFactorAuth
|
||||
{
|
||||
private static string $_base32dict = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=';
|
||||
|
||||
/** @var array<string> */
|
||||
private static array $_base32;
|
||||
|
||||
/** @var array<string, int> */
|
||||
private static array $_base32lookup = array();
|
||||
|
||||
public function __construct(
|
||||
private IQRCodeProvider $qrcodeprovider,
|
||||
private readonly ?string $issuer = null,
|
||||
private readonly int $digits = 6,
|
||||
private readonly int $period = 30,
|
||||
private readonly Algorithm $algorithm = Algorithm::Sha1,
|
||||
private ?IRNGProvider $rngprovider = null,
|
||||
private ?ITimeProvider $timeprovider = null
|
||||
) {
|
||||
if ($this->digits <= 0) {
|
||||
throw new TwoFactorAuthException('Digits must be > 0');
|
||||
}
|
||||
|
||||
if ($this->period <= 0) {
|
||||
throw new TwoFactorAuthException('Period must be int > 0');
|
||||
}
|
||||
|
||||
self::$_base32 = str_split(self::$_base32dict);
|
||||
self::$_base32lookup = array_flip(self::$_base32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new secret
|
||||
*/
|
||||
public function createSecret(int $bits = 160): string
|
||||
{
|
||||
$secret = '';
|
||||
$bytes = (int)ceil($bits / 5); // We use 5 bits of each byte (since we have a 32-character 'alphabet' / BASE32)
|
||||
$rngprovider = $this->getRngProvider();
|
||||
$rnd = $rngprovider->getRandomBytes($bytes);
|
||||
for ($i = 0; $i < $bytes; $i++) {
|
||||
$secret .= self::$_base32[ord($rnd[$i]) & 31]; //Mask out left 3 bits for 0-31 values
|
||||
}
|
||||
return $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the code with given secret and point in time
|
||||
*/
|
||||
public function getCode(#[SensitiveParameter] string $secret, ?int $time = null): string
|
||||
{
|
||||
$secretkey = $this->base32Decode($secret);
|
||||
|
||||
$timestamp = "\0\0\0\0" . pack('N*', $this->getTimeSlice($this->getTime($time))); // Pack time into binary string
|
||||
$hashhmac = hash_hmac($this->algorithm->value, $timestamp, $secretkey, true); // Hash it with users secret key
|
||||
$hashpart = substr($hashhmac, ord(substr($hashhmac, -1)) & 0x0F, 4); // Use last nibble of result as index/offset and grab 4 bytes of the result
|
||||
$value = unpack('N', $hashpart); // Unpack binary value
|
||||
$value = $value[1] & 0x7FFFFFFF; // Drop MSB, keep only 31 bits
|
||||
|
||||
return str_pad((string)($value % 10 ** $this->digits), $this->digits, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the code is correct. This will accept codes starting from ($discrepancy * $period) sec ago to ($discrepancy * period) sec from now
|
||||
*/
|
||||
public function verifyCode(string $secret, string $code, int $discrepancy = 1, ?int $time = null, ?int &$timeslice = 0): bool
|
||||
{
|
||||
$timestamp = $this->getTime($time);
|
||||
|
||||
$timeslice = 0;
|
||||
|
||||
// To keep safe from timing-attacks we iterate *all* possible codes even though we already may have
|
||||
// verified a code is correct. We use the timeslice variable to hold either 0 (no match) or the timeslice
|
||||
// of the match. Each iteration we either set the timeslice variable to the timeslice of the match
|
||||
// or set the value to itself. This is an effort to maintain constant execution time for the code.
|
||||
for ($i = -$discrepancy; $i <= $discrepancy; $i++) {
|
||||
$ts = $timestamp + ($i * $this->period);
|
||||
$slice = $this->getTimeSlice($ts);
|
||||
$timeslice = hash_equals($this->getCode($secret, $ts), $code) ? $slice : $timeslice;
|
||||
}
|
||||
|
||||
return $timeslice > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data-uri of QRCode
|
||||
*/
|
||||
public function getQRCodeImageAsDataUri(string $label, #[SensitiveParameter] string $secret, int $size = 200): string
|
||||
{
|
||||
if ($size <= 0) {
|
||||
throw new TwoFactorAuthException('Size must be > 0');
|
||||
}
|
||||
|
||||
return 'data:'
|
||||
. $this->qrcodeprovider->getMimeType()
|
||||
. ';base64,'
|
||||
. base64_encode($this->qrcodeprovider->getQRCodeImage($this->getQRText($label, $secret), $size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare default timeprovider with specified timeproviders and ensure the time is within the specified number of seconds (leniency)
|
||||
* @param array<ITimeProvider> $timeproviders
|
||||
* @throws TwoFactorAuthException
|
||||
*/
|
||||
public function ensureCorrectTime(?array $timeproviders = null, int $leniency = 5): void
|
||||
{
|
||||
if ($timeproviders === null) {
|
||||
$timeproviders = array(
|
||||
new NTPTimeProvider(),
|
||||
new HttpTimeProvider(),
|
||||
);
|
||||
}
|
||||
|
||||
// Get default time provider
|
||||
$timeprovider = $this->getTimeProvider();
|
||||
|
||||
// Iterate specified time providers
|
||||
foreach ($timeproviders as $t) {
|
||||
if (!($t instanceof ITimeProvider)) {
|
||||
throw new TwoFactorAuthException('Object does not implement ITimeProvider');
|
||||
}
|
||||
|
||||
// Get time from default time provider and compare to specific time provider and throw if time difference is more than specified number of seconds leniency
|
||||
if (abs($timeprovider->getTime() - $t->getTime()) > $leniency) {
|
||||
throw new TwoFactorAuthException(sprintf('Time for timeprovider is off by more than %d seconds when compared to %s', $leniency, get_class($t)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a string to be encoded in a QR code
|
||||
*/
|
||||
public function getQRText(string $label, #[SensitiveParameter] string $secret): string
|
||||
{
|
||||
return 'otpauth://totp/' . rawurlencode($label)
|
||||
. '?secret=' . rawurlencode($secret)
|
||||
. '&issuer=' . rawurlencode((string)$this->issuer)
|
||||
. '&period=' . $this->period
|
||||
. '&algorithm=' . rawurlencode(strtoupper($this->algorithm->value))
|
||||
. '&digits=' . $this->digits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws TwoFactorAuthException
|
||||
*/
|
||||
public function getRngProvider(): IRNGProvider
|
||||
{
|
||||
return $this->rngprovider ??= new CSRNGProvider();
|
||||
}
|
||||
|
||||
public function getTimeProvider(): ITimeProvider
|
||||
{
|
||||
// Set default time provider if none was specified
|
||||
return $this->timeprovider ??= new LocalMachineTimeProvider();
|
||||
}
|
||||
|
||||
private function getTime(?int $time = null): int
|
||||
{
|
||||
return $time ?? $this->getTimeProvider()->getTime();
|
||||
}
|
||||
|
||||
private function getTimeSlice(?int $time = null, int $offset = 0): int
|
||||
{
|
||||
return (int)floor($time / $this->period) + ($offset * $this->period);
|
||||
}
|
||||
|
||||
private function base32Decode(string $value): string
|
||||
{
|
||||
if ($value === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (preg_match('/[^' . preg_quote(self::$_base32dict, '/') . ']/', $value) !== 0) {
|
||||
throw new TwoFactorAuthException('Invalid base32 string');
|
||||
}
|
||||
|
||||
$buffer = '';
|
||||
foreach (str_split($value) as $char) {
|
||||
if ($char !== '=') {
|
||||
$buffer .= str_pad(decbin(self::$_base32lookup[$char]), 5, '0', STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
$length = strlen($buffer);
|
||||
$blocks = trim(chunk_split(substr($buffer, 0, $length - ($length % 8)), 8, ' '));
|
||||
|
||||
$output = '';
|
||||
foreach (explode(' ', $blocks) as $block) {
|
||||
$output .= chr(bindec(str_pad($block, 8, '0', STR_PAD_RIGHT)));
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
11
vendor/robthree/twofactorauth/lib/TwoFactorAuthException.php
vendored
Normal file
11
vendor/robthree/twofactorauth/lib/TwoFactorAuthException.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RobThree\Auth;
|
||||
|
||||
use Exception;
|
||||
|
||||
class TwoFactorAuthException extends Exception
|
||||
{
|
||||
}
|
||||
BIN
vendor/robthree/twofactorauth/logo.png
vendored
Normal file
BIN
vendor/robthree/twofactorauth/logo.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
BIN
vendor/robthree/twofactorauth/multifactorauthforeveryone.png
vendored
Normal file
BIN
vendor/robthree/twofactorauth/multifactorauthforeveryone.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
12
vendor/robthree/twofactorauth/phpstan.neon
vendored
Normal file
12
vendor/robthree/twofactorauth/phpstan.neon
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
parameters:
|
||||
level: 6
|
||||
|
||||
excludePaths:
|
||||
- %currentWorkingDirectory%/lib/Providers/Qr/BaconQrCodeProvider.php
|
||||
- %currentWorkingDirectory%/lib/Providers/Qr/EndroidQrCodeProvider.php
|
||||
- %currentWorkingDirectory%/lib/Providers/Qr/EndroidQrCodeWithLogoProvider.php
|
||||
|
||||
paths:
|
||||
- %currentWorkingDirectory%/lib
|
||||
- %currentWorkingDirectory%/tests
|
||||
|
||||
27
vendor/robthree/twofactorauth/phpunit.xml
vendored
Normal file
27
vendor/robthree/twofactorauth/phpunit.xml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertDeprecationsToExceptions="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
stopOnFailure="false">
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./lib</directory>
|
||||
</include>
|
||||
<report>
|
||||
<html outputDirectory="build/coverage"/>
|
||||
<text outputFile="php://stdout"/>
|
||||
</report>
|
||||
</coverage>
|
||||
</phpunit>
|
||||
56
vendor/robthree/twofactorauth/tests/Providers/Qr/IQRCodeProviderTest.php
vendored
Normal file
56
vendor/robthree/twofactorauth/tests/Providers/Qr/IQRCodeProviderTest.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Providers\Qr;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use RobThree\Auth\Algorithm;
|
||||
use RobThree\Auth\Providers\Qr\HandlesDataUri;
|
||||
use RobThree\Auth\Providers\Qr\IQRCodeProvider;
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
use RobThree\Auth\TwoFactorAuthException;
|
||||
|
||||
class IQRCodeProviderTest extends TestCase
|
||||
{
|
||||
use HandlesDataUri;
|
||||
|
||||
protected IQRCodeProvider $qr;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->qr = new TestQrProvider();
|
||||
}
|
||||
|
||||
public function testTotpUriIsCorrect(): void
|
||||
{
|
||||
$tfa = new TwoFactorAuth($this->qr, 'Test&Issuer', 6, 30, Algorithm::Sha1);
|
||||
$data = $this->DecodeDataUri($tfa->getQRCodeImageAsDataUri('Test&Label', 'VMR466AB62ZBOKHE'));
|
||||
$this->assertSame('test/test', $data['mimetype']);
|
||||
$this->assertSame('base64', $data['encoding']);
|
||||
$this->assertSame('otpauth://totp/Test%26Label?secret=VMR466AB62ZBOKHE&issuer=Test%26Issuer&period=30&algorithm=SHA1&digits=6@200', $data['data']);
|
||||
}
|
||||
|
||||
public function testTotpUriIsCorrectNoIssuer(): void
|
||||
{
|
||||
/**
|
||||
* The library specifies the issuer is null by default however in PHP 8.1
|
||||
* there is a deprecation warning for passing null as a string argument to rawurlencode
|
||||
*/
|
||||
|
||||
$tfa = new TwoFactorAuth($this->qr, null, 6, 30, Algorithm::Sha1);
|
||||
$data = $this->DecodeDataUri($tfa->getQRCodeImageAsDataUri('Test&Label', 'VMR466AB62ZBOKHE'));
|
||||
$this->assertSame('test/test', $data['mimetype']);
|
||||
$this->assertSame('base64', $data['encoding']);
|
||||
$this->assertSame('otpauth://totp/Test%26Label?secret=VMR466AB62ZBOKHE&issuer=&period=30&algorithm=SHA1&digits=6@200', $data['data']);
|
||||
}
|
||||
|
||||
public function testGetQRCodeImageAsDataUriThrowsOnInvalidSize(): void
|
||||
{
|
||||
$tfa = new TwoFactorAuth($this->qr, 'Test', 6, 30, Algorithm::Sha1);
|
||||
|
||||
$this->expectException(TwoFactorAuthException::class);
|
||||
|
||||
$tfa->getQRCodeImageAsDataUri('Test', 'VMR466AB62ZBOKHE', 0);
|
||||
}
|
||||
}
|
||||
20
vendor/robthree/twofactorauth/tests/Providers/Qr/TestQrProvider.php
vendored
Normal file
20
vendor/robthree/twofactorauth/tests/Providers/Qr/TestQrProvider.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Providers\Qr;
|
||||
|
||||
use RobThree\Auth\Providers\Qr\IQRCodeProvider;
|
||||
|
||||
class TestQrProvider implements IQRCodeProvider
|
||||
{
|
||||
public function getQRCodeImage(string $qrText, int $size): string
|
||||
{
|
||||
return $qrText . '@' . $size;
|
||||
}
|
||||
|
||||
public function getMimeType(): string
|
||||
{
|
||||
return 'test/test';
|
||||
}
|
||||
}
|
||||
21
vendor/robthree/twofactorauth/tests/Providers/Rng/CSRNGProviderTest.php
vendored
Normal file
21
vendor/robthree/twofactorauth/tests/Providers/Rng/CSRNGProviderTest.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Providers\Rng;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use RobThree\Auth\Providers\Rng\CSRNGProvider;
|
||||
|
||||
class CSRNGProviderTest extends TestCase
|
||||
{
|
||||
use NeedsRngLengths;
|
||||
|
||||
public function testCSRNGProvidersReturnExpectedNumberOfBytes(): void
|
||||
{
|
||||
$rng = new CSRNGProvider();
|
||||
foreach ($this->rngTestLengths as $l) {
|
||||
$this->assertSame($l, strlen($rng->getRandomBytes($l)));
|
||||
}
|
||||
}
|
||||
}
|
||||
19
vendor/robthree/twofactorauth/tests/Providers/Rng/IRNGProviderTest.php
vendored
Normal file
19
vendor/robthree/twofactorauth/tests/Providers/Rng/IRNGProviderTest.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Providers\Rng;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use RobThree\Auth\Algorithm;
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
use Tests\Providers\Qr\TestQrProvider;
|
||||
|
||||
class IRNGProviderTest extends TestCase
|
||||
{
|
||||
public function testCreateSecret(): void
|
||||
{
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test', 6, 30, Algorithm::Sha1, null, null);
|
||||
$this->assertIsString($tfa->createSecret());
|
||||
}
|
||||
}
|
||||
11
vendor/robthree/twofactorauth/tests/Providers/Rng/NeedsRngLengths.php
vendored
Normal file
11
vendor/robthree/twofactorauth/tests/Providers/Rng/NeedsRngLengths.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Providers\Rng;
|
||||
|
||||
trait NeedsRngLengths
|
||||
{
|
||||
/** @var array<int> */
|
||||
protected $rngTestLengths = array(1, 16, 32, 256);
|
||||
}
|
||||
43
vendor/robthree/twofactorauth/tests/Providers/Time/ITimeProviderTest.php
vendored
Normal file
43
vendor/robthree/twofactorauth/tests/Providers/Time/ITimeProviderTest.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Providers\Time;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use RobThree\Auth\Algorithm;
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
use RobThree\Auth\TwoFactorAuthException;
|
||||
use Tests\Providers\Qr\TestQrProvider;
|
||||
|
||||
class ITimeProviderTest extends TestCase
|
||||
{
|
||||
public function testEnsureCorrectTimeDoesNotThrowForCorrectTime(): void
|
||||
{
|
||||
$this->expectNotToPerformAssertions();
|
||||
$tpr1 = new TestTimeProvider(123);
|
||||
$tpr2 = new TestTimeProvider(128);
|
||||
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test', 6, 30, Algorithm::Sha1, null, $tpr1);
|
||||
$tfa->ensureCorrectTime(array($tpr2)); // 128 - 123 = 5 => within default leniency
|
||||
}
|
||||
|
||||
public function testEnsureCorrectTimeThrowsOnIncorrectTime(): void
|
||||
{
|
||||
$tpr1 = new TestTimeProvider(123);
|
||||
$tpr2 = new TestTimeProvider(124);
|
||||
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test', 6, 30, Algorithm::Sha1, null, $tpr1);
|
||||
|
||||
$this->expectException(TwoFactorAuthException::class);
|
||||
|
||||
$tfa->ensureCorrectTime(array($tpr2), 0); // We force a leniency of 0, 124-123 = 1 so this should throw
|
||||
}
|
||||
|
||||
public function testEnsureDefaultTimeProviderReturnsCorrectTime(): void
|
||||
{
|
||||
$this->expectNotToPerformAssertions();
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test', 6, 30, Algorithm::Sha1);
|
||||
$tfa->ensureCorrectTime(array(new TestTimeProvider(time())), 1); // Use a leniency of 1, should the time change between both time() calls
|
||||
}
|
||||
}
|
||||
29
vendor/robthree/twofactorauth/tests/Providers/Time/TestTimeProvider.php
vendored
Normal file
29
vendor/robthree/twofactorauth/tests/Providers/Time/TestTimeProvider.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Providers\Time;
|
||||
|
||||
use RobThree\Auth\Providers\Time\ITimeProvider;
|
||||
|
||||
class TestTimeProvider implements ITimeProvider
|
||||
{
|
||||
/** @var int */
|
||||
private $time;
|
||||
|
||||
/**
|
||||
* @param int $time
|
||||
*/
|
||||
public function __construct($time)
|
||||
{
|
||||
$this->time = $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTime()
|
||||
{
|
||||
return $this->time;
|
||||
}
|
||||
}
|
||||
200
vendor/robthree/twofactorauth/tests/TwoFactorAuthTest.php
vendored
Normal file
200
vendor/robthree/twofactorauth/tests/TwoFactorAuthTest.php
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionMethod;
|
||||
use RobThree\Auth\Algorithm;
|
||||
use RobThree\Auth\Providers\Time\HttpTimeProvider;
|
||||
use RobThree\Auth\Providers\Time\NTPTimeProvider;
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
use RobThree\Auth\TwoFactorAuthException;
|
||||
use Tests\Providers\Qr\TestQrProvider;
|
||||
|
||||
class TwoFactorAuthTest extends TestCase
|
||||
{
|
||||
public function testConstructorThrowsOnInvalidDigits(): void
|
||||
{
|
||||
$this->expectException(TwoFactorAuthException::class);
|
||||
|
||||
new TwoFactorAuth(new TestQrProvider(), 'Test', 0);
|
||||
}
|
||||
|
||||
public function testConstructorThrowsOnInvalidPeriod(): void
|
||||
{
|
||||
$this->expectException(TwoFactorAuthException::class);
|
||||
|
||||
new TwoFactorAuth(new TestQrProvider(), 'Test', 6, 0);
|
||||
}
|
||||
|
||||
public function testGetCodeReturnsCorrectResults(): void
|
||||
{
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test');
|
||||
$this->assertSame('543160', $tfa->getCode('VMR466AB62ZBOKHE', 1426847216));
|
||||
$this->assertSame('538532', $tfa->getCode('VMR466AB62ZBOKHE', 0));
|
||||
}
|
||||
|
||||
public function testEnsureAllTimeProvidersReturnCorrectTime(): void
|
||||
{
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test', 6, 30, Algorithm::Sha1);
|
||||
$tfa->ensureCorrectTime(array(
|
||||
new NTPTimeProvider(), // Uses pool.ntp.org by default
|
||||
//new \RobThree\Auth\Providers\Time\NTPTimeProvider('time.google.com'), // Somehow time.google.com and time.windows.com make travis timeout??
|
||||
new HttpTimeProvider(), // Uses google.com by default
|
||||
//new \RobThree\Auth\Providers\Time\HttpTimeProvider('https://github.com'), // github.com will periodically report times that are off by more than 5 sec
|
||||
new HttpTimeProvider('https://yahoo.com'),
|
||||
));
|
||||
$this->expectNotToPerformAssertions();
|
||||
}
|
||||
|
||||
public function testVerifyCodeWorksCorrectly(): void
|
||||
{
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test', 6, 30);
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847190));
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 0, 1426847190 + 29)); //Test discrepancy
|
||||
$this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 0, 1426847190 + 30)); //Test discrepancy
|
||||
$this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 0, 1426847190 - 1)); //Test discrepancy
|
||||
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205)); //Test discrepancy
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 + 35)); //Test discrepancy
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 - 35)); //Test discrepancy
|
||||
|
||||
$this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 + 65)); //Test discrepancy
|
||||
$this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 - 65)); //Test discrepancy
|
||||
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 2, 1426847205 + 65)); //Test discrepancy
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 2, 1426847205 - 65)); //Test discrepancy
|
||||
}
|
||||
|
||||
public function testVerifyCorrectTimeSliceIsReturned(): void
|
||||
{
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test', 6, 30);
|
||||
|
||||
// We test with discrepancy 3 (so total of 7 codes: c-3, c-2, c-1, c, c+1, c+2, c+3
|
||||
// Ensure each corresponding timeslice is returned correctly
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '534113', 3, 1426847190, $timeslice1));
|
||||
$this->assertSame(47561570, $timeslice1);
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '819652', 3, 1426847190, $timeslice2));
|
||||
$this->assertSame(47561571, $timeslice2);
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '915954', 3, 1426847190, $timeslice3));
|
||||
$this->assertSame(47561572, $timeslice3);
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 3, 1426847190, $timeslice4));
|
||||
$this->assertSame(47561573, $timeslice4);
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '348401', 3, 1426847190, $timeslice5));
|
||||
$this->assertSame(47561574, $timeslice5);
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '648525', 3, 1426847190, $timeslice6));
|
||||
$this->assertSame(47561575, $timeslice6);
|
||||
$this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '170645', 3, 1426847190, $timeslice7));
|
||||
$this->assertSame(47561576, $timeslice7);
|
||||
|
||||
// Incorrect code should return false and a 0 timeslice
|
||||
$this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '111111', 3, 1426847190, $timeslice8));
|
||||
$this->assertSame(0, $timeslice8);
|
||||
}
|
||||
|
||||
public function testGetCodeThrowsOnInvalidBase32String1(): void
|
||||
{
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test');
|
||||
|
||||
$this->expectException(TwoFactorAuthException::class);
|
||||
|
||||
$tfa->getCode('FOO1BAR8BAZ9'); //1, 8 & 9 are invalid chars
|
||||
}
|
||||
|
||||
public function testGetCodeThrowsOnInvalidBase32String2(): void
|
||||
{
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test');
|
||||
|
||||
$this->expectException(TwoFactorAuthException::class);
|
||||
|
||||
$tfa->getCode('mzxw6==='); //Lowercase
|
||||
}
|
||||
|
||||
public function testKnownBase32DecodeTestVectors(): void
|
||||
{
|
||||
// We usually don't test internals (e.g. privates) but since we rely heavily on base32 decoding and don't want
|
||||
// to expose this method nor do we want to give people the possibility of implementing / providing their own base32
|
||||
// decoding/decoder (as we do with Rng/QR providers for example) we simply test the private base32Decode() method
|
||||
// with some known testvectors **only** to ensure base32 decoding works correctly following RFC's so there won't
|
||||
// be any bugs hiding in there. We **could** 'fool' ourselves by calling the public getCode() method (which uses
|
||||
// base32decode internally) and then make sure getCode's output (in digits) equals expected output since that would
|
||||
// mean the base32Decode() works as expected but that **could** hide some subtle bug(s) in decoding the base32 string.
|
||||
|
||||
// "In general, you don't want to break any encapsulation for the sake of testing (or as Mom used to say, "don't
|
||||
// expose your privates!"). Most of the time, you should be able to test a class by exercising its public methods."
|
||||
// Dave Thomas and Andy Hunt -- "Pragmatic Unit Testing
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test');
|
||||
|
||||
$method = new ReflectionMethod(TwoFactorAuth::class, 'base32Decode');
|
||||
|
||||
// Test vectors from: https://tools.ietf.org/html/rfc4648#page-12
|
||||
$this->assertSame('', $method->invoke($tfa, ''));
|
||||
$this->assertSame('f', $method->invoke($tfa, 'MY======'));
|
||||
$this->assertSame('fo', $method->invoke($tfa, 'MZXQ===='));
|
||||
$this->assertSame('foo', $method->invoke($tfa, 'MZXW6==='));
|
||||
$this->assertSame('foob', $method->invoke($tfa, 'MZXW6YQ='));
|
||||
$this->assertSame('fooba', $method->invoke($tfa, 'MZXW6YTB'));
|
||||
$this->assertSame('foobar', $method->invoke($tfa, 'MZXW6YTBOI======'));
|
||||
}
|
||||
|
||||
public function testKnownBase32DecodeUnpaddedTestVectors(): void
|
||||
{
|
||||
// See testKnownBase32DecodeTestVectors() for the rationale behind testing the private base32Decode() method.
|
||||
// This test ensures that strings without the padding-char ('=') are also decoded correctly.
|
||||
// https://tools.ietf.org/html/rfc4648#page-4:
|
||||
// "In some circumstances, the use of padding ("=") in base-encoded data is not required or used."
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test');
|
||||
|
||||
$method = new ReflectionMethod(TwoFactorAuth::class, 'base32Decode');
|
||||
|
||||
// Test vectors from: https://tools.ietf.org/html/rfc4648#page-12
|
||||
$this->assertSame('', $method->invoke($tfa, ''));
|
||||
$this->assertSame('f', $method->invoke($tfa, 'MY'));
|
||||
$this->assertSame('fo', $method->invoke($tfa, 'MZXQ'));
|
||||
$this->assertSame('foo', $method->invoke($tfa, 'MZXW6'));
|
||||
$this->assertSame('foob', $method->invoke($tfa, 'MZXW6YQ'));
|
||||
$this->assertSame('fooba', $method->invoke($tfa, 'MZXW6YTB'));
|
||||
$this->assertSame('foobar', $method->invoke($tfa, 'MZXW6YTBOI'));
|
||||
}
|
||||
|
||||
public function testKnownTestVectors_sha1(): void
|
||||
{
|
||||
//Known test vectors for SHA1: https://tools.ietf.org/html/rfc6238#page-15
|
||||
$secret = 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'; //== base32encode('12345678901234567890')
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test', 8, 30, Algorithm::Sha1);
|
||||
$this->assertSame('94287082', $tfa->getCode($secret, 59));
|
||||
$this->assertSame('07081804', $tfa->getCode($secret, 1111111109));
|
||||
$this->assertSame('14050471', $tfa->getCode($secret, 1111111111));
|
||||
$this->assertSame('89005924', $tfa->getCode($secret, 1234567890));
|
||||
$this->assertSame('69279037', $tfa->getCode($secret, 2000000000));
|
||||
$this->assertSame('65353130', $tfa->getCode($secret, 20000000000));
|
||||
}
|
||||
|
||||
public function testKnownTestVectors_sha256(): void
|
||||
{
|
||||
//Known test vectors for SHA256: https://tools.ietf.org/html/rfc6238#page-15
|
||||
$secret = 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA'; //== base32encode('12345678901234567890123456789012')
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test', 8, 30, Algorithm::Sha256);
|
||||
$this->assertSame('46119246', $tfa->getCode($secret, 59));
|
||||
$this->assertSame('68084774', $tfa->getCode($secret, 1111111109));
|
||||
$this->assertSame('67062674', $tfa->getCode($secret, 1111111111));
|
||||
$this->assertSame('91819424', $tfa->getCode($secret, 1234567890));
|
||||
$this->assertSame('90698825', $tfa->getCode($secret, 2000000000));
|
||||
$this->assertSame('77737706', $tfa->getCode($secret, 20000000000));
|
||||
}
|
||||
|
||||
public function testKnownTestVectors_sha512(): void
|
||||
{
|
||||
//Known test vectors for SHA512: https://tools.ietf.org/html/rfc6238#page-15
|
||||
$secret = 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNA'; //== base32encode('1234567890123456789012345678901234567890123456789012345678901234')
|
||||
$tfa = new TwoFactorAuth(new TestQrProvider(), 'Test', 8, 30, Algorithm::Sha512);
|
||||
$this->assertSame('90693936', $tfa->getCode($secret, 59));
|
||||
$this->assertSame('25091201', $tfa->getCode($secret, 1111111109));
|
||||
$this->assertSame('99943326', $tfa->getCode($secret, 1111111111));
|
||||
$this->assertSame('93441116', $tfa->getCode($secret, 1234567890));
|
||||
$this->assertSame('38618901', $tfa->getCode($secret, 2000000000));
|
||||
$this->assertSame('47863826', $tfa->getCode($secret, 20000000000));
|
||||
}
|
||||
}
|
||||
62
vendor/robthree/twofactorauth/testsDependency/BaconQRCodeTest.php
vendored
Normal file
62
vendor/robthree/twofactorauth/testsDependency/BaconQRCodeTest.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace TestsDependency;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use RobThree\Auth\Algorithm;
|
||||
use RobThree\Auth\Providers\Qr\BaconQrCodeProvider;
|
||||
use RobThree\Auth\Providers\Qr\HandlesDataUri;
|
||||
use RobThree\Auth\Providers\Qr\IQRCodeProvider;
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
use RuntimeException;
|
||||
|
||||
class BaconQRCodeTest extends TestCase
|
||||
{
|
||||
use HandlesDataUri;
|
||||
|
||||
protected IQRCodeProvider $qr;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->qr = new BaconQrCodeProvider(1, '#000', '#FFF', 'svg');
|
||||
;
|
||||
}
|
||||
|
||||
public function testDependency(): void
|
||||
{
|
||||
$tfa = new TwoFactorAuth($this->qr, 'Test&Issuer', 6, 30, Algorithm::Sha1);
|
||||
|
||||
$data = $this->DecodeDataUri($tfa->getQRCodeImageAsDataUri('Test&Label', 'VMR466AB62ZBOKHE'));
|
||||
$this->assertSame('image/svg+xml', $data['mimetype']);
|
||||
}
|
||||
|
||||
public function testBadTextColour(): void
|
||||
{
|
||||
$this->expectException(RuntimeException::class);
|
||||
|
||||
new BaconQrCodeProvider(1, 'not-a-colour', '#FFF');
|
||||
}
|
||||
|
||||
public function testBadBackgroundColour(): void
|
||||
{
|
||||
$this->expectException(RuntimeException::class);
|
||||
|
||||
new BaconQrCodeProvider(1, '#000', 'not-a-colour');
|
||||
}
|
||||
|
||||
public function testBadTextColourHexRef(): void
|
||||
{
|
||||
$this->expectException(RuntimeException::class);
|
||||
|
||||
new BaconQrCodeProvider(1, '#AAAA', '#FFF');
|
||||
}
|
||||
|
||||
public function testBadBackgroundColourHexRef(): void
|
||||
{
|
||||
$this->expectException(RuntimeException::class);
|
||||
|
||||
new BaconQrCodeProvider(1, '#000', '#AAAA');
|
||||
}
|
||||
}
|
||||
26
vendor/robthree/twofactorauth/testsDependency/EndroidQRCodeTest.php
vendored
Normal file
26
vendor/robthree/twofactorauth/testsDependency/EndroidQRCodeTest.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace TestsDependency;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use RobThree\Auth\Algorithm;
|
||||
use RobThree\Auth\Providers\Qr\EndroidQrCodeProvider;
|
||||
use RobThree\Auth\Providers\Qr\HandlesDataUri;
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
|
||||
class EndroidQRCodeTest extends TestCase
|
||||
{
|
||||
use HandlesDataUri;
|
||||
|
||||
public function testDependency(): void
|
||||
{
|
||||
$qr = new EndroidQrCodeProvider();
|
||||
$tfa = new TwoFactorAuth($qr, 'Test&Issuer', 6, 30, Algorithm::Sha1);
|
||||
$data = $this->DecodeDataUri($tfa->getQRCodeImageAsDataUri('Test&Label', 'VMR466AB62ZBOKHE'));
|
||||
$this->assertSame('image/png', $data['mimetype']);
|
||||
$this->assertSame('base64', $data['encoding']);
|
||||
$this->assertNotEmpty($data['data']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user