comparison mod_unified_push/mod_unified_push.lua @ 5149:fa56ed2bacab

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.
author Matthew Wild <mwild1@gmail.com>
date Sat, 14 Jan 2023 14:31:37 +0000
parents bf42f1401f1c
children 2b6c543c4d3a
comparison
equal deleted inserted replaced
5148:bf42f1401f1c 5149:fa56ed2bacab
35 if not d then return nil, "invalid base64"; end 35 if not d then return nil, "invalid base64"; end
36 if #d ~= 32 then return nil, "incorrect decoded length, expected 32"; end 36 if #d ~= 32 then return nil, "incorrect decoded length, expected 32"; end
37 return s; 37 return s;
38 end 38 end
39 39
40 -- COMPAT w/0.12 40 local push_store = module:open_store();
41 local function jwt_sign(data) 41
42 return jwt.sign(unified_push_secret, data); 42 local backends = {
43 jwt = {
44 sign = function (data)
45 return jwt.sign(unified_push_secret, data);
46 end;
47
48 verify = function (token)
49 local ok, result = jwt.verify(unified_push_secret, token);
50
51 if not ok then
52 return ok, result;
53 end
54 if result.exp and result.exp < os.time() then
55 return nil, "token-expired";
56 end
57 return ok, result;
58 end;
59 };
60
61 storage = {
62 sign = function (data)
63 local reg_id = id.long();
64 local user, host = jid.split(data.sub);
65 if host ~= module.host or not user then
66 return;
67 end
68 push_store:set(reg_id, data);
69 return reg_id;
70 end;
71 verify = function (token)
72 local data = push_store:get(token);
73 if not data then
74 return nil, "item-not-found";
75 elseif data.exp and data.exp < os.time() then
76 push_store:set(token, nil);
77 return nil, "token-expired";
78 end
79 return data;
80 end;
81 };
82 };
83
84 if pcall(require, "util.paseto") then
85 local sign, verify = require "util.paseto".init(unified_push_secret);
86 backends.paseto = { sign = sign, verify = verify };
43 end 87 end
44 88
45 -- COMPAT w/0.12: add expiry check 89 local backend = module:get_option_string("unified_push_backend", backends.paseto and "paseto" or "storage");
46 local function jwt_verify(token)
47 local ok, result = jwt.verify(unified_push_secret, token);
48
49 if not ok then
50 return ok, result;
51 end
52 if result.exp and result.exp < os.time() then
53 return nil, "token-expired";
54 end
55 return ok, result;
56 end
57 90
58 local function register_route(params) 91 local function register_route(params)
59 local expiry = os.time() + push_registration_ttl; 92 local expiry = os.time() + push_registration_ttl;
93 local token = backends[backend].sign({
94 instance = params.instance;
95 application = params.application;
96 sub = params.jid;
97 exp = expiry;
98 });
60 return { 99 return {
61 url = module:http_url("push").."/"..urlencode(jwt_sign(unified_push_secret, { 100 url = module:http_url("push").."/"..urlencode(token);
62 instance = params.instance;
63 application = params.application;
64 sub = params.jid;
65 exp = expiry;
66 }));
67 expiry = expiry; 101 expiry = expiry;
68 }; 102 };
69 end 103 end
70 104
71 -- Handle incoming registration from XMPP client 105 -- Handle incoming registration from XMPP client
103 module:hook("iq-set/host/"..xmlns_up..":register", handle_register); 137 module:hook("iq-set/host/"..xmlns_up..":register", handle_register);
104 138
105 -- Handle incoming POST 139 -- Handle incoming POST
106 function handle_push(event, subpath) 140 function handle_push(event, subpath)
107 module:log("debug", "Incoming push received!"); 141 module:log("debug", "Incoming push received!");
108 local ok, data = jwt_verify(subpath); 142 local ok, data = backends[backend].verify(subpath);
109 if not ok then 143 if not ok then
110 module:log("debug", "Received push to unacceptable token (%s)", data); 144 module:log("debug", "Received push to unacceptable token (%s)", data);
111 return 404; 145 return 404;
112 end 146 end
113 local payload = event.request.body; 147 local payload = event.request.body;