Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9d127aa
feat: Add MultiFactor Authentication
matiasperrone-exo Apr 16, 2026
a598ec3
chore: Add PR's requested changes and additional AI comments
matiasperrone-exo Apr 24, 2026
84e8cb3
chore: Add PR's requested changes
matiasperrone-exo Apr 29, 2026
c2653ca
chore: Add guards on setEventType and setMethod methods on TwoFactorA…
matiasperrone-exo Apr 29, 2026
f33a84f
chore: Guard the unique-index migration against existing duplicates.
matiasperrone-exo Apr 29, 2026
e949fa9
chore: Add UniqueConstraint annotation to mirror the database migration
matiasperrone-exo Apr 29, 2026
ab499ec
chore: Refactor UserRecoveryCode and TwoFactorAuditLog models; update…
matiasperrone-exo May 14, 2026
56a1975
chore: remove BaseEntity oveloaded props and methods. fix skipped test
matiasperrone-exo May 18, 2026
d172dd2
chore: unify migrations
matiasperrone-exo May 21, 2026
36a45a6
Feature | Implement Two-Factor Authentication (2FA) support for users…
matiasperrone-exo Aug 11, 2026
7360e80
MFA hardening: OAuth2 memento guard on recovery, DTO refactors, full …
smarcet Aug 12, 2026
70c7d9c
fix(session): restore Secure and SameSite=None defaults for the sessi…
smarcet Sep 22, 2026
1886467
fix(auth): split the login SPA reset from cancelLogin to keep OAuth2 …
smarcet Sep 22, 2026
4ec70ba
fix(2fa): reserve verify/recovery attempts atomically before the cont…
smarcet Sep 22, 2026
002c2cd
chore(auth): drop unused imports from MFACookieManager
smarcet Sep 22, 2026
6790e64
fix(2fa): require CSRF on the profile 2FA enable and recovery-code re…
smarcet Sep 22, 2026
db80cc6
fix(2fa): make the pending MFA session TTL configurable, default 600s
smarcet Sep 22, 2026
46b39fd
fix(tests): drop stale OAuth2ProtocolTestCase.php entry from phpunit.xml
smarcet Sep 22, 2026
e9b6b98
fix(e2e): assert the intended post-error password step, not the old r…
smarcet Sep 22, 2026
42ce437
fix: MFA-enforced accounts leak enumeration via passwordless login gu…
smarcet Sep 23, 2026
519f2cc
Recovery code login flow UI (#151)
romanetar Sep 23, 2026
5d6cb70
chore: fix ssl redirect
smarcet Sep 23, 2026
d6c7ac2
fix(2fa): apply the local MFA policy to social login
smarcet Sep 23, 2026
161f7fd
fix(session): finish the SESSION_COOKIE_SECURE rename after rebasing …
smarcet Oct 8, 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.0
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.0
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