Introduction
I recently made a pull request that got merged into the Laravel framework and was released in v8.55. It added a new assertRedirectToSignedRoute()
method that you can use in your tests.
I've been using this method in a few of my projects for quite a while by adding it using macros, but I decided to make a pull request to the framework to try and help other developers who could probably make use of it too.
By the way, if you're interested in learning about macros, check out my How to Swap Items in an Array Using Laravel Macros article.
What Are Signed Routes?
Before we look at testing the signed URLs, let's take a quick look at what they are and what they can be used for.
Signed URLs are just URLs to named routes in your application that contain a signature in the query string.
Here's an example of a signed URL:
https://my-website.co.uk/protected?signature=41cacd19b33b5d29d46876762e6b0091e236a8c2ab59a656df88a31b500471e1
As you can see, the URL contains a signature
parameter.
Now, if we were to try and go to this route, we could use the signed
middleware to verify that the URL hadn't been tampered with since it was created. This can be really useful when we want to create a route that should be publicly accessible but that needs a bit of protection. If the URL has changed in any way, when someone navigates to it, they'll receive a 403 error response.
You can generate the signed URLs using code such as this:
1use Illuminate\Support\Facades\URL;2 3return URL::signedRoute('protected-route', ['user' => 1]);
If you've subscribed to my newsletter and have eagle eyes, you might have actually noticed that I use signed routes in the 'Confirm Subscription' email. The signed route looks like this:
https://ashallendesign.co.uk/subscribe/confirm?email=example%40example.com&signature=f6def8b181b2ff43629994bda724f6ada19b3677bbaf09bc656ecde7ba1144c3
When someone goes to this route, it confirms that they want to subscribe to the newsletter. By signing this URL, I can have confidence that it hasn't been tampered with and that someone isn't trying to flood my newsletter and sign up lots of random email addresses.
What Are Temporary Signed URLs?
The signed URLs discussed above don't have an expiry date and generally should be accessible indefinitely. However, there might be times that you want to make a signed URL that can be accessed for a specific amount of time. These work in the same way as the generic signed URLs but they also have an expiry time in the query string.
Here's an example of a temporary signed URL:
https://my-website.co.uk/protected?expires=1628978674&signature=ec582ea358f0d357edd91fd456a01b0e0b0a3bc62fb460a2853a82c921cd6f54
As you can see, the URL contains signature
and expires
parameters.
In a similar way to the generic signed URLs, if the URL is tampered with or if someone tries to navigate to it after the expiration time, a 403 error response will be returned.
Testing Redirects to Signed Routes
Now that we've briefly covered signed routes in Laravel, we can look at how to write a test to assert that a controller redirects to a signed URL. Please note that the tests below are going to be very basic and we're only going to be testing the redirect; but, hopefully, they should help to explain the overall concept.
Let's imagine that we have the following single-use controller that has the route /my-route
:
app/Http/Controllers/RandomController.php
1class RandomController extends Controller2{3 public function __invoke(): RedirectResponse4 {5 // Do something...67 return redirect()->signedRoute('example.route', ['param' => 'hello']);8 }9}
If we wanted to test that the controller returned a redirect to a signed route but didn't particularly care what the URL was, we could write a test like so:
1/** @test */2public function user_is_redirected_to_signed_route(): void3{4 $this->get('/my-route')5 ->assertRedirectToSignedRoute();6}
Alternatively, if we wanted to be stricter and check that the redirect was to a specific route, we could write the following test:
1/** @test */2public function user_is_redirected_to_signed_route(): void3{4 $this->get('/my-route')5 ->assertRedirectToSignedRoute(route('example.route', ['param' => 'hello']));6}
The assertRedirectToSignedRoute()
method also works with temporary signed routes. So, if our controller returned redirect()->temporarySignedRoute()
rather than redirect()->signedRoute()
, the tests above would still work.
Conclusion
Although this post is a bit shorter than usual, hopefully it should have given you a brief overview of signed URLs in Laravel and how to write tests that your controllers redirect to them correctly.
If this post helped you out, I'd love to hear about it. Likewise, if you have any feedback to improve this post, I'd also love to hear that too.
If you're interested in getting updated each time I publish a new post, feel free to sign up for my newsletter below.
Keep on building awesome stuff! 🚀