Skip to content

Commit 7dcd739

Browse files
authored
Create an auth webhooks reference section (#8548)
I went to start adding more detail to the webhook section and realized that there is a lot to say about webhooks, so I made a separate section, and linked to it.
1 parent b7fcc8a commit 7dcd739

File tree

2 files changed

+261
-101
lines changed

2 files changed

+261
-101
lines changed

docs/reference/auth/index.rst

Lines changed: 7 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Auth
1313
oauth
1414
magic_link
1515
webauthn
16+
webhooks
1617

1718
:edb-alt-title: Using Gel Auth
1819

@@ -81,7 +82,7 @@ auth_signing_key
8182

8283
The extension uses JSON Web Tokens (JWTs) internally for many operations.
8384
``auth_signing_key`` is the value that is used as a symmetric key for signing
84-
the JWTs. At the moment, the JWTs are not considered public API, so there is
85+
the JWTs. At the moment, the JWTs are not considered "public" API, so there is
8586
no need to save this value for your own application use. It is exposed mainly
8687
to allow rotation.
8788

@@ -100,7 +101,7 @@ token_time_to_live
100101
------------------
101102

102103
This value controls the expiration time on the authentication token's
103-
JSON Web Token. This is effectively the session time.
104+
JSON Web Token. This is effectively the "session" time.
104105

105106
To configure via query or script:
106107

@@ -162,107 +163,12 @@ To configure via query or script:
162163
};
163164
164165
165-
Configuring webhooks
166-
====================
166+
Webhooks
167+
========
167168

168-
The auth extension supports sending webhooks for a variety of auth events. You
169-
can use these webhooks to, for instance, send a fully customized email for
170-
email verification, or password reset instead of our built-in email
171-
verification and password reset emails. You could also use them to trigger
172-
analytics events, start an email drip campaign, create an audit log, or
173-
trigger other side effects in your application.
169+
The auth extension supports sending webhooks for a variety of auth events. You can use these webhooks to, for instance, send a fully customized email for email verification, or password reset instead of our built-in email verification and password reset emails. You could also use them to trigger analytics events, start an email drip campaign, create an audit log, or trigger other side effects in your application.
174170

175-
.. note::
176-
177-
We send webhooks with no durability or reliability guarantees, so you should
178-
always provide a mechanism for retrying delivery of any critical events,
179-
such as email verification and password reset. We detail how to resend these
180-
events in the relevant sections on the various authentication flows.
181-
182-
183-
Here are the webhooks that are currently supported:
184-
185-
.. list-table::
186-
:header-rows: 1
187-
:widths: 30 70
188-
189-
* - Event
190-
- Payload
191-
* - ``IdentityCreated``
192-
- | - ``event_type``: ``"IdentityCreated"`` (``str``)
193-
| - ``event_id``: Unique event identifier (``str``)
194-
| - ``timestamp``: ISO 8601 timestamp (``datetime``)
195-
| - ``identity_id``: ID of created identity (``str``)
196-
* - ``IdentityAuthenticated``
197-
- | - ``event_type``: ``"IdentityAuthenticated"`` (``str``)
198-
| - ``event_id``: Unique event identifier (``str``)
199-
| - ``timestamp``: ISO 8601 timestamp (``datetime``)
200-
| - ``identity_id``: ID of authenticated identity (``str``)
201-
* - ``EmailFactorCreated``
202-
- | - ``event_type``: ``"EmailFactorCreated"`` (``str``)
203-
| - ``event_id``: Unique event identifier (``str``)
204-
| - ``timestamp``: ISO 8601 timestamp (``datetime``)
205-
| - ``identity_id``: Associated identity ID (``str``)
206-
| - ``email_factor_id``: ID of created email factor (``str``)
207-
* - ``EmailVerified``
208-
- | - ``event_type``: ``"EmailVerified"`` (``str``)
209-
| - ``event_id``: Unique event identifier (``str``)
210-
| - ``timestamp``: ISO 8601 timestamp (``datetime``)
211-
| - ``identity_id``: Associated identity ID (``str``)
212-
| - ``email_factor_id``: ID of verified email factor (``str``)
213-
* - ``EmailVerificationRequested``
214-
- | - ``event_type``: ``"EmailVerificationRequested"`` (``str``)
215-
| - ``event_id``: Unique event identifier (``str``)
216-
| - ``timestamp``: ISO 8601 timestamp (``datetime``)
217-
| - ``identity_id``: Associated identity ID (``str``)
218-
| - ``email_factor_id``: ID of email factor to verify (``str``)
219-
| - ``verification_token``: Token for verification (``str``)
220-
* - ``PasswordResetRequested``
221-
- | - ``event_type``: ``"PasswordResetRequested"`` (``str``)
222-
| - ``event_id``: Unique event identifier (``str``)
223-
| - ``timestamp``: ISO 8601 timestamp (``datetime``)
224-
| - ``identity_id``: Associated identity ID (``str``)
225-
| - ``email_factor_id``: ID of email factor (``str``)
226-
| - ``reset_token``: Token for password reset (``str``)
227-
* - ``MagicLinkRequested``
228-
- | - ``event_type``: ``"MagicLinkRequested"`` (``str``)
229-
| - ``event_id``: Unique event identifier (``str``)
230-
| - ``timestamp``: ISO 8601 timestamp (``datetime``)
231-
| - ``identity_id``: Associated identity ID (``str``)
232-
| - ``email_factor_id``: ID of email factor (``str``)
233-
| - ``magic_link_token``: Token for magic link (``str``)
234-
| - ``magic_link_url``: Complete URL for magic link (``str``)
235-
236-
You can configure webhooks with the UI or via query.
237-
238-
.. code-block:: edgeql
239-
240-
CONFIGURE CURRENT BRANCH INSERT
241-
ext::auth::WebhookConfig {
242-
url := 'https://example.com/auth/webhook',
243-
events := {
244-
ext::auth::WebhookEvent.EmailVerificationRequested,
245-
ext::auth::WebhookEvent.PasswordResetRequested,
246-
}
247-
};
248-
249-
.. note::
250-
251-
URLs must be unique across all webhooks configured for each branch. If you want
252-
to send multiple events to the same URL, you can do so by adding multiple
253-
``ext::auth::WebhookEvent`` values to the ``events`` set.
254-
255-
Troubleshooting webhooks
256-
------------------------
257-
258-
If you are having trouble receiving webhooks, you might need to look for any responses from the requests that are being scheduled by the :ref:`std::net::http <ref_std_net>` module. You can list all of the :eql:type:`net::http::ScheduledRequest` objects, and any returned responses with the following query:
259-
260-
.. code-block:: edgeql
261-
262-
select net::http::ScheduledRequest {
263-
**,
264-
response: { ** }
265-
}
171+
See the :ref:`webhooks documentation <ref_auth_webhooks>` for more details on how to configure and use webhooks.
266172

267173
Configuring SMTP
268174
================

docs/reference/auth/webhooks.rst

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
.. _ref_auth_webhooks:
2+
3+
========
4+
Webhooks
5+
========
6+
7+
The auth extension supports sending webhooks for a variety of auth events. You can use these webhooks to, for instance, send a fully customized email for email verification, or password reset instead of our built-in email verification and password reset emails. You could also use them to trigger analytics events, start an email drip campaign, create an audit log, or trigger other side effects in your application.
8+
9+
If you are using Webhooks to send emails, be sure to not also configure an SMTP provider otherwise we will send the email via SMTP and also send the webhook which will trigger your custom email sending behavior.
10+
11+
.. warning::
12+
13+
We send webhooks with no durability or reliability guarantees, so you should always provide a mechanism for retrying delivery of any critical events, such as email verification and password reset. We detail how to resend these events in the relevant sections on the various authentication flows.
14+
15+
Configuration
16+
=============
17+
18+
You can configure webhooks with the UI or via query. The URLs you register as webhooks must be unique across all webhooks configured for each branch. If you want to send multiple events to the same URL, you can do so by adding multiple ``ext::auth::WebhookEvent`` values to the ``events`` set, like in this example.
19+
20+
.. code-block:: edgeql
21+
22+
configure current branch insert
23+
ext::auth::WebhookConfig {
24+
url := 'https://example.com/auth/webhook',
25+
events := {
26+
ext::auth::WebhookEvent.EmailVerificationRequested,
27+
ext::auth::WebhookEvent.PasswordResetRequested,
28+
},
29+
# Optional, only needed if you want to verify the webhook request
30+
signing_secret_key := '1234567890',
31+
};
32+
33+
When you receive a webhook, you'll look at the ``event_type`` field to determine which event corresponds to this webhook request and handle it accordingly.
34+
35+
Checking webhook signatures
36+
===========================
37+
38+
You can provide a signing key, which you will need to generate and save in a place that your application will have access to. The extension will then add a ``x-ext-auth-signature-sha256`` header to the request, which you can use to verify the request by comparing the signature to the SHA256 hash of the request body.
39+
40+
Here is an example of how you might verify the signature in a Node.js application:
41+
42+
.. code-block:: typescript
43+
44+
/**
45+
* Assert that if the request contains a signature header, that the signature
46+
* is valid for the request body. Will return false if there is no signature
47+
* header.
48+
*
49+
* @param {Request} request - The request to verify.
50+
* @param {string} signingKey - The key to use to verify the signature.
51+
* @returns {boolean} - True if the signature is present and valid, false if
52+
* the signature is not present at all.
53+
* @throws {AssertionError} - If the signature is present but invalid.
54+
*/
55+
async function assertSignature(
56+
request: Request,
57+
signingKey: string,
58+
): Promise<boolean> {
59+
const signatureHeader = request.headers.get('x-ext-auth-signature-sha256');
60+
if (!signatureHeader) {
61+
return false;
62+
}
63+
64+
const requestBody = await request.text();
65+
const encoder = new TextEncoder();
66+
const data = encoder.encode(requestBody);
67+
const key = await crypto.subtle.importKey(
68+
'raw',
69+
encoder.encode(signingKey),
70+
{ name: 'HMAC', hash: 'SHA-256' },
71+
false,
72+
['sign']
73+
);
74+
const signature = await crypto.subtle.sign('HMAC', key, data);
75+
const signatureHex = Buffer.from(signature).toString('hex');
76+
77+
assert.strictEqual(
78+
signatureHeader,
79+
signatureHex,
80+
"Signature header is set, but the signature is invalid"
81+
);
82+
83+
return true;
84+
};
85+
86+
87+
Troubleshooting webhooks
88+
========================
89+
90+
If you are having trouble receiving webhooks, you might need to look for any responses from the requests that are being scheduled by the :ref:`std::net::http <ref_std_net>` module. You can list all of the :eql:type:`net::http::ScheduledRequest` objects, and any returned responses with the following query:
91+
92+
.. code-block:: edgeql
93+
94+
select net::http::ScheduledRequest {
95+
**,
96+
response: { ** }
97+
}
98+
99+
Events reference
100+
================
101+
102+
Common fields for all events:
103+
104+
* ``event_type``: (string) This will be a literal string containing the name of the event. You can use this to determine which event occurred.
105+
* ``event_id``: (string) A unique identifier to help disambiguate events of the same type.
106+
* ``timestamp``: (string) The ISO 8601 timestamp of when the event was triggered.
107+
108+
109+
Identity created
110+
^^^^^^^^^^^^^^^^
111+
112+
When a new ``ext::auth::Identity`` object is created, like when a new user signs up, or an existing user adds a new factor, this event is triggered.
113+
114+
**Example payload:**
115+
116+
.. code-block:: text
117+
118+
POST http://localhost:8000/auth/webhook
119+
Content-type: application/json
120+
x-ext-auth-signature-sha256: 1234567890
121+
122+
{
123+
"event_type": "IdentityCreated",
124+
"event_id": "1234567890",
125+
"timestamp": "2021-01-01T00:00:00Z",
126+
"identity_id": "identity123"
127+
}
128+
129+
Identity authenticated
130+
^^^^^^^^^^^^^^^^^^^^^^
131+
132+
When an ``ext::auth::Identity`` object is authenticated, like when a user logs in, this event is triggered.
133+
134+
**Example payload:**
135+
136+
.. code-block:: text
137+
138+
POST http://localhost:8000/auth/webhook
139+
Content-type: application/json
140+
x-ext-auth-signature-sha256: 1234567890
141+
142+
{
143+
"event_type": "IdentityAuthenticated",
144+
"event_id": "1234567890",
145+
"timestamp": "2021-01-01T00:00:00Z",
146+
"identity_id": "identity123"
147+
}
148+
149+
Email factor created
150+
^^^^^^^^^^^^^^^^^^^^
151+
152+
When a new ``ext::auth::EmailFactor`` object is created, like when a user adds a new email factor, this event is triggered.
153+
154+
**Example payload:**
155+
156+
.. code-block:: text
157+
158+
POST http://localhost:8000/auth/webhook
159+
Content-type: application/json
160+
x-ext-auth-signature-sha256: 1234567890
161+
162+
{
163+
"event_type": "EmailFactorCreated",
164+
"event_id": "1234567890",
165+
"timestamp": "2021-01-01T00:00:00Z",
166+
"identity_id": "identity123",
167+
"email_factor_id": "emailfactor123"
168+
}
169+
170+
Email verified
171+
^^^^^^^^^^^^^^
172+
173+
When a user verifies their email address, this event is triggered.
174+
175+
**Example payload:**
176+
177+
.. code-block:: text
178+
179+
POST http://localhost:8000/auth/webhook
180+
Content-type: application/json
181+
x-ext-auth-signature-sha256: 1234567890
182+
183+
{
184+
"event_type": "EmailVerified",
185+
"event_id": "1234567890",
186+
"timestamp": "2021-01-01T00:00:00Z",
187+
"identity_id": "identity123",
188+
"email_factor_id": "emailfactor123"
189+
}
190+
191+
Email verification requested
192+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
193+
194+
When a user requests to verify their email address, like when they first sign up, or requests to resend the verification email, this event is triggered.
195+
196+
**Example payload:**
197+
198+
.. code-block:: text
199+
200+
POST http://localhost:8000/auth/webhook
201+
Content-type: application/json
202+
x-ext-auth-signature-sha256: 1234567890
203+
204+
{
205+
"event_type": "EmailVerificationRequested",
206+
"event_id": "1234567890",
207+
"timestamp": "2021-01-01T00:00:00Z",
208+
"identity_id": "identity123",
209+
"verification_token": "verificationtoken123"
210+
}
211+
212+
Password reset requested
213+
^^^^^^^^^^^^^^^^^^^^^^^^
214+
215+
When a user requests to reset their password, this event is triggered.
216+
217+
**Example payload:**
218+
219+
.. code-block:: text
220+
221+
POST http://localhost:8000/auth/webhook
222+
Content-type: application/json
223+
x-ext-auth-signature-sha256: 1234567890
224+
225+
{
226+
"event_type": "PasswordResetRequested",
227+
"event_id": "1234567890",
228+
"timestamp": "2021-01-01T00:00:00Z",
229+
"identity_id": "identity123",
230+
"reset_token": "resettoken123"
231+
}
232+
233+
Magic link requested
234+
^^^^^^^^^^^^^^^^^^^^
235+
236+
When a user requests to send a magic link email, like for signing in, or signing up for the first time, this event is triggered.
237+
238+
**Example payload:**
239+
240+
.. code-block:: text
241+
242+
POST http://localhost:8000/auth/webhook
243+
Content-type: application/json
244+
x-ext-auth-signature-sha256: 1234567890
245+
246+
{
247+
"event_type": "MagicLinkRequested",
248+
"event_id": "1234567890",
249+
"timestamp": "2021-01-01T00:00:00Z",
250+
"identity_id": "identity123",
251+
"email_factor_id": "emailfactor123",
252+
"magic_link_token": "magiclinktoken123",
253+
"magic_link_url": "http://localhost:8000/auth/magic-link?token=magiclinktoken123"
254+
}

0 commit comments

Comments
 (0)