10000 [HttpFoundation] `#[IsSignatureValid]` attribute by santysisi · Pull Request #60395 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[HttpFoundation] #[IsSignatureValid] attribute #60395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 7.4
Choose a base branch
from

Conversation

santysisi
Copy link
Contributor
@santysisi santysisi commented May 9, 2025
Q A
Branch? 7.4
Bug fix? no
New feature? yes
Deprecations? no
Issues Feature #60189
License MIT

✨ New Feature: #[IsSignatureValid] Attribute

This attribute enables declarative signature validation on controller actions. It ensures that requests using specific HTTP methods are validated using UriSigner. If the signature is invalid, a SignedUriException is thrown.

✅ Usage Examples

#[IsSignatureValid] // Applies to all HTTP methods by default
public function someAction(): Response
#[IsSignatureValid(methods: ['POST', 'PUT'])] // Only validates POST and PUT requests
public function updateAction(): Response

@santysisi santysisi requested a review from chalasr as a code owner May 9, 2025 21:24
@carsonbot carsonbot added this to the 7.3 milestone May 9, 2025
@santysisi santysisi force-pushed the feature/verify-signature-attribute branch 5 times, most recently from 4730169 to c72ef83 Compare May 9, 2025 22:27
@santysisi
Copy link
Contributor Author
santysisi commented May 9, 2025

Hi 👋
I'll update this version 7.3 to 7.4 once the new minor dev version is released.

Edit: I updated this version to 7.3 because I need this method from UriSigner.

@kbond
Copy link
Member
kbond commented May 12, 2025

This is looking great @santysisi! I'll give it a more thorough review after 7.3 is released.

@santysisi santysisi force-pushed the feature/verify-signature-attribute branch from c72ef83 to 85320cf Compare May 18, 2025 03:00
@santysisi
Copy link
Contributor Author

We could also consider adding an extra argument to the attribute called methods to restrict the validation to specific HTTP methods. This would align with the current behavior of the IsCsrfTokenValid attribute.

@fabpot fabpot modified the milestones: 7.3, 7.4 May 26, 2025
@santysisi santysisi force-pushed the feature/verify-signature-attribute branch from 85320cf to c000b69 Compare May 29, 2025 23:01
@santysisi
Copy link
Contributor Author

We could also consider adding an extra argument to the attribute called methods to restrict the validation to specific HTTP methods. This would align with the current behavior of the IsCsrfTokenValid attribute.

Hi 👋 I'll update this version 7.3 to 7.4 once the new minor dev version is released.

Edit: I updated this version to 7.3 because I need this method from UriSigner.

ready these 2 changes 🚀

Copy link
Member
@kbond kbond left a comment

Choose a reason for hiding this comment

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

This looks really great, nice work @santysisi!

@santysisi
Copy link
Contributor Author

I believe the error is not relevant 🤔
I only rebased the branch to resolve some conflicts, so there shouldn't be any functional changes introduced.

@santysisi santysisi force-pushed the feature/verify-signature-attribute branch 3 times, most recently from 5881d55 to ce5fea7 Compare June 7, 2025 04:57
@santysisi
Copy link
Contributor Author

Hi @nicolas-grekas 👋,

Thanks so much for all your suggestions. I really appreciate them ❤️
I believe everything is now addressed. Please let me know if there's anything else I should change!

Thanks again! ❤️

@santysisi santysisi force-pushed the feature/verify-signature-attribute branch 5 times, most recently from cb33c19 to da9f672 Compare June 27, 2025 12:44
/**
* @param int|null $statusCode The HTTP status code to return if the signature is invalid.
* Ignored when 'throw' is true. If null, error code 404 is used.
* @param bool|null $throw If true, an exception is thrown on signature failure instead of returning a response.
Copy link
Member

Choose a reason for hiding this comment

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

This is confusing IMO. With throw: false, it still throws an HttpException, so it still goes through the global error handling to return the error page.

what changes is whether it throws a SignedUriException or an HttpException.

What I would suggest instead is to always throw an HttpException, with the SignedUriException attached as previous exception. This simplifies the feature while still allowing advanced use cases by inspecting the previous exception.

Copy link
Member

Choose a reason for hiding this comment

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

Alternatively, always throw the SignedUriException and add the #[WithHttpStatus(404)] attribute on the SignedUriException, letting projects use the configuration in FrameworkBundle if they way to change the status code (assuming it is fine to have the same status code for all endpoints when signature verification fails)

Note that this option would allow having different status code for the various subclasses of SignedUriException to have 404 for a missing signature but 403 for an expired one for instance.

Copy link
Member

Choose a reason for hiding this comment

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

Alternatively, always throw the SignedUriException and add the #[WithHttpStatus(404)] attribute on the SignedUriException, letting projects use the configuration in FrameworkBundle if they way to change the status code (assuming it is fine to have the same status code for all endpoints when signature verification fails)

I like this option.

public function onKernelControllerArguments(ControllerArgumentsEvent $event): void
{
/** @var IsSignatureValid[] $attributes */
if (!\is_array($attributes = $event->getAttributes()[IsSignatureValid::class] ?? null)) {
Copy link
Member

Choose a reason for hiding this comment

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

use $event->getAttributes(IsSignatureValid::class) instead, which always returns an array and already has the proper IsSignatureValid[] type thanks to the generic type.

*/
public function __construct(
public ?int $statusCode = null,
public ?bool $throw = null,
Copy link
Member

Choose a reason for hiding this comment

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

there is no reason to make property nullable (as you make no difference between false and null)

* Useful for global exception handling or listener-based workflows.
*/
public function __construct(
public ?int $statusCode = null,
Copy link
Member

Choose a reason for hiding this comment

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

instead of making the property nullable with a constant in the listener for the default status, I suggest making it non-nullable with 404 as default value (to be discussed whether it should be a 403 instead)

/**
* @param int $validationFailedStatusCode The HTTP status code to return if the signature is invalid
* @param bool|null $throw If true, a {@see \Symfony\Component\HttpFoundation\Exception\SignedUriException} is thrown on signature failure instead of returning a response
* @param array|string $methods HTTP methods that require signature validation
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change F438
* @param array|string $methods HTTP methods that require signature validation
* @param string[]|string $methods HTTP methods that require signature validation. An empty array means that no method filtering is done.

* @param array|string $methods HTTP methods that require signature validation
*/
public function __construct(
public int $validationFailedStatusCode = Response::HTTP_NOT_FOUND,
Copy link
Member

Choose a reason for hiding this comment

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

those public properties should be readonly IMO, instead of being mutable externally.

public function __construct(
public int $validationFailedStatusCode = Response::HTTP_NOT_FOUND,
public ?bool $throw = null,
public array|string $methods = [],
Copy link
Member

Choose a reason for hiding this comment

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

I would suggest not using a promoted property here, so that the constructor argument can accept array|string but we normalize it to always store an array in the property.

@santysisi santysisi force-pushed the feature/verify-signature-attribute branch from da9f672 to 3811790 Compare June 28, 2025 17:43
@santysisi
Copy link
Contributor Author

Hi @stof , thanks for your suggestion!
I've made the changes accordingly, let me know if there's anything else you'd like me to adjust.

array|string $methods = [],
) {
if (!method_exists(UriSigner::class, 'verify')) {
throw new \LogicException('The method UriSigner::verify is required. Please upgrade symfony/http-foundation to 7.3 version or ensure it is correctly installed.');
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
throw new \LogicException('The method UriSigner::verify is required. Please upgrade symfony/http-foundation to 7.3 version or ensure it is correctly installed.');
throw new \LogicException('The `IsSignatureValid` attribute requires symfony/http-foundation >= 7.3.');

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi 👋: , thanks for your suggestion ❤️
I've made the changes accordingly, let me know if there's anything else you'd like me to adjust.

@santysisi santysisi force-pushed the feature/verify-signature-attribute branch from 3811790 to 05d2de6 Compare June 29, 2025 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants
0