Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

vincent15000's avatar

How to protect a webhook handler route ?

Hello,

I'm using wehbooks in an application : the Laravel app receives event (webhook) from an external application and the webhook is handled via a POST route.

I'd like to protect this route.

The external application gives a unique possibility : add an HTTP user and an HTTP password.

What does this mean ? Is it OAuth ? (I'm not so familiar with OAuth)

What do I have to do in the Laravel application so that the route is protected ?

Thanks for your help.

V

0 likes
19 replies
LaryAI's avatar

The external application's option to "add an HTTP user and an HTTP password" refers to HTTP Basic Authentication, not OAuth. This is a simple authentication scheme built into the HTTP protocol, where the client (the external app) sends a username and password with each request.

To protect your Laravel webhook route using HTTP Basic Auth, you can use Laravel's built-in middleware.

Step 1: Add the middleware to your route

Suppose your webhook route looks like this:

Route::post('/webhook-handler', [WebhookController::class, 'handle']);

You can protect it with HTTP Basic Auth like so:

Route::post('/webhook-handler', [WebhookController::class, 'handle'])
    ->middleware('auth.basic');

Step 2: Configure the credentials

Laravel's auth.basic middleware uses your users table by default. But for webhooks, you might want to use a dedicated username and password, not tied to your users table.

You can create a custom middleware for this. Here’s how:

Create a middleware:

php artisan make:middleware WebhookAuth

Edit app/Http/Middleware/WebhookAuth.php:

namespace App\Http\Middleware;

use Closure;

class WebhookAuth
{
    public function handle($request, Closure $next)
    {
        $USERNAME = env('WEBHOOK_USER', 'your_username');
        $PASSWORD = env('WEBHOOK_PASSWORD', 'your_password');

        if (
            $request->getUser() !== $USERNAME ||
            $request->getPassword() !== $PASSWORD
        ) {
            return response('Unauthorized', 401, ['WWW-Authenticate' => 'Basic']);
        }

        return $next($request);
    }
}

Register the middleware in app/Http/Kernel.php:

protected $routeMiddleware = [
    // ...
    'webhook.auth' => \App\Http\Middleware\WebhookAuth::class,
];

Protect your route:

Route::post('/webhook-handler', [WebhookController::class, 'handle'])
    ->middleware('webhook.auth');

Step 3: Set the credentials in your .env file:

WEBHOOK_USER=your_username
WEBHOOK_PASSWORD=your_password

Summary:

  • This is HTTP Basic Auth, not OAuth.
  • Use Laravel's built-in auth.basic or create a custom middleware for static credentials.
  • Set the same username and password in both your Laravel app and the external application.

Let me know if you need help with any step!

Snapey's avatar

Sanctum is ideal for this, and already has the middleware.

From your app, create an api user, and generate a personal token. Send this token with the webhook as a bearer token.

command

use sanctum in your api.php routes file

Route::middleware(['auth:sanctum'])->group(function () {

    Route::post('/webhook',  WebhookHandler::class);

});

or by whatever method you like to use middleware

in your webhook post send Authorization header with value of 'Bearer xxxxyyyyzzzz'

Sanctum will validate your token on each webhook call

2 likes
vincent15000's avatar

@Snapey It seems exactly what I need, but the problem is that Pipedrive (the external application sending data via its webhooks) doesn't send any Bearer token ... the only authentication method is basic http auth. And the user and the password appear in plaintext in the headers.

https://pipedrive.readme.io/docs/guide-for-webhooks

Or perhaps I don't understand what you suggest me : in your suggestion, the token is also sent in the headers. Anybody could intercept the request, take the Bearer token and send malicious data via a new custom webhook.

For the moment, I just have generated a random string with 100 characters for the user and another one for the password and all users will have the same user / password pair designed for the Laravel application. The data coming from the Pipedrive webhooks are identified by a company_id value so that I can handle the data for the right company in the Laravel application.

Is there any way to exploit this basic http auth to have something more secure ?

vincent15000's avatar

@Snapey As Pipedrive can't send a Bearer token with their current implementation, would it be a good idea to use a signed route and replace the Sanctum token with a signature ?

Snapey's avatar

@vincent15000 see if they sign the payload. Then you check the payload for tampering. If someone was able to execute man in the middle, they could only send exactly the same data as came from pipedrive in the first place

OussamaMater's avatar

I'm not sure I fully understand what you mean by:

add an HTTP use

But the de facto standard is using webhook secrets, like in the case of GitHub, Stripe, Twilio, and actually most (if not all) services I used.

When you register your webhook URL with the service, you are usually allowed to either input a secret or have one generated for you. Then, whenever an event occurs, the service sends a request to your endpoint that includes the payload and a signature (usually an HMAC-SHA256 hash, but check their docs).

You use the shared secret to recompute the signature and verify that it matches the one you got.

That's how I would go about it.

I think you should also take a look at Spatie's package for inspiration, they basically implement what I just mentioned:

1 like
OussamaMater's avatar

@vincent15000 Thanks for sharing the docs. It looks like they're using Basic Auth. In this case, you can simply create a middleware to authenticate their requests.

Assuming they implement Basic Auth correctly (since the exact payload is not mentioned) you should expect to get a Base64-encoded string representing <user>:<password>, which you define when setting up the webhook as a bearer token.

For example:

  • Username: user
  • Password: password

They will concatenate these into user:password, encode it in Base64 to get dXNlcjpwYXNzd29yZA==, and include it in the request header like this Authorization: Basic dXNlcjpwYXNzd29yZA==

You can then validate this in your middleware.

1 like
vincent15000's avatar

@OussamaMater The problem is that they don't implement this correctly : the user and the password appear in the request headers in plaintext.

Well ... exactly like when a user logs in in a web application, the credentials are sent in paintext via HTTPS.

I don't think being able to do another way, because Pipedrive doesn't proceed another way.

But I think that the problem remains exactly the same even if the user and the password are sent encoded in base64. Any interception of the request could capture the base64 encoded string and do whatever he/her wants with.

vincent15000's avatar

@OussamaMater

In the Laravel application, the webhooks are handled according to the company id present in the payload.

So I have create a unique user / password pair for the Laravel application (and not customized ones for each company, what I don't have done, but is there a really need to do that ?).

Perhaps there is something to do with a signed route and custom user / password for each company ? The signature could have the company id, the user and the password encrypted.

This means : the route is signed with some custom informations, pipedrive sends the data via the webhook with user / password in plaintext and the Laravel application compares these informations (user / password / company_id with the signature.

But won't the problem be the same ?

DhPandya's avatar

@vincent15000 Check this out. PipeDrive will send those Username and password in the header with every webhook request. So in the Laravel you will have to prepare some logic that validates those credentials. If it validated successfully then the webhook is triggered from the Pipedrive else return 403.

Hope this works for you. :)

1 like
vincent15000's avatar

@DhPandya I already know this, but I wonder if it's secure enough.

I'm just looking for what to do with this pipedrive implementation so that the webhook handler is secure enough in the Laravel application.

OussamaMater's avatar
Level 37

@vincent15000 well it's not exactly secure, but using SSL does solve the plaintext issue. So even if a request is intercepted, there's not much to do.

You do this every day when logging into any sevrice, you send your email and password. The only difference is that those credentials are sent in the headers rather than in the body.

And based on their documentation, its the only option they provide.

I don't get the signature part you mean tbh

1 like
vincent15000's avatar

@OussamaMater The signature ? I just meant that I could create a signed route. The signature could contain some informations that the Laravel application will check. But does it really solve the problem ?

OussamaMater's avatar

@vincent15000 I'm not sure how you are planning to solve this with signed routes tbh. The service would need to include a signature param when hitting your webhook — which they won't.

Also, just to reiterate, you will be using basic auth over HTTPS. It's what we all do in similar way on a daily basis, just like I logged in to Laracasts a few minutes ago. The only difference is that my username and password were sent in the body instead of the header.

Please or to participate in this conversation.