jitsi-meet/resources/prosody-plugins/mod_short_lived_token.lua
theluyuan 38ba663466
Some checks failed
Close stale issues and PRs / stale (push) Has been cancelled
init
2025-09-02 14:49:16 +08:00

139 lines
4.8 KiB
Lua

-- to be enabled under the main virtual host with all required settings
-- short_lived_token = {
-- issuer = 'myissuer';
-- accepted_audiences = { 'file-sharing' };
-- key_path = '/etc/prosody/short_lived_token.key';
-- key_id = 'my_kid';
-- ttl_seconds = 30;
-- };
-- The key in key_path can be generated via: openssl genrsa -out $PRIVATE_KEY_PATH 2048
-- And you can get the public key from it, which can be used ot verify those tokens via:
-- openssl rsa -in $PRIVATE_KEY_PATH -pubout -out $PUBLIC_KEY_PATH
local jid = require 'util.jid';
local st = require 'util.stanza';
local jwt = module:require 'luajwtjitsi';
local util = module:require 'util';
local is_vpaas = util.is_vpaas;
local process_host_module = util.process_host_module;
local table_find = util.table_find;
local create_throttle = require 'prosody.util.throttle'.create;
local SERVICE_TYPE = 'short-lived-token';
local options = module:get_option('short_lived_token');
if not (options.issuer and options.accepted_audiences
and options.key_path and options.key_id and options.ttl_seconds) then
module:log('error', 'Missing required options for short_lived_token');
return;
end
local f = io.open(options.key_path, 'r');
if f then
options.key = f:read('*all');
f:close();
end
local accepted_requests = {};
for _, host in pairs(options.accepted_audiences) do
accepted_requests[string.format('%s:%s:0', SERVICE_TYPE, host)] = host;
end
local server_region_name = module:get_option_string('region_name');
local main_muc_component_host = module:get_option_string('main_muc');
if main_muc_component_host == nil then
module:log('error', 'main_muc not configured. Cannot proceed.');
return;
end
local main_muc_service;
function generateToken(session, audience, room, occupant)
local t = os.time();
local exp = t + options.ttl_seconds;
local presence = occupant:get_presence(session.full_jid);
local _, _, id = extract_subdomain(jid.node(room.jid));
local payload = {
iss = options.issuer,
aud = audience,
nbf = t,
exp = exp,
sub = session.jitsi_web_query_prefix or module.host,
context = {
group = session.jitsi_meet_context_group or session.granted_jitsi_meet_context_group_id,
user = session.jitsi_meet_context_user or {
id = session.full_jid,
name = presence:get_child_text('nick', 'http://jabber.org/protocol/nick'),
email = presence:get_child_text("email") or nil,
nick = jid.resource(occupant.nick)
},
features = session.jitsi_meet_context_features
},
room = session.jitsi_web_query_room,
meeting_id = room._data.meetingId,
granted_from = session.granted_jitsi_meet_context_user_id,
customer_id = id or session.jitsi_meet_context_group or session.granted_jitsi_meet_context_group_id,
backend_region = server_region_name,
user_region = session.user_region
};
local alg = 'RS256';
local token, err = jwt.encode(payload, options.key, alg, { kid = options.key_id });
if not err then
return token
else
module:log('error', 'Error generating token: %s', err);
return ''
end
end
module:hook('external_service/credentials', function (event)
local requested_credentials, services, session, stanza
= event.requested_credentials, event.services, event.origin, event.stanza;
local room = get_room_by_name_and_subdomain(session.jitsi_web_query_room, session.jitsi_web_query_prefix);
if not room then
session.send(st.error_reply(stanza, 'cancel', 'not-allowed'));
return;
end
local occupant = room:get_occupant_by_real_jid(session.full_jid);
if not occupant then
session.send(st.error_reply(stanza, 'cancel', 'not-allowed'));
return;
end
for request in requested_credentials do
local host = accepted_requests[request];
if host then
services:push({
type = SERVICE_TYPE;
host = host;
username = 'token';
password = generateToken(session, host, room, occupant);
expires = os.time() + options.ttl_seconds;
restricted = true;
transport = 'https';
port = 443;
});
end
end
end);
process_host_module(main_muc_component_host, function(host_module, host)
local muc_module = prosody.hosts[host].modules.muc;
if muc_module then
main_muc_service = muc_module;
else
prosody.hosts[host].events.add_handler('module-loaded', function(event)
if (event.module == 'muc') then
main_muc_service = prosody.hosts[host].modules.muc;
end
end);
end
end);