Why does Laravel `FormRequest` authorizes first and validates input last? How do I change this behaviour? What unexpected drawbacks might arise?

I use custom request classes which extend Laravavel’s FormRequest (User Guide, API Documentaion) in order

  1. to validate the input parameters, and
  2. to authorize the request.

The skeleton looks like this:

class MyRequest extends FormRequest {
  /**
   * Determines if the user is authorized to make this request.
   */
  public function authorize(): bool {
    // TODO: Put your authorization logic here
  }

  /**
   * Returns the validation rules that apply to the request.
   */
  public function rules(): array
  {
    // TODO: Define your validation rules here
  }
}

Usually and specifically in my case, the decision whether a user is authorized to make the request depends on the input parameters. Hence, I expected that the syntax of the input parameters is validated first and then the request is authorized. In particular, for proper authorization it is very often necessary to run DB queries using the input parameters of the request. Obviously, the DB query should only be executed, if the syntax of input parameters has already been validated.

Having said this, I would have expected the following order of events

  1. authentication of the user (happens in the middleware, out-of-scope here)
  2. validation of input parameters (e.g. method rules is being called)
  3. authorization of the user (e.g. method authorize is being called)

However, Laravel calls 2. and 3. in inverse order. Hence, the user must be authorized at a point when the input has not been validated yet. The problematic code seems to be in IlluminateValidationValidatesWhenResolvedTrait::validateResolved. This method first calls the authorization logic and then calls for validation of input.

Proposed solution

For now, I consider to write my own base class FormRequestWithBugfix extend it from FormRequest overwrite validateResolved such that it calls the methods it better order and then use FormRequestWithBugfix as the common base class throughout my project.

Questions:

  1. Will the proposed solution work? Are there any corner cases I should be aware of which are not addressed by the proposed solution?
  2. What are the reasons why Laravel implemented it this way in the first step? I assume there should be good reasons to do it the way how Laravel did it.
  3. Are there any security implications I should be aware of? What are potential drawbacks if the order is changed?

Source: Laravel

Leave a Reply