Skip to content

Feat/fe testing infrastructure#143

Closed
romanetar wants to merge 26 commits into
mainfrom
feat/fe-testing-infrastructure
Closed

Feat/fe testing infrastructure#143
romanetar wants to merge 26 commits into
mainfrom
feat/fe-testing-infrastructure

Conversation

@romanetar

@romanetar romanetar commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

ref https://app.clickup.com/t/9014802374/86bak2ur6

Summary by CodeRabbit

  • New Features

    • Added two-factor authentication during login, including verification, resend, recovery code, and trusted-device options.
    • Introduced improved login and registration screens with clearer step-by-step flows and updated help actions.
    • Added Playwright and Jest test support for frontend and end-to-end coverage.
  • Bug Fixes

    • Login now handles CAPTCHA more flexibly and improves error handling for MFA/session expiry cases.
    • Session and cookie settings were adjusted to better support secure sign-in flows.
  • Documentation

    • Updated testing instructions and added guidance for viewing test reports.

matiasperrone-exo and others added 26 commits June 8, 2026 19:55
feat(2fa): add migration for 2FA schema foundation (Phase I)
feat(2fa): add UserTrustedDevice entity
feat(2fa): add TwoFactorAuditLog entity
feat(2fa): add UserRecoveryCode entity
feat(2fa): add repository interfaces for 2FA entities
feat(2fa): add Doctrine implementations for 2FA repositories
feat(2fa): register 2FA repositories in service container
test(2fa): add repository round-trip tests for 2FA entities
- test: cover canLogin()=false branch in validateCredentials() unit tests
- docs: document known double-query cost in validateCredentials()
- fix: use consistent error message in validateCredentials()
Add tests changes with suggestion
Signed-off-by: romanetar <roman_ag@hotmail.com>
Signed-off-by: romanetar <roman_ag@hotmail.com>
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d27b88f6-3a10-4f8b-baea-91d2166e9714

📥 Commits

Reviewing files that changed from the base of the PR and between 24c00ca and 7acd03d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (96)
  • .github/workflows/pull_request_frontend_tests.yml
  • .gitignore
  • app/Console/Commands/CreateRawUser.php
  • app/Console/Kernel.php
  • app/Http/Controllers/Auth/RegisterController.php
  • app/Http/Controllers/Traits/MFACookieManager.php
  • app/Http/Controllers/UserController.php
  • app/Http/Kernel.php
  • app/Http/Middleware/EncryptCookies.php
  • app/Http/Middleware/TwoFactorRateLimitMiddleware.php
  • app/Repositories/DoctrineTwoFactorAuditLogRepository.php
  • app/Repositories/DoctrineUserRecoveryCodeRepository.php
  • app/Repositories/DoctrineUserTrustedDeviceRepository.php
  • app/Repositories/RepositoriesProvider.php
  • app/Services/Auth/DeviceTrustService.php
  • app/Services/Auth/IDeviceTrustService.php
  • app/Services/Auth/ITwoFactorAuditService.php
  • app/Services/Auth/ITwoFactorGateService.php
  • app/Services/Auth/MFAGateService.php
  • app/Services/Auth/TwoFactorAuditService.php
  • app/Services/Auth/TwoFactorServiceProvider.php
  • app/Strategies/MFA/AbstractMFAChallengeStrategy.php
  • app/Strategies/MFA/EmailOTPMFAChallengeStrategy.php
  • app/Strategies/MFA/IMFAChallengeStrategy.php
  • app/Strategies/MFA/MFAChallengeStrategyFactory.php
  • app/libs/Auth/AuthService.php
  • app/libs/Auth/Models/TwoFactorAuditLog.php
  • app/libs/Auth/Models/User.php
  • app/libs/Auth/Models/UserRecoveryCode.php
  • app/libs/Auth/Models/UserTrustedDevice.php
  • app/libs/Auth/Repositories/ITwoFactorAuditLogRepository.php
  • app/libs/Auth/Repositories/IUserRecoveryCodeRepository.php
  • app/libs/Auth/Repositories/IUserTrustedDeviceRepository.php
  • app/libs/Utils/Services/IAuthService.php
  • babel.config.js
  • config/app.php
  • config/session.php
  • config/two_factor.php
  • database/migrations/Version20260416194357.php
  • doc/mfa-test-gap-report.md
  • docker-compose.yml
  • jest.config.js
  • package.json
  • phpunit.xml
  • playwright.config.ts
  • readme.md
  • resources/js/base_actions.js
  • resources/js/login/actions.js
  • resources/js/login/components/email_error_actions.js
  • resources/js/login/components/email_input_form.js
  • resources/js/login/components/existing_account_actions.js
  • resources/js/login/components/help_links.js
  • resources/js/login/components/otp_help_links.js
  • resources/js/login/components/otp_input_form.js
  • resources/js/login/components/password_input_form.js
  • resources/js/login/components/recovery_code_form.js
  • resources/js/login/components/third_party_identity_providers.js
  • resources/js/login/components/two_factor_form.js
  • resources/js/login/constants.js
  • resources/js/login/login.js
  • resources/js/login/login.module.scss
  • resources/js/shared/HTMLRender.jsx
  • resources/js/signup/signup.js
  • resources/views/auth/login.blade.php
  • routes/web.php
  • start_local_server.sh
  • storage/framework/cache/data/.gitignore
  • tests/AuthServiceValidateCredentialsIntegrationTest.php
  • tests/DeviceTrustServiceTest.php
  • tests/TwoFactorLoginFlowTest.php
  • tests/TwoFactorRepositoriesTest.php
  • tests/e2e/fixtures/index.ts
  • tests/e2e/pages/LoginPage.ts
  • tests/e2e/pages/RegisterPage.ts
  • tests/e2e/tests/auth/login-mfa-flow.spec.ts
  • tests/e2e/tests/auth/login.spec.ts
  • tests/e2e/tests/auth/register.spec.ts
  • tests/e2e/tsconfig.json
  • tests/js/__mocks__/fileMock.js
  • tests/js/components/Banner.test.js
  • tests/js/components/CustomSnackbar.test.js
  • tests/js/components/DividerWithText.test.js
  • tests/js/login/components/two-factor-form.test.js
  • tests/js/login/login.mfa.test.js
  • tests/js/setup.js
  • tests/js/validator/validator.test.js
  • tests/unit/AuthServiceValidateCredentialsTest.php
  • tests/unit/DisqusSSOProfileMappingTest.php
  • tests/unit/MFA/AbstractMFAChallengeStrategyTest.php
  • tests/unit/MFA/EmailOTPMFAChallengeStrategyTest.php
  • tests/unit/MFA/MFAChallengeStrategyFactoryTest.php
  • tests/unit/MFAGateServiceTest.php
  • tests/unit/OAuth2LoginStrategyTest.php
  • tests/unit/TwoFactorAuditServiceTest.php
  • tests/unit/UserTwoFactorTest.php
  • webpack.common.js

📝 Walkthrough

Walkthrough

This PR implements Phase I two-factor authentication (email OTP) for OpenStack ID. It adds Doctrine domain models for trusted devices, recovery codes, and audit logs; Doctrine repositories; DeviceTrustService, TwoFactorAuditService, and MFAGateService; an email-OTP challenge strategy pattern; a refactored UserController MFA flow; a TwoFactorRateLimitMiddleware; a React login refactor with extracted MFA/recovery UI subcomponents; Playwright E2E tests; and Jest unit tests with a GitHub Actions CI workflow.

Changes

Phase I MFA Backend

Layer / File(s) Summary
2FA Domain Models and DB Migration
config/two_factor.php, app/libs/Auth/Models/User.php, app/libs/Auth/Models/UserTrustedDevice.php, app/libs/Auth/Models/UserRecoveryCode.php, app/libs/Auth/Models/TwoFactorAuditLog.php, database/migrations/Version20260416194357.php
Adds 2FA fields (enabled, method, enforced_at, shouldRequire2FA, enable/disable2FA) to User; introduces UserTrustedDevice, UserRecoveryCode, and TwoFactorAuditLog Doctrine entities with validators; adds the Phase I migration creating three new tables and augmenting users.
Repository Interfaces and Doctrine Implementations
app/libs/Auth/Repositories/I*.php, app/Repositories/Doctrine*Repository.php, app/Repositories/RepositoriesProvider.php
Defines IUserTrustedDeviceRepository, IUserRecoveryCodeRepository, and ITwoFactorAuditLogRepository; adds Doctrine implementations with active-device filtering, pessimistic-lock refresh, and bulk-delete; registers all three as singletons.
DeviceTrust, Audit, and Gate Services
app/Services/Auth/IDeviceTrustService.php, app/Services/Auth/DeviceTrustService.php, app/Services/Auth/ITwoFactorAuditService.php, app/Services/Auth/TwoFactorAuditService.php, app/Services/Auth/ITwoFactorGateService.php, app/Services/Auth/MFAGateService.php, app/Services/Auth/TwoFactorServiceProvider.php, config/app.php
Implements DeviceTrustService (SHA-256 token hashing, enrollment, last-seen update, revocation with audit), TwoFactorAuditService (persists TwoFactorAuditLog, conditionally dispatches EmitAuditLogJob), and MFAGateService (delegates challenge decision to device-trust check); wires into TwoFactorServiceProvider.
MFA Challenge Strategy Pattern
app/Strategies/MFA/IMFAChallengeStrategy.php, app/Strategies/MFA/AbstractMFAChallengeStrategy.php, app/Strategies/MFA/EmailOTPMFAChallengeStrategy.php, app/Strategies/MFA/MFAChallengeStrategyFactory.php
Defines IMFAChallengeStrategy; AbstractMFAChallengeStrategy provides session-backed pending state (300s TTL) and recovery-code verification with exclusive lock; EmailOTPMFAChallengeStrategy adds OTP issuance, lock-and-redeem verification, and sibling revocation; factory selects implementation by method string.
AuthService and IAuthService MFA Wrappers
app/libs/Utils/Services/IAuthService.php, app/libs/Auth/AuthService.php
Adds validateCredentials (no session, throws on null/inactive user), loginUser (clears/registers OIDC principal before Auth::login), and four MFA challenge wrapper methods (issue, verify, verifyRecovery, resend) that execute inside a transaction.
UserController MFA Flow, Middleware, Routes, and Cookie
app/Http/Controllers/UserController.php, app/Http/Controllers/Traits/MFACookieManager.php, app/Http/Middleware/TwoFactorRateLimitMiddleware.php, app/Http/Middleware/EncryptCookies.php, app/Http/Kernel.php, routes/web.php, app/Http/Controllers/Auth/RegisterController.php, config/session.php, app/Console/Commands/CreateRawUser.php, app/Console/Kernel.php
Rewires postLogin to gate on MFAGateService and return mfa_required JSON; implements verify2FA, verify2FARecovery, resend2FA, mfaSessionExpired, resolveClientFromMemento; adds MFACookieManager trait; introduces TwoFactorRateLimitMiddleware (fixed-window cache, 429 response); excludes device_trust_token from cookie encryption; registers /2fa/verify,/recovery,/resend routes; makes session secure/same_site env-driven; makes Turnstile optional; adds CreateRawUser command.
PHP Unit, Integration, and Repository Tests
phpunit.xml, tests/TwoFactorLoginFlowTest.php, tests/TwoFactorRepositoriesTest.php, tests/DeviceTrustServiceTest.php, tests/AuthServiceValidateCredentialsIntegrationTest.php, tests/unit/MFA/*, tests/unit/MFAGateServiceTest.php, tests/unit/TwoFactorAuditServiceTest.php, tests/unit/UserTwoFactorTest.php, tests/unit/AuthServiceValidateCredentialsTest.php, tests/unit/OAuth2LoginStrategyTest.php, tests/unit/DisqusSSOProfileMappingTest.php, doc/mfa-test-gap-report.md
Adds integration tests for the full MFA login flow (gate, OTP, recovery, rate limiting, trusted-device bypass, best-effort fault tolerance), repository round-trips and schema constraints, DeviceTrustService, and unit tests for all MFA services/strategies/factories; updates existing tests for isolation; registers the Two Factor Authentication test suite.

MFA Frontend Login Refactor

Layer / File(s) Summary
Login Constants, HTTP Helpers, and Action Creators
resources/js/login/constants.js, resources/js/base_actions.js, resources/js/login/actions.js, resources/js/shared/HTMLRender.jsx
Adds constants (HTTP codes, MFA methods, flow types, error codes); adds postRawRequestFull (returns body, status, finalUrl); adds verify2FA, resend2FA, verifyRecoveryCode, authenticateWithPassword, cancelLogin action creators; adds DOMPurify-based HTMLRender component.
Extracted Login UI Subcomponents
resources/js/login/components/*, resources/js/login/login.module.scss
Extracts EmailInputForm, EmailErrorActions, ExistingAccountActions, HelpLinks, OTPHelpLinks, OTPInputForm, PasswordInputForm, RecoveryCodeForm, ThirdPartyIdentityProviders, and TwoFactorForm (with countdown timer, resend cooldown, trust-device checkbox); adds SCSS rules for countdown, trust-device row, and disabled link states.
LoginPage Refactor and Blade Template Update
resources/js/login/login.js, resources/views/auth/login.blade.php, resources/js/signup/signup.js, webpack.common.js
Rewrites LoginPage as a multi-stage flow (email/OTP/password/MFA/recovery) with centralized handleMfaError, handleAuthenticatePasswordOk, and cancel/resend/recovery handlers; render selects subcomponents by authFlow; exports LoginPage; updates Blade template with 2FA endpoint globals and session OTP config; makes signup captcha conditional.

Test and CI Infrastructure

Layer / File(s) Summary
Jest, Playwright, Babel, and CI Configuration
jest.config.js, playwright.config.ts, babel.config.js, package.json, .github/workflows/pull_request_frontend_tests.yml, docker-compose.yml, start_local_server.sh, .gitignore, readme.md
Adds Jest (JSDOM, Babel transform, coverage), Playwright (Chromium, CI controls), GitHub Actions workflow (js-unit-tests and e2e-tests jobs with MySQL/Redis, artifact uploads), Docker Compose playwright service and volume, Playwright Chromium install in local dev script, and updated developer docs.
Playwright E2E Fixtures, Page Objects, and Specs
tests/e2e/fixtures/index.ts, tests/e2e/pages/LoginPage.ts, tests/e2e/pages/RegisterPage.ts, tests/e2e/tests/auth/*, tests/e2e/tsconfig.json
Adds APP_URL-rewriting fixtures with window endpoint override injection; LoginPage and RegisterPage page objects; login.spec.ts (5 basic flow tests), login-mfa-flow.spec.ts (8 MFA UI tests with route stubs for challenge/verify/resend/cancel/recovery/expired/rate-limit), register.spec.ts (3 tests).
Jest Unit Tests for Login MFA and UI Components
tests/js/__mocks__/fileMock.js, tests/js/setup.js, tests/js/components/*.test.js, tests/js/login/components/two-factor-form.test.js, tests/js/login/login.mfa.test.js, tests/js/validator/validator.test.js
Adds Banner, CustomSnackbar, DividerWithText component tests; TwoFactorForm tests (countdown, expired state, disabled button, error label); login.mfa.test.js (handleMfaError and handleAuthenticatePasswordOk scenarios); emailValidator and buildPasswordValidationSchema tests.

Sequence Diagram

sequenceDiagram
  participant Browser
  participant UserController
  participant AuthService
  participant MFAGateService
  participant DeviceTrustService
  participant EmailOTPMFAChallengeStrategy
  participant TwoFactorAuditService

  Browser->>UserController: POST /auth/login (password)
  UserController->>AuthService: validateCredentials(email, password)
  AuthService-->>UserController: User (no session created)
  UserController->>MFAGateService: requiresChallenge(user, cookieToken)
  MFAGateService->>DeviceTrustService: isDeviceTrusted(user, cookieToken)
  DeviceTrustService-->>MFAGateService: false
  MFAGateService-->>UserController: true
  UserController->>AuthService: issueMFAChallenge(user, EmailOTPStrategy)
  AuthService->>EmailOTPMFAChallengeStrategy: issueChallenge(user, client, remember)
  EmailOTPMFAChallengeStrategy-->>AuthService: {otp_length, otp_lifetime}
  UserController->>TwoFactorAuditService: log(EventChallengeIssued)
  UserController-->>Browser: 200 {error_code: mfa_required, otp_length, otp_lifetime}

  Browser->>UserController: POST /auth/login/2fa/verify (otp_value, trust_device)
  UserController->>AuthService: verifyMFAChallenge(user, strategy, otp_value)
  AuthService->>EmailOTPMFAChallengeStrategy: verifyChallenge(user, otp_value, client)
  EmailOTPMFAChallengeStrategy-->>AuthService: void (redeems OTP, revokes siblings)
  UserController->>AuthService: loginUser(user, remember)
  UserController->>DeviceTrustService: trustDevice(user, userAgent, ip)
  DeviceTrustService-->>UserController: rawToken
  UserController->>TwoFactorAuditService: log(EventChallengeSucceeded + EventDeviceTrusted)
  UserController-->>Browser: redirect via login_strategy.postLogin()
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • OpenStackweb/openstackid#121: Both PRs touch the Turnstile cf-turnstile-response validation in RegisterController::validator — this PR makes the field conditional on services.turnstile.secret being set.

Suggested reviewers

  • smarcet
  • martinquiroga-exo

Poem

🐰 Hop, hop, through the MFA gate,
A cookie token seals the fate!
SHA-256 hides the raw key,
OTP codes set the user free.
Rate limits guard the login door—
This bunny's 2FA to the core! 🔐

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/fe-testing-infrastructure

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 PHPStan (2.2.2)

Composer install failed: this project depends on private packages that require authentication (e.g. GitLab/GitHub, Laravel Nova, etc.).
CodeRabbit tooling environment cannot access private registries.
If your project requires private packages, disable the PHPStan tool in your coderabbit settings.

Instead, run PHPStan in a CI/CD pipeline where you can use custom packages — our pipeline remediation tool can use the PHPStan output from your CI/CD pipeline.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@romanetar romanetar closed this Jun 30, 2026
@github-actions

Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/openstackid/openapi/pr-143/

This page is automatically updated on each push to this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants