-
Notifications
You must be signed in to change notification settings - Fork 93
/
webhooks.html
198 lines (185 loc) · 7.32 KB
/
webhooks.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Webhooks</title>
<link rel="stylesheet" type="text/css" href="style/base.css"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" rel="stylesheet"/>
</head>
<body>
<h1 id="webhooks">Webhooks</h1>
<section>
<p>
Webhooks allow you to subscribe to certain events happening at W3C. When one of these events is triggered, we'll
send a HTTP POST payload to the webhook's configured URL.
</p>
<p>
You can configure your webhooks from <a href="https://www.w3.org/users/myprofile/webhooks">your profile</a>.
</p>
</section>
<section id="events">
<h2>Events</h2>
<p>
When configuring a webhook, you can choose which events you would like to receive payloads for. You can change
the subscribed events through the UI anytime.
</p>
<p>
Each event corresponds to a certain set of actions that can happen. For
example, if you subscribe to the publication event you'll receive detailed payloads every time a technical
report
is published.
</p>
<p>The available events are:</p>
<table>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
<tr>
<td>Publication (tr.published, tr.updated)</td>
<td>Any time a technical report is published or its metadata are updated</td>
</tr>
<tr>
<td>Group Participation (group.participant_joined, group.participant_left)</td>
<td>Any time someone joins or leaves a group</td>
</tr>
<tr>
<td>Connected Accounts (connected_account.created, connected_account.deleted)</td>
<td>Any time someone connects their W3C account to an external service such as GitHub, or removes such a
connection
</td>
</tr>
</table>
<p>This list of events will be progressively augmented.</p>
</section>
<section id="headers-payloads">
<h2>Headers and Payloads</h2>
<p>HTTP requests made to your webhook's configured URL endpoint will contain several special headers:</p>
<table>
<tr>
<th>Header</th>
<th>Description</th>
</tr>
<tr>
<td>X-W3C-Webhook</td>
<td>Link to the webhook that triggered the delivery</td>
</tr>
<tr>
<td>X-W3C-Webhook-Id</td>
<td>ID of the webhook that triggered the delivery</td>
</tr>
<tr>
<td>X-W3C-Webhook-Signature-256</td>
<td>
Signature of the payload, if a secret token was set.
<a href="#securing">See below for more information</a>
</td>
</tr>
<tr>
<td>X-W3C-Webhook-Signature-256-Base64</td>
<td>
Signature of the payload encoded in base64, if a secret token was set.
<a href="#securing">See below for more information</a>
</td>
</tr>
</table>
<p>
Payload of HTTP requests consists in a JSON fragment including the event that triggered the webhook, the exact
time when the request was made,
and one or several objects serialized in the same way as the <a href="https://api.w3.org/doc">API</a>.
</p>
<h3>Example delivery</h3>
<pre><code class="language-http">URL: http://example.org/my-webhook
Host: example.org
X-W3C-Webhook: https://www.w3.org/users/12345/webhooks/1
X-W3C-Webhook-Id: 1</code></pre>
<pre><code class="language-json">{
"event": "tr.published",
"microtime": 1661495980.622211,
"specVersion": {
"status": "Working Draft",
"uri": "http://www.w3.org/TR/2016/WD-preload-20160120/",
"date": "2016-01-20",
"informative": false,
"title": "Preload",
"shortlink": "http://www.w3.org/TR/preload/",
"editor_draft": "http://w3c.github.io/preload/",
"process_rules": "http://www.w3.org/2015/Process-20150901/",
"_links": {
"self": {
"href": "https://api.w3.org/specifications/preload/versions/20160120"
},
"editors": {
"href": "https://api.w3.org/specifications/preload/versions/20160120/editors"
},
"deliverers": {
"href": "https://api.w3.org/specifications/preload/versions/20160120/deliverers"
},
"specification": {
"href": "https://api.w3.org/specifications/preload"
},
"predecessor-version": {
"href": "https://api.w3.org/specifications/preload/versions/20160120/predecessors"
}
}
}
}</code></pre>
</section>
<section id="securing">
<h2>Securing your webhooks</h2>
<p>
For security reasons, you probably want to limit requests to those coming from W3C. To do so you can set up a
secret token and validate the information received.
</p>
<h3>Setting your secret token</h3>
<p>
When creating or editing a webhook, you can fill out the Secret textbox. You should use a random key with high
entropy, for example by taking the output of <code>LC_ALL=C tr -dc '[:alnum:]' < /dev/urandom | head -c20</code>
from the terminal (on a GNU/Linux system).
</p>
<p>
Beware that once set, you won't be able to see this secret anymore. If you forget it, you will have to change
it,
which can be done when editing a webhook.
</p>
<h3>Validating payloads</h3>
<p>
When your secret token is set, W3C uses it to create a hash signature with each payload. This hexadecimal
hash signature is included with the headers of each request as <code>X-W3C-Webhook-Signature-256</code>.
We also send a Base 64 version of the signature as <code>X-W3C-Webhook-Signature-256-Base64</code>.
</p>
<p>
To verify that this signature is correct, and thus that it indeed came from W3C, you need to compute hash using
an HMAC hex digest, with the SHA-256 algorithm, and then compare this hash with the signature.
</p>
<p>
Here is a sample PHP code exhibiting how to verify the signature:
</p>
<pre><code class="language-php"><?php
$body = file_get_contents('php://input');
$secret = $_ENV['SECRET_TOKEN'];
$signature = hash_hmac('sha256', $body, $secret);
if (hash_equals($signature, $_SERVER['HTTP_X_W3C_WEBHOOK_SIGNATURE_256'])) {
echo 'ok';
} else {
http_response_code(403);
}</code></pre>
<p>
Your language and server implementations may differ from this example code. However, there are a few important
things to point out:
</p>
<ul>
<li>
Never hardcode the secret token into your app! Use environment variables or other secure means to expose the
token to your app.
</li>
<li>
Avoid using <code>==</code> to compare the signature with the computed hash. Instead, use a method like
<code>hash_equals</code> to perform a timing attack safe string comparison.
</li>
</ul>
</section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
</body>
</html>