Software /
code /
prosody-modules
Changeset
3501:1df139b157fb
mod_pubsub_post: Add support for WebSub authentication
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Fri, 24 Aug 2018 14:52:09 +0200 |
parents | 3500:e86315c9b5c4 |
children | 3502:42e9e3c5eb02 |
files | mod_pubsub_post/README.markdown mod_pubsub_post/mod_pubsub_post.lua |
diffstat | 2 files changed, 42 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/mod_pubsub_post/README.markdown Fri Mar 29 17:06:18 2019 +0100 +++ b/mod_pubsub_post/README.markdown Fri Aug 24 14:52:09 2018 +0200 @@ -43,6 +43,24 @@ to attempt to publish to a non-existant node becomes owner of it, which includes publishing rights. +## WebSub + +``` {.lua} +-- Per node secrets +pubsub_post_secrets = { + my_node = "shared secret" +} + +-- Same secret for all nodes +pubsub_post_secret = "shared secret" +``` + +This enables the +[WebSub](https://www.w3.org/TR/2018/REC-websub-20180123/) [Authenticated +Content +Distribution](https://www.w3.org/TR/2018/REC-websub-20180123/#authenticated-content-distribution) +authentication method, where payloads are signed using a shared secret. + ## Setting up affiliations Prosodys PubSub module supports [setting affiliations via
--- a/mod_pubsub_post/mod_pubsub_post.lua Fri Mar 29 17:06:18 2019 +0100 +++ b/mod_pubsub_post/mod_pubsub_post.lua Fri Aug 24 14:52:09 2018 +0200 @@ -5,6 +5,14 @@ local xml = require "util.xml"; local uuid_generate = require "util.uuid".generate; local timestamp_generate = require "util.datetime".datetime; +local hashes = require "util.hashes"; +local from_hex = require "util.hex".from; +local hmacs = { + sha1 = hashes.hmac_sha1; + sha256 = hashes.hmac_sha256; + sha384 = hashes.hmac_sha384; + sha512 = hashes.hmac_sha512; +}; local pubsub_service = module:depends("pubsub").service; @@ -68,6 +76,17 @@ end local actor_source = module:get_option_string("pubsub_post_actor", "superuser"); +local actor_secret = module:get_option_string("pubsub_post_secret"); +local actor_secrets = module:get_option("pubsub_post_secrets"); + +local function verify_signature(secret, body, signature) + if not signature then return false; end + local algo, digest = signature:match("^([^=]+)=(%x+)"); + if not algo then return false; end + local hmac = hmacs[algo]; + if not algo then return false; end + return hmac(secret, body) == from_hex(digest); +end function handle_POST(event, path) local request = event.request; @@ -76,6 +95,11 @@ local content_type = request.headers.content_type or "application/octet-stream"; local actor; + local secret = actor_secrets and actor_secrets[path] or actor_secret; + if secret and not verify_signature(secret, request.body, request.headers.x_hub_signature) then + return 401; + end + if actor_source == "request.ip" then actor = request.ip or request.conn:ip(); elseif actor_source == "superuser" then