On the “Webhook Creating Endpoints Part 2” video on YouTube, a proper way to authenticate users is mentioned but not described. What is the proper way to secure Webhooks on n8n?
Ricardo,
there are other Auth Methods in Pipeline ? OAuth ? Or do we need to implement this via Proxy ?
Thanks,
Stefan
We have many more authentication method in the HTTP node. What do you want to do exactly?
Been trying to Basic authenticate a POST webhook in test-mode from self-hosted Seatable.
Wasn’t able to find an example on either the documentation or the forum, though I’m probably not the first doing this.
I thought this would be enough:
const username = "n8n-basic-auth-USER";
const password = "n8n-basic-auth-PASSWORD";
let data = {
row_id: base.context.currentRow['_id']
};
await fetch(url, {
method: 'POST',
credentials: 'include',
mode: "no-cors",
headers: {
'Content-Type': 'application/json',
'Authorization': "Basic " + btoa(`${username}:${password}`)
},
body: JSON.stringify(data)
}).then(res => {
console.log('Request complete! response:', res);
});
I get a 403 Error and been looking at specifications for a while now, trying different things.
It’s probably an easy fix, one suspicion I have is that the request from Seatable Javascript script originates from seatable.mydomain.com and goes to n8n.mydomain.com and gets rejected?
In n8n I have set my Basic Auth credentials with User = ‘n8n-basic-auth-USER’ and Password = ‘n8n-basic-auth-PASSWORD’ (in reality a 44-character string with uppercase, lowercase, digits).
Without authentication it works fine, so I guess I’m fucking up somewhere essential and can’t see it right now : D Any pointers would be much appreciated!
Information on your n8n setup
- n8n version: 0.193.5
- Database you’re using (default: SQLite): Postgres
- Running n8n with the execution process [own(default), main]: main
-
Running n8n via [Docker, npm, n8n.cloud, desktop app]: Docker
Using traefik as a reverse proxy.
Not using user management, but Basic Auth for logging into my n8n.
Hi Ricardo, That a Webhook Endpoint in N8n can use Auth Method OAuth2. What do you think ?
Regards,
Stefan
Hey folks — jumping in with two parts: a quick clarification on OAuth2/JWT for webhooks and a ready-made option if you need stronger security patterns in n8n.
1) “OAuth on a webhook?”
Webhooks typically don’t perform an OAuth2 handshake themselves. Instead, the sender (client) obtains an OAuth2 access token and calls your webhook with
Authorization: Bearer <token>.
On the webhook side you verify the token — ideally using JWKS from your IdP (Auth0/Okta/OIDC). That’s the standard “JWT (JWKS)” verification approach.
2) Basic Auth + browser request (Seatable) → 403
If you’re calling your webhook from code that runs in the browser on seatable.mydomain.com to n8n.mydomain.com, you’ll hit CORS:
-
Remove
mode: "no-cors"(that makes the request opaque and hides errors). -
Make sure your proxy/n8n answers preflight with the right headers, e.g.:
-
Access-Control-Allow-Origin: https://seatable.mydomain.com -
Access-Control-Allow-Headers: content-type, authorization -
Access-Control-Allow-Methods: POST, OPTIONS
-
-
Sanity-check from a server (no CORS) to confirm credentials are fine:
curl -i -X POST 'https://n8n.mydomain.com/webhook/<path>' \ -H 'content-type: application/json' \ -u 'n8n-basic-auth-USER:n8n-basic-auth-PASSWORD' \ --data '{"ok":true}'
If curl works but the browser doesn’t, it’s a pure CORS/proxy config issue (not auth).
A hardened webhook trigger for n8n (open source)
We built a community node that adds defense-in-depth for public endpoints:
-
Auth profiles: HMAC (with timestamp+nonce), JWT (JWKS), API Key, or Combo (HMAC + IP allow/deny)
-
Replay protection (timestamp + nonce)
-
Rate limiting per IP (memory or Redis)
-
Content-Type allowlist & body size caps
-
mTLS proof via proxy headers (e.g.,
x-ssl-client-verify: SUCCESS) -
Audit export (metadata-only) and dual-key rotation for HMAC
Install (Community Nodes): @prokodo/n8n-nodes-secure-webhook
Then pick one of the variants: Secure Webhook (HMAC), (JWT/JWKS), (API Key), or (Combo).
JWT/JWKS (for OAuth2 access tokens)
-
Set your IdP JWKS URL, and (optionally)
iss/aud. -
Clients call with
Authorization: Bearer <jwt>— the node verifies via JWKS.
HMAC (extended mode) client example
import crypto from 'node:crypto';
const url = 'https://n8n.example/webhook/secure';
const body = JSON.stringify({ hello: 'world' });
const ts = Math.floor(Date.now()/1000).toString();
const nonce = crypto.randomUUID();
const algo = 'sha256';
const bodyHash = crypto.createHash(algo).update(Buffer.from(body)).digest('hex');
const base = ['POST', '/webhook/secure', '', ts, nonce, bodyHash].join('\n');
const sig = crypto.createHmac(algo, process.env.WEBHOOK_SECRET).update(base).digest('hex');
await fetch(url, {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-timestamp': ts,
'x-nonce': nonce,
'x-signature': `${algo}=${sig}`,
},
body,
});
Happy to share a minimal Traefik/Nginx CORS snippet if helpful. If you need JWT verification for OAuth2 or stronger webhook hardening, this node should cover it.
