Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1585d0b
feat: Add MultiFactor Authentication
matiasperrone-exo Apr 16, 2026
385342a
chore: Add PR's requested changes and additional AI comments
matiasperrone-exo Apr 24, 2026
93c3310
chore: Add PR's requested changes
matiasperrone-exo Apr 29, 2026
706e775
chore: Add guards on setEventType and setMethod methods on TwoFactorA…
matiasperrone-exo Apr 29, 2026
fb205a8
chore: Guard the unique-index migration against existing duplicates.
matiasperrone-exo Apr 29, 2026
11fe73b
chore: Add UniqueConstraint annotation to mirror the database migration
matiasperrone-exo Apr 29, 2026
ac48cbc
chore: Refactor UserRecoveryCode and TwoFactorAuditLog models; update…
matiasperrone-exo May 14, 2026
11904d2
chore: remove BaseEntity oveloaded props and methods. fix skipped test
matiasperrone-exo May 18, 2026
e97bcd9
chore: unify migrations
matiasperrone-exo May 21, 2026
1428636
Feature | Implement Two-Factor Authentication (2FA) support for users…
matiasperrone-exo Aug 11, 2026
56e8f09
MFA hardening: OAuth2 memento guard on recovery, DTO refactors, full …
smarcet Aug 12, 2026
660d7c4
fix(session): restore Secure and SameSite=None defaults for the sessi…
smarcet Sep 22, 2026
66b252e
fix(auth): split the login SPA reset from cancelLogin to keep OAuth2 …
smarcet Sep 22, 2026
fec8fd1
fix(2fa): reserve verify/recovery attempts atomically before the cont…
smarcet Sep 22, 2026
1590816
chore(auth): drop unused imports from MFACookieManager
smarcet Sep 22, 2026
d759216
fix(2fa): require CSRF on the profile 2FA enable and recovery-code re…
smarcet Sep 22, 2026
ca9fe61
fix(2fa): make the pending MFA session TTL configurable, default 600s
smarcet Sep 22, 2026
e058c9f
fix(tests): drop stale OAuth2ProtocolTestCase.php entry from phpunit.xml
smarcet Sep 22, 2026
fb1b233
fix(e2e): assert the intended post-error password step, not the old r…
smarcet Sep 22, 2026
3e9c5d0
fix: MFA-enforced accounts leak enumeration via passwordless login gu…
smarcet Sep 23, 2026
1405ba9
Recovery code login flow UI (#151)
romanetar Sep 23, 2026
c9f5345
chore: fix ssl redirect
smarcet Sep 23, 2026
4bd1a20
fix(2fa): apply the local MFA policy to social login
smarcet Sep 23, 2026
a622991
fix(session): finish the SESSION_COOKIE_SECURE rename after rebasing …
smarcet Oct 8, 2026
3bdab76
fix(2fa): render the 2FA screen when social login was started with pr…
smarcet Oct 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions .github/workflows/pull_request_frontend_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
name: Front End Tests On Pull Request

on:
pull_request:
types: [opened, reopened, edited, synchronize]
branches: ["main"]

jobs:

js-unit-tests:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Run Jest unit tests
run: yarn test:unit:ci
- name: Upload Jest coverage
uses: actions/upload-artifact@v4
if: always()
with:
name: jest-coverage
path: tests/js/coverage
retention-days: 5

e2e-tests:
runs-on: ubuntu-latest
env:
APP_ENV: testing
APP_DEBUG: true
APP_KEY: base64:4vh0op/S1dAsXKQ2bbdCfWRyCI9r8NNIdPXyZWt9PX4=
APP_URL: http://localhost:8001
DEV_EMAIL_TO: smarcet@gmail.com
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_DATABASE: idp_test
DB_USERNAME: root
DB_PASSWORD: 1qaz2wsx
REDIS_HOST: 127.0.0.1
REDIS_PORT: 6379
REDIS_DB: 0
REDIS_PASSWORD: 1qaz2wsx
REDIS_DATABASES: 16
SSL_ENABLED: false
SESSION_DRIVER: redis
SESSION_COOKIE_SECURE: false
SESSION_COOKIE_SAME_SITE: lax
PHP_VERSION: 8.3
OTEL_SDK_DISABLED: true
OTEL_SERVICE_ENABLED: false
TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: 1qaz2wsx
MYSQL_DATABASE: idp_test
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- name: Create Redis
uses: supercharge/redis-github-action@1.8.1
with:
redis-port: 6379
redis-password: 1qaz2wsx
- name: Check out repository code
uses: actions/checkout@v4
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
extensions: pdo_mysql, mbstring, exif, pcntl, bcmath, sockets, gettext, apcu
- name: Install PHP dependencies
uses: ramsey/composer-install@v3
env:
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.PAT }}"} }'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Build frontend assets
run: yarn build
- name: Prepare application
run: |
./update_doctrine.sh
php artisan doctrine:migrations:migrate --no-interaction
php artisan db:seed --force
php artisan idp:create-super-admin test@test.com '1Qaz2wsx!'
php artisan idp:create-raw-user e2e@test.com '1Qaz2wsx!'
for i in 001 002 003 004 005 006 007 008 009 010 011 012; do
php artisan idp:create-super-admin "mfa-ts-$i@test.com" '1Qaz2wsx!'
done
php artisan idp:create-super-admin mfa-oauth2@test.com '1Qaz2wsx!'
php artisan idp:create-super-admin mfa-oauth2-consent@test.com '1Qaz2wsx!'
php artisan idp:create-super-admin mfa-oauth2-trust@test.com '1Qaz2wsx!'
php artisan idp:create-oauth2-test-client
- name: Install Playwright Chromium
run: npx playwright install --with-deps chromium
- name: Start web server
run: php artisan serve --host=127.0.0.1 --port=8001 &
- name: Wait for server to be ready
run: |
for i in $(seq 1 20); do
curl -sf http://localhost:8001 > /dev/null 2>&1 && echo "Server ready" && exit 0
sleep 2
done
echo "Server did not start in time" && exit 1
- name: Run E2E tests
run: yarn test:e2e --reporter=list
- name: Upload Playwright report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: tests/e2e/report
retention-days: 7
- name: Upload Playwright traces
uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-traces
path: test-results/
retention-days: 7
2 changes: 2 additions & 0 deletions .github/workflows/pull_request_unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ jobs:
PHP_VERSION: 8.3
OTEL_SDK_DISABLED: true
OTEL_SERVICE_ENABLED: false
TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
services:
mysql:
image: mysql:8.4
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ jobs:
PHP_VERSION: 8.3
OTEL_SDK_DISABLED: true
OTEL_SERVICE_ENABLED: false
TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
services:
mysql:
image: mysql:8.4
Expand Down
140 changes: 140 additions & 0 deletions .github/workflows/push_frontend_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: Front End Tests On Push

on: push

jobs:

js-unit-tests:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Run Jest unit tests
run: yarn test:unit:ci
- name: Upload Jest coverage
uses: actions/upload-artifact@v4
if: always()
with:
name: jest-coverage
path: tests/js/coverage
retention-days: 5

e2e-tests:
runs-on: ubuntu-latest
env:
APP_ENV: testing
APP_DEBUG: true
APP_KEY: base64:4vh0op/S1dAsXKQ2bbdCfWRyCI9r8NNIdPXyZWt9PX4=
APP_URL: http://localhost:8001
DEV_EMAIL_TO: smarcet@gmail.com
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_DATABASE: idp_test
DB_USERNAME: root
DB_PASSWORD: 1qaz2wsx
REDIS_HOST: 127.0.0.1
REDIS_PORT: 6379
REDIS_DB: 0
REDIS_PASSWORD: 1qaz2wsx
REDIS_DATABASES: 16
SSL_ENABLED: false
SESSION_DRIVER: redis
SESSION_COOKIE_SECURE: false
SESSION_COOKIE_SAME_SITE: lax
PHP_VERSION: 8.3
OTEL_SDK_DISABLED: true
OTEL_SERVICE_ENABLED: false
TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
TURNSTILE_SECRET_KEY: ${{ secrets.TURNSTILE_SECRET_KEY }}
# `php artisan serve` (below) is PHP's built-in single-threaded dev server —
# it can only handle one request at a time, so Playwright workers must stay
# at 1 here or concurrent page loads queue up and blow the 30s test timeout.
PLAYWRIGHT_WORKERS: 1
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: 1qaz2wsx
MYSQL_DATABASE: idp_test
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- name: Create Redis
uses: supercharge/redis-github-action@1.8.1
with:
redis-port: 6379
redis-password: 1qaz2wsx
- name: Check out repository code
uses: actions/checkout@v4
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
extensions: pdo_mysql, mbstring, exif, pcntl, bcmath, sockets, gettext, apcu
- name: Install PHP dependencies
uses: ramsey/composer-install@v3
env:
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.PAT }}"} }'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'yarn'
- name: Install JS dependencies
run: yarn install --frozen-lockfile
- name: Build frontend assets
run: yarn build
- name: Prepare application
run: |
./update_doctrine.sh
php artisan doctrine:migrations:migrate --no-interaction
php artisan db:seed --force
php artisan idp:create-super-admin test@test.com '1Qaz2wsx!'
php artisan idp:create-raw-user e2e@test.com '1Qaz2wsx!'
for i in 001 002 003 004 005 006 007 008 009 010 011 012; do
php artisan idp:create-super-admin "mfa-ts-$i@test.com" '1Qaz2wsx!'
done
php artisan idp:create-super-admin mfa-oauth2@test.com '1Qaz2wsx!'
php artisan idp:create-super-admin mfa-oauth2-consent@test.com '1Qaz2wsx!'
php artisan idp:create-super-admin mfa-oauth2-trust@test.com '1Qaz2wsx!'
php artisan idp:create-oauth2-test-client
- name: Install Playwright Chromium
run: npx playwright install --with-deps chromium
- name: Start web server
run: php artisan serve --host=127.0.0.1 --port=8001 &
- name: Wait for server to be ready
run: |
for i in $(seq 1 20); do
curl -sf http://localhost:8001 > /dev/null 2>&1 && echo "Server ready" && exit 0
sleep 2
done
echo "Server did not start in time" && exit 1
- name: Run E2E tests
run: yarn test:e2e --reporter=list
- name: Upload Playwright report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: tests/e2e/report
retention-days: 7
- name: Upload Playwright traces
uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-traces
path: test-results/
retention-days: 7
13 changes: 12 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,15 @@ model.sql
/.phpunit.cache/
docker-compose/mysql/model/*.sql
public/assets/*.map
public/assets/css/*.map
public/assets/css/*.map
.codegraph
docs/plans

# Playwright
/tests/e2e/report/
/test-results/

# Jest
/tests/js/coverage/
/playwright-report/
/.playwright-out/
Loading
Loading