mod_unified_push: Add support for multiple token backends, including stoage
Now that we have ACLs by default, it is no longer necessary to be completely
stateless. On 0.12, using storage has benefits over JWT, because it does not
expose client JIDs to the push apps/services. In trunk, PASETO is stateless
and does not expose client JIDs.
--- a/mod_unified_push/mod_unified_push.lua Fri Jan 13 16:50:43 2023 +0000
+++ b/mod_unified_push/mod_unified_push.lua Sat Jan 14 14:31:37 2023 +0000
@@ -37,33 +37,67 @@
return s;
end
--- COMPAT w/0.12
-local function jwt_sign(data)
- return jwt.sign(unified_push_secret, data);
+local push_store = module:open_store();
+
+local backends = {
+ jwt = {
+ sign = function (data)
+ return jwt.sign(unified_push_secret, data);
+ end;
+
+ verify = function (token)
+ local ok, result = jwt.verify(unified_push_secret, token);
+
+ if not ok then
+ return ok, result;
+ end
+ if result.exp and result.exp < os.time() then
+ return nil, "token-expired";
+ end
+ return ok, result;
+ end;
+ };
+
+ storage = {
+ sign = function (data)
+ local reg_id = id.long();
+ local user, host = jid.split(data.sub);
+ if host ~= module.host or not user then
+ return;
+ end
+ push_store:set(reg_id, data);
+ return reg_id;
+ end;
+ verify = function (token)
+ local data = push_store:get(token);
+ if not data then
+ return nil, "item-not-found";
+ elseif data.exp and data.exp < os.time() then
+ push_store:set(token, nil);
+ return nil, "token-expired";
+ end
+ return data;
+ end;
+ };
+};
+
+if pcall(require, "util.paseto") then
+ local sign, verify = require "util.paseto".init(unified_push_secret);
+ backends.paseto = { sign = sign, verify = verify };
end
--- COMPAT w/0.12: add expiry check
-local function jwt_verify(token)
- local ok, result = jwt.verify(unified_push_secret, token);
-
- if not ok then
- return ok, result;
- end
- if result.exp and result.exp < os.time() then
- return nil, "token-expired";
- end
- return ok, result;
-end
+local backend = module:get_option_string("unified_push_backend", backends.paseto and "paseto" or "storage");
local function register_route(params)
local expiry = os.time() + push_registration_ttl;
+ local token = backends[backend].sign({
+ instance = params.instance;
+ application = params.application;
+ sub = params.jid;
+ exp = expiry;
+ });
return {
- url = module:http_url("push").."/"..urlencode(jwt_sign(unified_push_secret, {
- instance = params.instance;
- application = params.application;
- sub = params.jid;
- exp = expiry;
- }));
+ url = module:http_url("push").."/"..urlencode(token);
expiry = expiry;
};
end
@@ -105,7 +139,7 @@
-- Handle incoming POST
function handle_push(event, subpath)
module:log("debug", "Incoming push received!");
- local ok, data = jwt_verify(subpath);
+ local ok, data = backends[backend].verify(subpath);
if not ok then
module:log("debug", "Received push to unacceptable token (%s)", data);
return 404;