Skip to content

Evaluate after middleware#96

Closed
andresilvagomez wants to merge 1 commit intostephenjude:3.xfrom
andresilvagomez:3.x
Closed

Evaluate after middleware#96
andresilvagomez wants to merge 1 commit intostephenjude:3.xfrom
andresilvagomez:3.x

Conversation

@andresilvagomez
Copy link
Copy Markdown
Contributor

@andresilvagomez andresilvagomez commented Dec 17, 2025

Summary by CodeRabbit

  • New Features

    • Authentication configuration now supports dynamic conditions via closures for passkey authentication, two-factor authentication, and challenge middleware settings.
  • Refactor

    • Improved condition evaluation to process at runtime, enabling more flexible and efficient authentication policy management.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings December 17, 2025 15:42
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 17, 2025

Walkthrough

This change introduces lazy evaluation support to the TwoFactorAuthenticationPlugin by adding Closure type unions to three configuration properties. Properties now store closures directly and evaluation is deferred to getters, enabling runtime evaluation rather than setup-time evaluation.

Changes

Cohort / File(s) Summary
Lazy Evaluation Refactor
src/TwoFactorAuthenticationPlugin.php
Updated property types to accept Closure: enablePasskeyAuthentication (bool | Closure), enableTwoFactorAuthentication (bool | Closure), twoFactorChallengeMiddleware (string | bool | Closure). Changed setters to store conditions directly without eager evaluation. Updated getters to evaluate stored properties at runtime.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Verify all getters correctly evaluate closures versus raw boolean/string values
  • Ensure deprecated setters handle deferred assignment and maintain backward compatibility
  • Validate that hasEnabledTwoFactorAuthentication() and hasEnabledPasskeyAuthentication() properly handle both direct values and closure evaluation
  • Confirm type narrowing and evaluation logic are consistent across all affected methods

Poem

🐰 A rabbit hops through configs new,
Where closures bloom and booleans brew,
No hasty checks, we now defer—
Lazy eval makes logic stir!
Authentication, flexible and spry,
Hops onward to the sky. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Evaluate after middleware' is vague and does not clearly communicate the main changes, which involve introducing union types for properties to allow closures in enabling flags and middleware evaluation. Consider a more descriptive title such as 'Support closure-based configuration for authentication flags and middleware' or 'Defer middleware evaluation using union types and closures'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.1.33)

Invalid configuration:
Unexpected item 'parameters › checkOctaneCompatibility'.
Invalid configuration:
Unexpected item 'parameters › checkModelProperties'.
Invalid configuration:
Unexpected item 'parameters › checkMissingIterableValueType'.


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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/TwoFactorAuthenticationPlugin.php (1)

80-80: Return type mismatch: getTwoFactorChallengeMiddleware() declares string but can return bool.

The method at line 165 declares return type string, but the property $twoFactorChallengeMiddleware can be string | bool | Closure (line 42), and the evaluate() method preserves non-Closure values as-is. The deprecated setChallengeTwoFactorMiddleware() explicitly accepts bool, creating a path where false can be returned.

This causes a problem at line 80: ->middleware($this->getTwoFactorChallengeMiddleware()) passes the result directly to Filament's middleware() method without filtering. If the middleware is false (via the deprecated setter), this violates the method's expectations. Line 98 correctly handles this by wrapping the same method call in array_filter(), which filters out false values.

Update the return type to string | bool and either wrap line 80 in conditional handling or use array_filter() like line 98 does.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4752dfb and 0c5d888.

📒 Files selected for processing (1)
  • src/TwoFactorAuthenticationPlugin.php (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/TwoFactorAuthenticationPlugin.php (2)
src/Middleware/TwoFactorChallenge.php (1)
  • TwoFactorChallenge (8-32)
src/TwoFactorAuthenticatable.php (2)
  • hasEnabledTwoFactorAuthentication (22-26)
  • hasEnabledPasskeyAuthentication (28-31)
🔇 Additional comments (4)
src/TwoFactorAuthenticationPlugin.php (4)

29-29: LGTM! Property types updated for lazy evaluation.

The addition of Closure to these property types enables runtime evaluation while maintaining backward compatibility with existing bool and string values.

Also applies to: 31-31, 42-42


210-212: LGTM! Lazy evaluation correctly implemented.

The method stores closures directly and relies on hasEnabledTwoFactorAuthentication() (line 219) and getTwoFactorChallengeMiddleware() (line 167) to evaluate them at runtime.


219-219: LGTM! Evaluation logic is correct.

The getter properly evaluates the property to return a bool, maintaining type safety.


224-224: LGTM! Passkey authentication follows the same lazy evaluation pattern.

The setter stores the condition directly (line 229) and the getter evaluates it at runtime (line 224), consistent with the refactoring approach.

Also applies to: 229-229

public function requirePasswordWhenEnabling(bool | Closure $condition = true): static
{
$this->isPasswordRequiredForEnable = $this->evaluate($condition);
$this->isPasswordRequiredForEnable = $condition;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Deprecated setters now store closures but getters don't evaluate them.

These deprecated setters now assign closures directly without evaluation (Lines 107, 115, 123), but their corresponding getters (isPasswordRequiredForEnable(), isPasswordRequiredForDisable(), isPasswordRequiredForRegenerateRecoveryCodes() at lines 131-143) return the raw property value without calling $this->evaluate().

If a user passes a Closure to these deprecated setters, the getters will return a Closure instead of a bool, violating their return type and breaking existing code.

Apply this diff to fix the deprecated getters:

 #[Deprecated('Use twoFactorSetupRequiresPassword instead')]
 public function isPasswordRequiredForRegenerateRecoveryCodes(): bool
 {
-    return $this->isPasswordRequiredForRegenerateRecoveryCodes;
+    return $this->evaluate($this->isPasswordRequiredForRegenerateRecoveryCodes);
 }

 #[Deprecated('Use twoFactorSetupRequiresPassword instead')]
 public function isPasswordRequiredForEnable(): bool
 {
-    return $this->isPasswordRequiredForEnable;
+    return $this->evaluate($this->isPasswordRequiredForEnable);
 }

 #[Deprecated('Use twoFactorSetupRequiresPassword instead')]
 public function isPasswordRequiredForDisable(): bool
 {
-    return $this->isPasswordRequiredForDisable;
+    return $this->evaluate($this->isPasswordRequiredForDisable);
 }

Also applies to: 115-115, 123-123

🤖 Prompt for AI Agents
In src/TwoFactorAuthenticationPlugin.php lines 107, 115, 123 and getters at
131-143, the deprecated setters store closures into properties but the getters
return the raw property values; update each getter (isPasswordRequiredForEnable,
isPasswordRequiredForDisable, isPasswordRequiredForRegenerateRecoveryCodes) to
evaluate the stored value via $this->evaluate($this->propertyName) and return
the evaluated result coerced to bool so a Closure yields a boolean instead of
being returned directly.

Comment on lines +162 to 163
return $this->evaluate($this->twoFactorChallengeMiddleware);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Return type mismatch: methods can return bool but declare string.

Both getter methods have a return type of string, but since twoFactorChallengeMiddleware is typed as string | bool | Closure (line 42), evaluating it can return false. This violates the type contract and may cause issues at line 80 where the result is passed directly to ->middleware().

Consider one of these solutions:

Solution 1: Change return type to nullable string and filter false values

-public function getChallengeTwoFactorMiddleware(): string
+public function getChallengeTwoFactorMiddleware(): ?string
 {
-    return $this->evaluate($this->twoFactorChallengeMiddleware);
+    $middleware = $this->evaluate($this->twoFactorChallengeMiddleware);
+    return $middleware === false ? null : $middleware;
 }

-public function getTwoFactorChallengeMiddleware(): string
+public function getTwoFactorChallengeMiddleware(): ?string
 {
-    return $this->evaluate($this->twoFactorChallengeMiddleware);
+    $middleware = $this->evaluate($this->twoFactorChallengeMiddleware);
+    return $middleware === false ? null : $middleware;
 }

Solution 2: Change return type to string | bool to match the property

-public function getChallengeTwoFactorMiddleware(): string
+public function getChallengeTwoFactorMiddleware(): string | bool
 {
     return $this->evaluate($this->twoFactorChallengeMiddleware);
 }

-public function getTwoFactorChallengeMiddleware(): string
+public function getTwoFactorChallengeMiddleware(): string | bool
 {
     return $this->evaluate($this->twoFactorChallengeMiddleware);
 }

Also applies to: 167-168

🤖 Prompt for AI Agents
In src/TwoFactorAuthenticationPlugin.php around lines 162-163 (and likewise
167-168), the getter methods declare a return type of string but may return
false because $twoFactorChallengeMiddleware is typed as string|bool|Closure;
update the method signatures to return string|bool to match the property OR
change them to ?string and ensure they convert/filter out false before
returning; additionally, update the call site (around line 80) so ->middleware()
is only passed a string (e.g., check if the getter returned a string before
calling ->middleware(), or cast/throw if false) to preserve type-safety and
avoid passing false into middleware.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modifies the two-factor authentication plugin to defer closure evaluation until after middleware execution. Previously, closures were evaluated immediately when configuration methods were called; now they are stored and evaluated only when the values are accessed through getter methods.

  • Changed property type hints to accept Closure in addition to their original types
  • Modified setter methods to store closures directly instead of evaluating them immediately
  • Updated getter methods to evaluate closures when the values are accessed

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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