diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 0000000..ee58205 --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,54 @@ +on: + - push + +name: Run Codecov checks + +jobs: + code-coverage: + name: Code Coverage + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: + - ubuntu-latest + + php: + - "8.3" + - "8.4" + - "8.5" + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: pcov + ini-values: assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On + tools: composer:v2, cs2pr + + - name: Determine composer cache directory + run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV + + - name: Cache dependencies installed with composer + uses: actions/cache@v5 + with: + path: ${{ env.COMPOSER_CACHE_DIR }} + key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: | + php${{ matrix.php }}-composer- + + - name: Install dependencies with composer + run: composer install --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + - name: Collect code coverage with PHPUnit + run: vendor/bin/phpunit --colors=always --coverage-clover clover.xml + + - name: Send code coverage report to Codecov.io + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml new file mode 100644 index 0000000..26c5802 --- /dev/null +++ b/.github/workflows/continuous-integration.yml @@ -0,0 +1,11 @@ +name: "Continuous Integration" + +on: + pull_request: + push: + branches: + tags: + +jobs: + ci: + uses: laminas/workflow-continuous-integration/.github/workflows/continuous-integration.yml@1.x diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 0000000..94fbdf8 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,49 @@ +on: + - push + +name: Run PHPStan checks + +jobs: + mutation: + name: PHPStan ${{ matrix.php }}-${{ matrix.os }} + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: + - ubuntu-latest + + php: + - "8.3" + - "8.4" + - "8.5" + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: pcov + ini-values: assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On + tools: composer:v2, cs2pr + + - name: Determine composer cache directory + run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV + + - name: Cache dependencies installed with composer + uses: actions/cache@v5 + with: + path: ${{ env.COMPOSER_CACHE_DIR }} + key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: | + php${{ matrix.php }}-composer- + + - name: Install dependencies with composer + run: composer install --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + - name: Run static analysis with PHPStan + run: vendor/bin/phpstan analyse diff --git a/.laminas-ci.json b/.laminas-ci.json new file mode 100644 index 0000000..29216fa --- /dev/null +++ b/.laminas-ci.json @@ -0,0 +1,3 @@ +{ + "backwardCompatibilityCheck": true +} diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..78a4252 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2026 Dotkernel (https://www.dotkernel.com) + +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. diff --git a/README.md b/README.md index 5976d82..357d068 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,18 @@ Dotkernel's TOTP authentication. +![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/dot-totp) +![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-totp/1.0.0) + +[![GitHub issues](https://img.shields.io/github/issues/dotkernel/dot-totp)](https://github.com/dotkernel/dot-totp/issues) +[![GitHub forks](https://img.shields.io/github/forks/dotkernel/dot-totp)](https://github.com/dotkernel/dot-totp/network) +[![GitHub stars](https://img.shields.io/github/stars/dotkernel/dot-totp)](https://github.com/dotkernel/dot-totp/stargazers) +[![GitHub license](https://img.shields.io/github/license/dotkernel/dot-totp)](https://github.com/dotkernel/dot-totp/blob/1.0/LICENSE.md) + +[![Build Static](https://github.com/dotkernel/dot-totp/actions/workflows/continuous-integration.yml/badge.svg?branch=1.0)](https://github.com/dotkernel/dot-totp/actions/workflows/continuous-integration.yml) +[![codecov](https://codecov.io/gh/dotkernel/dot-totp/graph/badge.svg?token=R5PopWHvRu)](https://codecov.io/gh/dotkernel/dot-totp) +[![PHPStan](https://github.com/dotkernel/dot-totp/actions/workflows/static-analysis.yml/badge.svg?branch=1.0)](https://github.com/dotkernel/dot-totp/actions/workflows/static-analysis.yml) + ## Install You can install dot-totp by running the following command: @@ -15,7 +27,6 @@ composer require dotkernel/dot-totp > **Note:** These instructions are written in the style of Mezzio middleware configuration and assume the use of Doctrine ORM. > They can be adapted to any database layer or configuration style. If you are using a different framework or service container, follow the same logical steps while adjusting the syntax and configuration to match your environment. - Create a new file configuration `config/autoload/totp.global.php`. ```php @@ -85,6 +96,7 @@ trait TotpTrait To enable TOTP, generate a temporary secret and encode it into a QR code, which the user scans with an authenticator app (e.g., Google Authenticator, Authy). The user then confirms by providing a one-time code from the app. **Steps:** + 1. Generate a temporary Base32 secret. 2. Build a provisioning URI with a label and issuer. 3. Render the URI as a QR code. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..23a2aea --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,28 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | PHP Version | +|---------|--------------------|----------------------------------------------------------------------------------------------------------| +| 1.x | :white_check_mark: | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-totp/1.0.0) | + +## Reporting Potential Security Issues + +If you have encountered a potential security vulnerability in this project, please report it to us at . +We will work with you to verify the vulnerability and patch it. + +When reporting issues, please provide the following information: + +- Component(s) affected +- A description indicating how to reproduce the issue +- A summary of the security vulnerability and impact + +We request that you contact us via the email address above and give the project contributors a chance to resolve the vulnerability and issue a new release prior to any public exposure; +this helps protect the project's users and provides them with a chance to upgrade and/or update to protect their applications. + +## Policy + +If we verify a reported security vulnerability, our policy is: + +- We will patch the current release branch, as well as the immediate prior minor release branch. +- After patching the release branches, we will immediately issue new security fix releases for each patched release branch. diff --git a/composer.json b/composer.json index 34b6ff1..889f837 100644 --- a/composer.json +++ b/composer.json @@ -23,12 +23,14 @@ }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", - "endroid/qr-code": "^6.0.9" + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "endroid/qr-code": "^6.0.9", + "psr/container": "^1.1 || ^2.0.0" }, "require-dev": { "phpunit/phpunit": "^10.2", - "vimeo/psalm": "^6.14.3", + "phpstan/phpstan": "^2.1.33", + "phpstan/phpstan-phpunit": "^2.0.10", "laminas/laminas-coding-standard": "^2.5" }, "autoload": { @@ -44,12 +46,12 @@ "scripts": { "check": [ "@cs-check", - "@test" + "@test", + "@static-analysis" ], "cs-check": "phpcs", "cs-fix": "phpcbf", - "test": "phpunit --colors=always", - "test-coverage": "phpunit --colors=always --coverage-clover clover.xml", - "static-analysis": "psalm --shepherd --stats" + "static-analysis": "phpstan analyse --memory-limit 1G", + "test": "phpunit --colors=always" } } diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..349be25 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,8 @@ +includes: + - vendor/phpstan/phpstan-phpunit/extension.neon +parameters: + level: 5 + paths: + - src + - test + treatPhpDocTypesAsCertain: false diff --git a/test/ConfigProviderTest.php b/test/ConfigProviderTest.php index e4eb1d1..96f189f 100644 --- a/test/ConfigProviderTest.php +++ b/test/ConfigProviderTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Dot\test; +namespace DotTest\Totp; use Dot\Totp\ConfigProvider; use Dot\Totp\Totp; diff --git a/test/TotpFactoryTest.php b/test/TotpFactoryTest.php index abe77f4..0131e97 100644 --- a/test/TotpFactoryTest.php +++ b/test/TotpFactoryTest.php @@ -2,9 +2,8 @@ declare(strict_types=1); -namespace Dot\test; +namespace DotTest\Totp; -use Dot\Totp\Totp; use Dot\Totp\TotpFactory; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; @@ -37,7 +36,6 @@ public function testInvokeReturnsTotpWithConfig(): void $factory = new TotpFactory(); $totp = $factory($container); - $this->assertInstanceOf(Totp::class, $totp); $this->assertSame(60, $totp->getPeriod()); $this->assertSame(8, $totp->getDigits()); $this->assertSame('test', $totp->getAlgorithm()); @@ -56,7 +54,6 @@ public function testInvokeReturnsTotpWithDefaults(): void $factory = new TotpFactory(); $totp = $factory($container); - $this->assertInstanceOf(Totp::class, $totp); $this->assertSame(30, $totp->getPeriod()); $this->assertSame(6, $totp->getDigits()); $this->assertSame('sha1', $totp->getAlgorithm()); diff --git a/test/TotpTest.php b/test/TotpTest.php index 78d0471..59162fc 100644 --- a/test/TotpTest.php +++ b/test/TotpTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Dot\test; +namespace DotTest\Totp; use Dot\Totp\Totp; use PHPUnit\Framework\TestCase;