-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.html
302 lines (287 loc) · 19.7 KB
/
index.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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ActivityPub and WebFinger</title>
<script src="https://www.w3.org/Tools/respec/respec-w3c" class="remove" defer></script>
<script class="remove">
// All config options at https://respec.org/docs/
var respecConfig = {
editors: [{ name: "a", url: "https://trwnh.com" }, { name: "Evan Prodromou", url: "https://evanp.me/" }],
specStatus: "CG-FINAL",
latestVersion: "https://www.w3.org/community/reports/socialcg/apwf/",
xref: "web-platform",
group: "socialcg",
shortName: "apwf",
github: "swicg/activitypub-webfinger",
};
</script>
</head>
<body>
<section id="abstract">
<p>Identifiers in ActivityPub tend to be HTTPS URIs. The use of WebFinger (as defined in [[RFC7033]]) allows for discovery of an actor's identifier given a username and a hostname, which may be more socially salient or otherwise easier to communicate across various contexts and media. The username and hostname are resolved at the WebFinger endpoint of the hostname in order to discover a link to an actor associated with the user's account, and that actor similarly can be back-linked to the username and hostname.</p>
</section>
<section id="sotd">
</section>
<section class="informative" id="motivation">
<h2>Motivation</h2>
<p>Consider an HTTPS URI of the form <code>https://social.example/actors/9c5b94b1-35ad-49bb-b118-8e8fc24abf80</code> being used as an identifier for an actor associated with a user account. Communicating this digitally may be done by simply using the HTTPS URI as-is, as a hyperlink reference. However, communicating this verbally or in a space-constrained visual format can be difficult. WebFinger allows communicating aliases of the form <code>[email protected]</code>, which are easier to work with in the previously-cited cases.</p>
<p>Additional benefits of using WebFinger include smoothing over the differences between varying actor URI schemas. Different softwares may provide human-friendly URLs for an actor's profile, but these URLs may take several different forms:</p>
<ul>
<li><code>https://social.example/alyssa</code></li>
<li><code>https://social.example/@alyssa</code></li>
<li><code>https://social.example/~alyssa</code></li>
<li><code>https://social.example/u/alyssa</code></li>
<li>...and so on.</li>
</ul>
<p>Conventionally, people can be identified by their user@domain address, while documents can be identified by their HTTPS location.</p>
</section>
<section class="normative" id="discovery">
<h2>Discovery</h2>
<p>Discovery can occur in one of two directions:</p>
<ul>
<li>Given a WebFinger address, one can resolve it to an actor document;</li>
<li>Given an actor document with a preferredUsername, one can reconstruct a WebFinger address that should link back to the same actor document.</li>
</ul>
<p>The former will be referred to as "forward discovery" and the latter will be referred to as "reverse discovery".</p>
<section id="forward-discovery">
<h3>Forward discovery of an actor document given a WebFinger address</h3>
<p>Given a username and hostname in the form <code>user@domain</code>:</p>
<ol>
<li>Construct an <code>acct:</code> URI of the form <code>acct:user@domain</code> (as defined in [[RFC7565]])</li>
<li>Make an HTTP GET request to that hostname's WebFinger well-known endpoint, using the <code>acct:</code> URI as the value of the <code>resource</code> query parameter (as described in [[RFC7033]])</li>
</ol>
<p>For example, the WebFinger address <code>[email protected]</code> can be resolved as a resource by making an HTTP GET request for <code>https://social.example/.well-known/webfinger?resource=acct:[email protected]</code> (which is <code>https://social.example/.well-known/webfinger?resource=acct:alyssa%40social.example</code> when percent-encoded). This request MAY result in an HTTP 3xx redirect, in which case the redirect MUST be followed to the <code>Location</code> header's value, which MUST be an <code>https:</code> URI per [[RFC7033]]. (Subsequent redirects SHOULD be followed, up until a maximum redirect limit at the discretion of the requester.) The final request MUST return a JRD (JSON Resource Descriptor, as defined in [[RFC6415]]) with <code>application/jrd+json</code> as the content type (assuming no specified <code>Accept</code> header).</p>
<p>The WebFinger request and response may look like this:</p>
<pre class="http example" title="Sample WebFinger request and JRD response">
GET /.well-known/webfinger?resource=acct:alyssa%40social.example HTTP/1.1
Host: social.example
HTTP/1.1 307 Temporary Redirect
Location: https://social.example/jrd/alyssa
GET /jrd/alyssa HTTP/1.1
Host: social.example
HTTP/1.1 200 OK
Content-Type: application/jrd+json
{
"subject": "acct:[email protected]",
"aliases": [
"https://social.example/@alyssa",
"https://social.example/actors/9c5b94b1-35ad-49bb-b118-8e8fc24abf80"
],
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://social.example/@alyssa"
},
{
"rel": "self",
"type": "application/activity+json",
"href": "https://social.example/actors/9c5b94b1-35ad-49bb-b118-8e8fc24abf80"
}
]
}
</pre>
<p>At this point, you can parse for the <code>href</code> of the element of <code>links</code> that has a <code>rel</code> of <code>self</code> and a <code>type</code> of either <code>application/ld+json; profile="https://www.w3.org/ns/activitystreams"</code> or <code>application/activity+json</code> (depending on the implementation). See <a href="#link"></a> for more information about this.</p>
</section>
<section id="reverse-discovery">
<h3>Reverse discovery of a WebFinger address given an actor document</h3>
<p>Given an actor with an <code>id</code> and a <code>preferredUsername</code>:</p>
<ol>
<li>Take the hostname of the <code>id</code> to discover the WebFinger domain</li>
<li>Combine the <code>preferredUsername</code> and the WebFinger domain in order to form a WebFinger address</li>
<li>Verify that this WebFinger address links back to the same actor when performing discovery as described in <a href="#forward-discovery"></a></li>
<li>Optionally: If the JRD from the previous step has a <code>subject</code> and it contains an <code>acct:</code> URI different from the one you constructed, perform a verification discovery against that <code>acct:</code> URI afterward. (In such cases, the <code>subject</code> of the JRD denotes the expected canonical identifier.)</li>
</ol>
<p>For example, given an actor document at <code>https://activitypub.example.com/actor/1</code> like so:</p>
<pre class="json example" title="Sample actor document">
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://activitypub.example.com/actor/1",
"preferredUsername": "alice",
"name": "Alice P. Hacker"
}
</pre>
<p>The reverse discovery process would extract <code>alice</code> and <code>activitypub.example.com</code>, construct the <code>acct:</code> URI <code>acct:[email protected]</code>, then request <code>https://activitypub.example.com/.well-known/webfinger?resource=acct:[email protected]</code> like so:</p>
<pre class="http example" title="Verifying the constructed WebFinger address">
GET /.well-known/webfinger?resource=acct:[email protected] HTTP/1.1
Host: activitypub.example.com
HTTP/1.1 200 OK
Content-Type: application/jrd+json
{
"subject": "acct:[email protected]",
"aliases": [
"https://example.com/@alice",
"https://activitypub.example.com/actors/1"
],
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://example.com/@alice"
},
{
"rel": "self",
"type": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
"href": "https://activitypub.example.com/actors/1"
}
]
}
</pre>
<p>At this point, we have validated that <code>[email protected]</code> links back to our actor document, but we can optionally verify that the canonical WebFinger address of <code>[email protected]</code> also links back to the same actor document:</p>
<pre class="http example" title="Verifying the canonical WebFinger address discovered from the constructed WebFinger address">
GET /.well-known/webfinger?resource=acct:[email protected] HTTP/1.1
Host: example.com
HTTP/1.1 307 Temporary Redirect
Location: https://activitypub.example.com/.well-known/webfinger?resource=acct:[email protected]
GET /.well-known/webfinger?resource=acct:[email protected] HTTP/1.1
Host: activitypub.example.com
HTTP/1.1 200 OK
Content-Type: application/jrd+json
{
"subject": "acct:[email protected]",
"aliases": [
"https://example.com/@alice",
"https://activitypub.example.com/actors/1"
],
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://example.com/@alice"
},
{
"rel": "self",
"type": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
"href": "https://activitypub.example.com/actors/1"
}
]
}
</pre>
</section>
</section>
<section class="normative" id="encoding">
<h2>Encoding</h2>
<p>To ensure smooth operation of the WebFinger discovery flows, identifiers and responses should follow certain guidelines for encoding.</p>
<section class="normative" id="names">
<h3>Limitations on usernames and hostnames</h3>
<p>The <code>acct:</code> URI scheme is defined in [[RFC7565]], which contains ABNF ([[RFC5234]]) for allowed characters (inheriting from [[RFC3986]] as well):</p>
<pre class="abnf" title="acct: URI ABNF">
acctURI = "acct" ":" userpart "@" host
userpart = unreserved / sub-delims
0*( unreserved / pct-encoded / sub-delims )
; userpart regex: [A-Za-z0-9\-\.\_\~\!\$\&\'\(\)\*\+\,\;\=](?:[A-Za-z0-9\-\.\_\~\!\$\&\'\(\)\*\+\,\;\=]|(?:%[0-9A-Fa-f]{2}))*
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
pct-encoded = "%" HEXDIG HEXDIG
host = IP-literal / IPv4address / reg-name
reg-name = *( unreserved / pct-encoded / sub-delims )
; reg-name regex: (?:[A-Za-z0-9\-\.\_\~\!\$\&\'\(\)\*\+\,\;\=]|(?:%[0-9A-Fa-f]{2}))*
</pre>
<p>Further restrictions are specified in [[RFC7565]]:</p>
<ul>
<li>Per [[RFC3986]] Section 6.2.2.1 "Case Normalization", the scheme and host are case-insensitive and SHOULD therefore be normalized to lowercase.</li>
<li>Per [[RFC3986]] Section 6.2.2.2 "Percent-Encoding Normalization", any <code>unreserved</code> characters SHOULD be decoded if encountered as a percent-encoded octet.</li>
<li>Before percent-encoding anything, the userpart MUST consist only of Unicode code points that are part of the [[RFC7564]] PRECIS <code>IdentifierClass</code>
</li>
<li>Before percent-encoding anything, the hostname MUST conform to IDNA rules specified in [[RFC5982]] and [[RFC5980]] for A-label (ASCII-Compatible Encoding, or Punycode)</li>
</ul>
<section class="informative" id="rules">
<h4>Current implementation rules</h4>
<p>Note that while there are several symbols allowed in the userpart, the de facto limits set by some current implementers are much more restrictive.</p>
<p>At the time of this writing, Mastodon enforces the following rules:</p>
<ul>
<li>The username must be at least one character</li>
<li>ASCII alphanumeric characters (<code>A</code> through <code>Z</code>, <code>a</code> through <code>z</code>, <code>0</code> through <code>9</code>) and underscores (<code>_</code>) are generally allowed anywhere in the username</li>
<li>Dots (<code>.</code>) and dashes (<code>-</code>) are allowed in the middle of a username, but not as the first character or the last character</li>
<li>All other symbols are disallowed</li>
<li>Usernames are case-insensitive</li>
</ul>
<p>In other words, Mastodon will accept the regular expression or the following ABNF:</p>
<pre class="abnf" title="Mastodon username ABNF">
; As a regular expression, this can be expressed as follows:
; /[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?/i
username = word
*( rest )
word = ALPHA / DIGIT / "_"
rest = *( extended )
word
extended = word / "." / "-"
</pre>
<p>Similarly at the time of this writing, Misskey is subject to the following limitations:</p>
<ul>
<li>Usernames are limited to 128 characters</li>
<li>Hostnames are limited to 128 characters</li>
</ul>
</section>
<section class="normative" id="usernames">
<h4>Recommendations for handling usernames</h4>
<ul>
<li>Implementers SHOULD treat local usernames as case-insensitive.</li>
<li>Implementers SHOULD NOT assume case insensitivity for external usernames.</li>
<li>Implementers SHOULD NOT treat usernames as stable identifiers that will always map to the same actor, and SHOULD use the actor <code>id</code> in any references to an actor. (Implementers that currently treat usernames as canonical identifiers SHOULD take steps to avoid doing so in the future.)</li>
<li>Implementers SHOULD limit the length of local usernames. The exact limit is not specified, but it is noteworthy that similar systems such as email often limit the localpart to 64 characters (per [[RFC2821]]).</li>
<li>Implementers SHOULD support remote usernames containing valid characters per [[RFC7565]]. For short-term compatibility, implementers SHOULD NOT use characters other than alphanumeric (<code>A-Z, a-z, 0-9</code>) and underscores (<code>_</code>).</li>
</ul>
</section>
</section>
<section class="normative" id="link">
<h3>Establishing a link between the WebFinger resource and the actor document</h3>
<p>As discussed in <a href="#forward-discovery"></a>, the link to the actor associated with a given WebFinger address will have the following qualifiers:</p>
<ul>
<li><code>rel</code> MUST be <code>self</code></li>
<li><code>type</code> MUST be either <code>application/activity+json</code> or <code>application/ld+json; profile="https://www.w3.org/ns/activitystreams"</code></li>
</ul>
<p>Publishers SHOULD include only one such link.</p>
<p>Due to the prevailing use of WebFinger addresses as canonical primary identifiers for users, implementations that require WebFinger for compatibility will often also deduplicate actors based on the WebFinger address. Therefore, it is generally expected that there is only one <code>self</code> link to an Activity Streams document, in a unary relationship. However, some implementations do not follow this expectation, and there might be multiple links to ActivityStreams documents for the same WebFinger <code>acct:</code> resource. In such cases, one of the following strategies may be employed:</p>
<ul>
<li>Take the first matching entry in `links`</li>
<li>Deduplicate all matching entries in `links` by their `href`</li>
<li>Check for additional information, such as the presence of `properties` which may give further hints as to which entry of `links` is appropriate to follow</li>
</ul>
</section>
</section>
<section class="informative" id="other">
<h3>Other uses of WebFinger</h3>
<p>Aside from the self-link to the associated actor, resolving a WebFinger query may expose some other links of potential interest. The following link relations are currently common among WebFinger implementers, and are recommended for use especially when the actor document is not publicly available:</p>
<ul>
<li><code>http://webfinger.net/rel/profile-page</code> (for quickly getting the HTML profile page of a user without resolving their actor document and checking for the <code>url</code>)</li>
<li><code>http://webfinger.net/rel/avatar</code> (for quickly getting the avatar of a user without resolving their actor document and checking for the <code>icon</code>)</li>
</ul>
<p>The following link relations are less common, but offer useful information to ActivityPub implementers:</p>
<ul>
<li><code>http://ostatus.org/schema/1.0/subscribe</code> (used to power features like Mastodon's remote follow buttons)</li>
<li><code>http://schemas.google.com/g/2010#updates-from</code> (used by some implementations to link to an Atom feed)</li>
<li><code>feed</code> (used by some implementations to link to one or more feeds; feeds can be disambiguated by checking <code>type</code> and/or <code>title</code> properties of the link)</li>
<li><code>http://a9.com/-/spec/opensearch/1.1/</code> (for custom search bars)</li>
</ul>
<p>Also uncommon but supported by at least one implementation (WordPress) is the ability to query non-actor, non-user resources via WebFinger. The following link relations are exposed:</p>
<ul>
<li><code>shortlink</code></li>
<li><code>author</code></li>
<li><code>alternate</code></li>
<li><code>license</code></li>
<li><code>canonical</code></li>
<li><code>webmention</code></li>
</ul>
</section>
<section class="informative" id="security">
<h2>Security Considerations</h2>
<p>Using WebFinger can provide proof of existence of an associated actor document, as well as make it easier to discover that associated actor document; following this, an actor's inbox can be likewise discovered, and spam or other unwanted messages can be delivered to that actor's inbox. It may be desirable for some systems to not publicly expose an actor's existence and instead rely on the user manually entering their actor's HTTPS URI, or maintaining a "contact list" of bookmarked actors or resources. For such systems, the use of WebFinger is not advisable.</p>
<p>WebFinger allows for the lookup request to redirect; this primarily allows a web host or origin to defer or delegate their WebFinger lookups to a separate WebFinger service, but it can also create an issue when there are multiple redirects. For this reason, anyone making a WebFinger request should take care to limit the maximum number of redirects that they follow.</p>
</section>
<section class="informative" id="future">
<h2>Future Enhancements</h2>
<p>The current use of WebFinger with ActivityPub could be improved in several ways:</p>
<ul>
<li>Back-linking an actor to a WebFinger address could be more explicit. Current use of <code>preferredUsername</code> is not ideal for constructing WebFinger addresses, and it also does not allow for expressing actual "preferred usernames". An explicit property for denoting the user's "canonical" WebFinger address would ease reverse-discovery concerns.</li>
<li>Implementers could ease their de facto WebFinger requirements by treating WebFinger addresses purely as aliases for the actor's HTTPS identifier, rather than as the canonical identifier. This would make WebFinger address mappings mutable and no longer unary.</li>
<li>WebFinger could be used to expose certain properties or qualified links more directly, skipping the resolution of the actor resource.</li>
<li>An ActivityStreams profile of WebFinger can be defined in order to redirect or directly resolve the actor document if the <code>Accept</code> header specifies the correct media type, skipping the resolution of the JRD.</li>
<li>A convention could be adopted for identifying users by a DNS name instead of an acct: URI.</li>
</ul>
</section>
<section id="conformance">
</section>
</body>
</html>