Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 9 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ The plugin follows a provider pattern. `Two_Factor_Core` owns the login intercep

### Core Files

- **`two-factor.php`** — Entry point. Defines `TWO_FACTOR_DIR` and `TWO_FACTOR_VERSION`, loads all core files, instantiates `Two_Factor_Compat`, and calls `Two_Factor_Core::add_hooks()`.
- **`two-factor.php`** — Entry point. Defines `TWO_FACTOR_DIR` and `TWO_FACTOR_VERSION`, loads all core files (including the settings class), instantiates `Two_Factor_Compat`, and calls `Two_Factor_Core::add_hooks()`. Also registers the wp-admin settings page (Settings → Two-Factor) and the filter callbacks that enforce the site-wide enabled-providers option (`two_factor_filter_enabled_providers`, `two_factor_filter_enabled_providers_for_user`).
- **`class-two-factor-core.php`** — Central class. Owns the login flow, user meta, nonce management, rate limiting, session tracking, REST API endpoints, and the user profile settings UI.
- **`class-two-factor-compat.php`** — Compatibility shims for third-party plugins (currently: Jetpack SSO). New integrations go here; the goal is to avoid any plugin-specific logic outside this file.
- **`settings/class-two-factor-settings.php`** — `Two_Factor_Settings`, renders the site-wide settings screen (Settings → Two-Factor, requires `manage_options`) where admins choose which providers are available on the site. Saving writes the `two_factor_enabled_providers` option; enforcement happens via the filters registered in `two-factor.php`, not in this class.
- **`providers/class-two-factor-provider.php`** — Abstract base class all providers extend. Defines the required interface: `get_label()`, `is_available_for_user()`, `authentication_page()`, `validate_authentication()`, and optional hooks for REST routes, settings UI, and uninstall cleanup.
- **`providers/`** — Concrete providers: `class-two-factor-totp.php`, `class-two-factor-email.php`, `class-two-factor-backup-codes.php`, `class-two-factor-dummy.php`.
- **`includes/`** — Custom `login_header()` and `login_footer()` template functions that replace the WordPress core versions with additional filter hooks. Excluded from PHPCS because they intentionally deviate from core function signatures. Do not modify files in includes/ directly. They are intentionally kept close to WordPress core function signatures to ease future merging into Core. Any functional changes should go through the filter hooks they expose instead.
Expand Down Expand Up @@ -97,13 +98,20 @@ New providers should follow this pattern rather than registering hooks from outs
| `USER_FAILED_LOGIN_ATTEMPTS_KEY` | `_two_factor_failed_login_attempts` | Failed attempt count |
| `USER_PASSWORD_WAS_RESET_KEY` | `_two_factor_password_was_reset` | Flags compromised-password reset |

### Key Site Options (constants on `Two_Factor_Core`)

| Constant | Option Key | Purpose |
|---|---|---|
| `ENABLED_PROVIDERS_OPTION_KEY` | `two_factor_enabled_providers` | Provider class names enabled site-wide via the settings page; never saved means all providers are allowed. |

### REST API

Namespace: `two-factor/1.0` (constant `Two_Factor_Core::REST_NAMESPACE`). Each provider that exposes REST endpoints registers its own routes in `register_rest_routes()` called from its constructor.

## Code Standards

- PHP 7.2+ compatibility required; enforced by PHPCompatibilityWP.
- WordPress 6.9+ required.
- Follows WordPress coding standards (WPCS) and WordPress-VIP-Go rules.
- `includes/` is excluded from PHPCS — those files intentionally override core functions.
- The codebase does not fully pass all PHPCS checks (known issue [#437](https://github.com/WordPress/two-factor/issues/437)). Do not treat existing violations as license to introduce new ones.
10 changes: 6 additions & 4 deletions TESTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Pass PHPUnit arguments through the `composer` wrapper:

```bash
# Run a single test class
npm run composer -- test -- --filter Tests_Two_Factor_Core
npm run composer -- test -- --filter Test_ClassTwoFactorCore

# Run a single test method
npm run composer -- test -- --filter test_create_login_nonce
Expand All @@ -49,7 +49,7 @@ Smoke tests that the plugin loaded correctly: the `TWO_FACTOR_DIR` constant is d

### Core — `tests/class-two-factor-core.php`

**Class:** `Tests_Two_Factor_Core` · **Group:** `core`
**Class:** `Test_ClassTwoFactorCore` · **Group:** `core`
The largest test file. Covers the full authentication lifecycle managed by `Two_Factor_Core`:

- Hook registration (`add_hooks`)
Expand All @@ -58,10 +58,11 @@ The largest test file. Covers the full authentication lifecycle managed by `Two_
- Login nonce creation, verification, and deletion
- Rate limiting (`get_user_time_delay`, `is_user_rate_limited`)
- Session management: two-factor factored vs. non-factored sessions, session destruction on 2FA enable/disable, revalidation
- Password reset flow (compromise detection, email notifications, reset notices)
- Password reset flow (compromise detection, email notifications, reset notices and their nonce validation)
- REST API permission callbacks (`rest_api_can_edit_user_and_update_two_factor_options`)
- User settings actions (`trigger_user_settings_action`, `current_user_can_update_two_factor_options`)
- Uninstall cleanup
- Profile settings UI output (`user_two_factor_options`), including recovery-codes wording with and without the Backup Codes provider
- Uninstall cleanup (user meta and site-wide options)
- Filter hooks (`two_factor_providers`, `two_factor_primary_provider_for_user`, `two_factor_user_api_login_enable`)

### Provider Base Class — `tests/providers/class-two-factor-provider.php`
Expand Down Expand Up @@ -158,3 +159,4 @@ Tests `Two_Factor_Dummy_Secure` (a fixture that always _fails_ authentication, u

- **`tests/bootstrap.php`** — Locates the WordPress test library (via `WP_TESTS_DIR` env var, relative path, or `/tmp/wordpress-tests-lib`), loads the plugin via `muplugins_loaded`, then boots the WP test environment.
- **`tests/class-two-factor-dummy-secure.php`** — Defines `Two_Factor_Dummy_Secure`, a test-only provider class that spoofs the key of `Two_Factor_Dummy` but always fails `validate_authentication`. Used by `Tests_Two_Factor_Dummy_Secure` and some core tests.
- **`Two_Factor_Redirect_Exception`** — Defined in `tests/class-two-factor-core.php`. The core tests' render helper intercepts `wp_redirect` and throws this exception, so code paths that redirect and `exit` can be tested without terminating the test process.
Loading