首先阅读此帖子:How to Put Your Blog on the Fediverse
阅读该帖子后,您可能会想知道如何参加联邦政府活动,例如共享内容,获得追随者或接受评论。
答案是您需要先学习如何确认请求。
资源
Fediverse仍处于早期阶段。如果您想正确执行操作,请确保您是根据规格编写单元测试,并且不要害怕挖掘他人的代码。我对Chatgpt进行了很多反向工程,因为我不熟悉Ruby。诸如日期格式,JSON编码和base64之类的小东西都可以丢弃您的签名请求!
- ActivityPub Vocabulary (Data Structures)
- ActivityPub Spec
- HTTP Signatures Draft RFC
- Mastodon's Signature Verification
- RFC 7231 Date Format
- Base64 RFC
了解请求流(又称握手)
ActivityPub(核心)都是每个用户都有一个收件箱和一个输出箱,支持GET和发布请求。当互联网上的某人请求突变时,他们会发送邮政请求。当他们想获取当前状态时,他们会使用GET请求。将电子邮件作为您的心理模型,这将是有道理的。
收件箱和输出箱之间的区别
使用输出箱小于收件箱。 OUTBOX服务获得请求,以便其他服务器可以选择回填帖子。将邮政请求发送到发出框就像REST API一样。客户端将其发布到输出箱,然后服务器稍后对其进行处理,将帖子发送到其他收件箱。收件箱的获取主要用于私人使用。
确认请求常见问题解答
由于大多数交互通过收件箱发生,因此第一件事是建立用户的收件箱。
我应该使用哪种MIME类型或内容类型?
ActivityPub请求/响应应为application/ld+json; profile="https://www.w3.org/ns/activitystreams
或application/activity+json
,除非另有说明。他们应该得到平等对待。
我的HTTP响应包含ActivityPub文档;为什么什么都没发生?
要确认ActivityPub请求,您必须将邮政请求退还给演员收件箱。这就是ActivityPub服务器保持异步的方式。
发布请求无法正常工作!
双检查发送请求时,您正在附加Content-Type
标头,并将请求发送为原始字符串。一些库/框架将以表单请求或JSON发送您的数据。
示例已签署的接受/拒绝遵循请求
在上一个教程中,您应该定义一个用于处理收件箱请求的端点,并将其包含在用户的ActivityPub配置文件响应中。该响应至关重要,因为服务器使用它来找到您的收件箱。
设置服务器后处理收件箱的传入邮政请求。您将开始收到application/activity+json
类型请求,如以下内容:
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://mastodon.social/96cb3649-7a75-49c5-b246-xxxxxxxxxxxx",
"type": "Follow",
"actor": "https://mastodon.social/users/codoxuba",
"object": "https://example.com/activityPub/users/1"
}
在Follow
请求的情况下。 Actor
是启动请求的人/事物。 Object
是他们想要遵循的人/事物。 id
是由服务器开始请求的服务器创建的。大多数服务器使用ID跟踪请求状态。我不会在这篇文章中介绍。
但是,重要的是要注意,如上所述,actor
,object
和id
可以是未解决的URL,也可以是嵌套的JSON-LD对象。我不会在这篇文章中介绍。
承认即将到来的活动
这是您应该在TCP/IP协议上刷新的地方,因为该概念相似。我会提出预期的是,默认情况下忽略请求是明智的。任何确认都可以在攻击中对您使用。我只想处理遵循请求,因此我会忽略所有其他请求:
class InboxController extends Controller
{
public function receiveFromInternet(Request $request, User $user)
{
// todo: better validation
$activityType = $request->input('type');
if ($requestType !== 'Follow') {
return;
}
$acknowledgment = [
"@context" => "https://www.w3.org/ns/activitystreams",
"summary" => "Alice accepted a follow",
"type" => "Accept",
"actor" => "https://example.com/activityPub/users/1",
"object" => $request->input('id'),
];
}
}
确认很简单。对于关注请求,您可以选择Accept
,Reject
,TenativeAccept
,TenativeReject
或忽略。有问题的部分是签署确认。
签署确认
签名请求基于HTTP Signatures Draft RFC。在整个签名过程中,我将使用这些变量:
$url = parse_url($inboxUrl);
$host = data_get($url, 'host');
$path = data_get($url, 'path');
$publicKeyId = 'https://example.com/activityPub/users/1#main-key';
$date = now()->toRfc7231String();
接下来,将您的确认为JSON,然后创建一个摘要。您必须使用base64编码哈希,并使用用于生成哈希的算法的算法预处理值。大多数Fediverse服务器仅支持SHA-256。
$document_str = json_encode($document);
$sha256 = hash('sha256', $document_str, true);
$digest = 'SHA-256=' . base64_encode($sha256);
您必须使用该摘要来创建签名的Signature
标头。不包括摘要将产生无效的签名:
$dataToSign = "(request-target): {$method} {$path}\nhost: {$host}\ndate: {$date}\ndigest: {$digest}";
要签名的数据应该看起来像这样。没有尾随的新线条。没有马车返回。
(request-target): post /users/@bob/inbox
host: mastadon.social
date: Wed, 30 Aug 2023 12:01:01 GMT
digest: SHA-256=MmNmMjRkYmE1ZmIwYTMwZTI2ZTgzYjJhYzViOWUyOWUxYjE2MWU1YzFmYTc0MjVlNzMwNDMzNjI5MzhiOTgyNA==
在我们签署消息之前,您必须生成一个键对。公钥将分发给Fediverse。
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
注释您必须在用户的活动插件中包含公共密钥。
您必须使用相同的算法签名以创建摘要。根据编程语言,请参阅OpenSSL库。使用PHP,通常已经将其安装为PHP扩展名:
$privateKey = openssl_pkey_get_private(Storage::get('private.pem'));
$signature = null; // mutated by openssl
$enodedSignature = null;
if (openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256)) {
$encodedSignature = base64_encode($signature);
} else {
throw new Exception('Could not sign activityPub data');
}
签名后,您将获得一个基本64编码的字符串,而不是前面生成的摘要。在包含签名数据之前,我们必须构建Signature
标头,以便其他服务器可以验证标题。如果签署额外的标头,请修改签名标头的(request-target)
部分。
# signature header used when signing host, date, and digest.
$signatureHeader = sprintf( 'keyId="%s",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="%s"', $id, $base64 );
RAW HTTP标头在准备发送时将看起来像这样的东西:
Host: mastodon.social
Date: Wed, 18 Aug 2022 22:00:00 GMT
Digest: SHA-256=6ccPqhz2TJmLL08E8AFny1/Wube60hOH3g6zzwv/Ttg=
Signature: keyId="https://example.com/activityPub/users/1#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="Cdih8iQQQPeDInLCN4H94Lm/hTKSNOjSnjleI8gZfndRsTwO1CqG41s+BRF2Oh51yETWEsR2ezceDgUgH+ME4jdrgUIMPm/Ox4B6c5QEASPPlFpcOfWcLryCCvEkQOVd3tbMITeY+uY6WITuZKsXREAidmDopJ2pZ3Wvk4rXuTYHZEW2vsreLYCrXDkTCm4ySL2THlOrzc0JQh/4EYRaQx+v3VqVBJvY9+qPLIm1Y9RuRoN35SMNN/IcTkxHVue+mDu6I8IIq/QVmg8kKDbwQ/ywQGzegYt+P2lKujdx0sR3gbXAHX2sTDHCKncVu/PYLJF5/LoxhVxNc3s3QEo5Bw=="
Content-Type: application/activity+json
发送确认
最后,我们可以更新标题并将消息发布到用户的收件箱:
$headers = [
'Host' => $host,
'Date' => $date,
'Digest' => $digest,
'Signature' => $signatureHeader,
'Content-Type' => 'application/activity+json',
];
$activityResponse = Http::withHeaders($headers)
->withBody($document_str)
->contentType('application/activity+json')
->post($inboxUrl, $document);
包起来
成功发送了发布请求后,您应该能够接受关注者。但是,跟踪您的追随者将取决于您!