Have problem when set moderation on JWT

the first problem is: if I want to set an account as moderation I need to call api.executeCommand(‘toggleRaiseHand’); 2 times
the second problem is: can not grant moderation right to a user
this is my lua config etc/prosody/conf.d/video.mydomain.net.cfg.lua

Component "conference.video.mydomain.net" "muc"
    restrict_room_creation = true
    storage = "memory"
 --   storage = "null"
    modules_enabled = {
        "muc_meeting_id";
        "muc_domain_mapper";
        "polls";
        "muc_allowners";
        "muc_moderators";
        "token_verification";
        "token_affiliation";
        "token_moderation";
    }

and this is console log when I grant moderation rights to a user, but nothing effect
2021-12-02T18:02:55.785Z [modules/xmpp/ChatRoom.js] <setAffiliation/<>: Set affiliation of participant with jid: wm5621631@conference.video.mydomain.net/92f11f7b to owner

any one know what is happening and how to solve those problem

You cannot use muc_allowners, token_affiliation and token_moderation modules at the same time. These are similar modules which handle ownership (moderator) in different ways

when I disable token_moderation every body joined to room are set as moderator

  --"muc_allowners";
        "muc_moderators";
        "token_verification";
        "token_affiliation";
 --"token_moderation";

Can you try only with token_verification and token_affiliation?
Remove muc_allowners, muc_moderators and token_moderation

still set all users as moderation
my config

 modules_enabled = {
        "muc_meeting_id";
        "muc_domain_mapper";
        "polls";
--      "muc_allowners";
--      "muc_moderators";
        "token_verification";
        "token_affiliation";
--      "token_moderation";
    }

code if user is owner

const options = {
    roomName: '{{$chatRoom->room_id}}',
    jwt:'{{$tokenKey}}',
    width:'100%',
    height:'100%',
    exp:{{$exp}},
    type:'JWT',
    useStunTurn:true,
    parentNode: document.querySelector('#meet'),
    interfaceConfigOverwrite: {
                    TOOLBAR_BUTTONS: [
        'microphone', 'camera', 'closedcaptions', 'desktop', 'fullscreen',
        'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
        'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
        'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
        'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone'
        ],
        MOBILE_APP_PROMO: false,
       },
       "context":{
       'user':{
        moderator:true,
        affiliation: 'owner',
        email:'{{$user->email}}',
        name:'{{$name}}',
        avatar:"{{$avatar}}",
        id:{{$user->id}},
    },
    "aud":"{{env('VIDEO_SERVER')}}",
    "iss":"{{env('VIDEO_CLIENT_ID')}}",
    "sub":"{{env('VIDEO_SERVER')}}",
    "room":"{{$chatRoom->room_id}}",
    "group": "{{$chatRoom->room_id}}",
    "moderator":true,
    "affiliation":'owner',
    "alg":"RS256"
},
    configOverwrite: { defaultLanguage: 'vi',disableDeepLinking: true,openBridgeChannel: 'datachannel',prejoinPageEnabled: true,TOOLBAR_ALWAYS_VISIBLE:true}
};

code if user is member

const options = {
    roomName: '{{$chatRoom->room_id}}',
    jwt:'{{$tokenKey}}',
    width: '100%',
    height: '100%',
    exp:{{$exp}},
    typ:'JWT', 
    useStunTurn:true,
    parentNode: document.querySelector('#meet'),
    interfaceConfigOverwrite: {
                    TOOLBAR_BUTTONS: [
        'microphone', 'camera', 'closedcaptions', 'desktop', 'fullscreen',
        'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
        'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
        'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
        'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone'
        ],
        MOBILE_APP_PROMO: false,
       },
       "context":{
       'user':{
        moderator:false,
        affiliation: 'member',
        email:'{{$user->email}}',
        name:'{{$name}}',
        avatar:"{{$avatar}}",
        id:{{$user->id}},
    },
    "aud":"{{env('VIDEO_SERVER')}}",
    "iss":"{{env('VIDEO_CLIENT_ID')}}",
    "sub":"{{env('VIDEO_SERVER')}}",
    "room":"{{$chatRoom->room_id}}",
    "group":"{{$chatRoom->room_id}}",
    "moderator":false,
    "affiliation":'member',
    "alg":"RS256"
},
    configOverwrite: { defaultLanguage: 'vi',disableDeepLinking: true,openBridgeChannel: 'datachannel',prejoinPageEnabled: true}
};

Did you restart prosody?

Did you check the token content on jwt.io

I restart prosody by command
service jicofo restart && service jitsi-videobridge2 restart && service prosody restart
token content on jwt.io for member
HEADER:ALGORITHM & TOKEN TYPE

{
  "typ": "JWT",
  "alg": "HS256"
}

PAYLOAD:DATA

{
  "context": {
    "user": {
      "moderator": false,
      "affiliation": "member",
      "email": "myemail@gmail.com",
      "name": " test",
      "avatar": "https://mydomain.net/frontend/images/user.png",
      "id": 4
    }
  },
  "aud": "video.mydomain.net",
  "iss": "YG2JwtYJl8Ts45O27DGbi7Vka",
  "sub": "video.mydomain.net",
  "room": "WM5621631",
  "exp": 1638480063,
  "moderator": false,
  "typ": "JWT",
  "alg": "RS256"
}

what is your prosody version?

this is my prosody version:
Prosody 0.11.9

Prosody directories

Data directory: /var/lib/prosody
Config directory: /etc/prosody
Source directory: /usr/lib/prosody
Plugin directories:
/usr/share/jitsi-meet/prosody-plugins/
/usr/lib/prosody/modules/

Lua environment

Lua version: Lua 5.2

Lua module search paths:
/usr/lib/prosody/?.lua
/usr/local/share/lua/5.2/?.lua
/usr/local/share/lua/5.2/?/init.lua
/usr/local/lib/lua/5.2/?.lua
/usr/local/lib/lua/5.2/?/init.lua
/usr/share/lua/5.2/?.lua
/usr/share/lua/5.2/?/init.lua
/root/.luarocks/share/lua/5.2/?.lua
/root/.luarocks/share/lua/5.2/?/init.lua

Lua C module search paths:
/usr/lib/prosody/?.so
/usr/local/lib/lua/5.2/?.so
/usr/lib/x86_64-linux-gnu/lua/5.2/?.so
/usr/lib/lua/5.2/?.so
/usr/local/lib/lua/5.2/loadall.so
/root/.luarocks/lib/lua/5.2/?.so

LuaRocks: Installed (2.4.1)

Network

Backend: select

Lua module versions

lfs: LuaFileSystem 1.6.3
lxp: LuaExpat 1.3.0
socket: LuaSocket 3.0-rc1
ssl: 0.6

Can you try without an id field in payload?

Or with a token from jitok.emrah.com
Fill only secret and aud

tried, but not working

Can you try without an id field in payload?

Or with a token from jitok.emrah.com
Fill only secret and aud

It works on my side when adding token_verification and token_affiliation to vanilla jitsi. Maybe you have some customizations and extra modules which cause the issue

thank you, let me check again my code

can you help me to take a look my code
this is my lua

plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }

-- domain mapper options, must at least have domain base set to use the mapper
muc_mapper_domain_base = "video.mydomain.net";

external_service_secret = "ZKSF0CqlDH1BYISd";
external_services = {
     { type = "stun", host = "video.mydomain.net", port = 3478 },
     { type = "turn", host = "video.mydomain.net", port = 3478, transport = "udp", secret = true, ttl = 86400, algorithm = "turn" },
     { type = "turns", host = "video.mydomain.net", port = 5349, transport = "tcp", secret = true, ttl = 86400, algorithm = "turn" }
};

cross_domain_bosh = false;
consider_bosh_secure = true;
-- https_ports = { }; -- Remove this line to prevent listening on port 5284

-- https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4
ssl = {
    protocol = "tlsv1_2+";
    ciphers = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
}

unlimited_jids = {
    "focus@auth.video.mydomain.net",
    "jvb@auth.video.mydomain.net"
}

VirtualHost "guest.video.mydomain.net"
    authentication = "anonymous"
    c2s_require_encryption = false

VirtualHost "mydomain.net"
    -- enabled = false -- Remove this line to enable this host
    authentication = "token"
    -- Properties below are modified by jitsi-meet-tokens package config
    -- and authentication above is switched to "token"
    app_id="mypublickey"
    app_secret="mysecretkey"
    c2s_require_encryption = true;
    allow_empty_token = false;

VirtualHost "video.mydomain.net"
    -- enabled = false -- Remove this line to enable this host
    authentication = "token"
    -- Properties below are modified by jitsi-meet-tokens package config
    -- and authentication above is switched to "token"
    app_id="mypublickey"
    app_secret="mysecretkey"
    -- Assign this host a certificate for TLS, otherwise it would use the one
    -- set in the global section (if any).
    -- Note that old-style SSL on port 5223 only supports one certificate, and will always
    -- use the global one.
    ssl = {
	key = "/etc/prosody/certs/video.mydomain.net.key";
	certificate = "/etc/prosody/certs/video.mydomain.net.crt";
    }
    av_moderation_component = "avmoderation.video.mydomain.net"
    speakerstats_component = "speakerstats.video.mydomain.net"
    conference_duration_component = "conferenceduration.video.mydomain.net"
    -- we need bosh
    modules_enabled = {
        "bosh";
        "pubsub";
        "ping"; -- Enable mod_ping
        "speakerstats";
        "external_services";
        "conference_duration";
        "muc_lobby_rooms";
        "av_moderation";
	"presence_identity";
    }
    c2s_require_encryption = false
    lobby_muc = "lobby.video.mydomain.net"
    main_muc = "conference.video.mydomain.net"
    -- muc_lobby_whitelist = { "recorder.video.mydomain.net" } -- Here we can whitelist jibri to enter lobby enabled rooms

Component "conference.video.mydomain.net" "muc"
    restrict_room_creation = true
    storage = "memory"
 --   storage = "null"
    modules_enabled = {
        "muc_meeting_id";
        "muc_domain_mapper";
        "polls";
	"muc_allowners";
--	"muc_moderators";
        "token_verification";
--	"token_affiliation";
	"token_moderation";
    }
    admins = { "focus@auth.video.mydomain.net" }
    muc_room_locking = false
    muc_room_default_public_jids = true

-- internal muc component
Component "internal.auth.video.mydomain.net" "muc"
    storage = "memory"
    modules_enabled = {
        "ping";
    }
    admins = { "focus@auth.video.mydomain.net", "jvb@auth.video.mydomain.net" }
    muc_room_locking = false
    muc_room_default_public_jids = true

VirtualHost "auth.video.mydomain.net"
    ssl = {
	key = "/etc/prosody/certs/auth.video.mydomain.net.key";
	certificate = "/etc/prosody/certs/auth.video.mydomain.net.crt";
}
    modules_enabled = {
        "limits_exception";
    }
    authentication = "internal_hashed"

-- Proxy to jicofo's user JID, so that it doesn't have to register as a component.
Component "focus.video.mydomain.net" "client_proxy"
    target_address = "focus@auth.video.mydomain.net"

Component "speakerstats.video.mydomain.net" "speakerstats_component"
    muc_component = "conference.video.mydomain.net"

Component "conferenceduration.video.mydomain.net" "conference_duration_component"
    muc_component = "conference.video.mydomain.net"

Component "avmoderation.video.mydomain.net" "av_moderation_component"
    muc_component = "conference.video.mydomain.net"

Component "lobby.video.mydomain.net" "muc"
    storage = "memory"
    restrict_room_creation = true
    muc_room_locking = false
    muc_room_default_public_jids = true

this is my module mod_token_moderation.lua

local log = module._log;
local jid_bare = require "util.jid".bare;
local json = require "cjson";
local basexx = require "basexx";
local um_is_admin = require "core.usermanager".is_admin;

local function is_admin(jid)
        return um_is_admin(jid, module.host);
end

log('info', 'Loaded token moderation plugin');
-- Hook into room creation to add this wrapper to every new room
module:hook("muc-room-created", function(event)
        log('info', 'room created, adding token moderation code');
        local room = event.room;
        local _handle_normal_presence = room.handle_normal_presence;
        local _handle_first_presence = room.handle_first_presence;
        -- Wrap presence handlers to set affiliations from token whenever a user joins
        room.handle_normal_presence = function(thisRoom, origin, stanza)
                local pres = _handle_normal_presence(thisRoom, origin, stanza);
                setupAffiliation(thisRoom, origin, stanza);
                return pres;
        end;
        room.handle_first_presence = function(thisRoom, origin, stanza)
                local pres = _handle_first_presence(thisRoom, origin, stanza);
                setupAffiliation(thisRoom, origin, stanza);
                return pres;
        end;
        -- Wrap set affilaition to block anything but token setting owner (stop pesky auto-ownering)
        local _set_affiliation = room.set_affiliation;
        room.set_affiliation = function(room, actor, jid, affiliation, reason)
                -- let this plugin do whatever it wants
                if actor == "token_plugin" then
                        return _set_affiliation(room, true, jid, affiliation, reason);
                -- noone else can assign owner (in order to block prosody/jisti's built in moderation functionality
                elseif affiliation == "owner" then
                        return nil, "modify", "not-acceptable"
                -- keep other affil stuff working as normal (hopefully, haven't needed to use/test any of it)
		else
                        return _set_affiliation(room, actor, jid, affiliation, reason);
                end;
        end;
end);
function setupAffiliation(room, origin, stanza)
        if origin.auth_token then
                -- Extract token body and decode it
                local dotFirst = origin.auth_token:find("%.");
                if dotFirst then
                        local dotSecond = origin.auth_token:sub(dotFirst + 1):find("%.");
                        if dotSecond then
                                local bodyB64 = origin.auth_token:sub(dotFirst + 1, dotFirst + dotSecond - 1);
                                local body = json.decode(basexx.from_url64(bodyB64));
                                local jid = jid_bare(stanza.attr.from);
                                -- If user is a moderator or an admin, set their affiliation to be an owner
                                if body["moderator"] == true or is_admin(jid) then
                                        room:set_affiliation("token_plugin", jid, "owner");
                                else
                                        room:set_affiliation("token_plugin", jid, "member");
                                end;
			end;
		end;
	end;
end;

list files in: /usr/share/jitsi-meet/prosody-plugins

ext_events.lib.lua
mod_jiconop.lua
mod_muc_poltergeist.lua
mod_speakerstats_component.lua
mod_auth_jitsi-anonymous.lua
mod_jitsi_session.lua
mod_muc_size.lua
mod_token_affiliation.lua
mod_auth_token.lua
mod_limits_exception.lua
mod_muc_transcription_filter.lua
mod_token_moderation.lua
mod_av_moderation.lua
mod_muc_allowners.lua
mod_polls.lua
mod_token_verification.lua
mod_av_moderation_component.lua
mod_muc_call.lua
mod_poltergeist_component.lua
mod_turncredentials.lua
mod_client_proxy.lua
mod_muc_census.lua
mod_presence_identity.lua
mod_websocket_session_event.patch
mod_conference_duration.lua
mod_muc_domain_mapper.lua
mod_reservations.lua
muc_owner_allow_kick.patch
mod_conference_duration_component.lua
mod_muc_lobby_rooms---back-up-filelua-before-change-function-filter_stanzamod_roster_command.lua
poltergeist.lib.lua
mod_external_services.lua
mod_muc_lobby_rooms.lua
mod_roster_command.patchtoken
mod_filter_iq_jibri.lua
mod_muc_max_occupants.lua
mod_smacks.lua
util.lib.lua
mod_filter_iq_rayo.lua
mod_muc_meeting_id.lua
mod_speakerstats.lua

These are from my working setup

jitsi.mydomain.corp.cfg.lua.txt (4.3 KB)
jicofo.conf.txt (519 Bytes)

1 Like

can you upload your config token?
I changed my config file like your, but still not working, every body joined to room was set moderation.

What do you mean by config token?

That mean the javascript file like below

<script language="javascript">
const domain ='video.mydomain.net';
const options = {
    roomName: 'Room name',
    jwt:'mytoken',
    width:'100%',
    height:'100%',
    exp:{{$exp}},
    type:'JWT',
    useStunTurn:true,
    parentNode: document.querySelector('#meet'),
    interfaceConfigOverwrite: {
                    TOOLBAR_BUTTONS: [
        'microphone', 'camera', 'closedcaptions', 'desktop', 'fullscreen',
        'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
        'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
        'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
        'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone'
        ],
        MOBILE_APP_PROMO: false,
       },
       "context":{
       'user':{
        moderator:false,
        affiliation: 'member',
        email:'test@mydomain.net',
        name:'Test',
        avatar:"https://mydomain.net/avatar/test.png"
    },
    "aud":"mydomain.net",
    "iss":"mysecretkey",
    "sub":"video.mydomain.net",
    "room":"123",
    "group": "123",
    "moderator":false,
    "affiliation":'member',
    "alg":"RS256"
},
    configOverwrite: { defaultLanguage: 'en',disableDeepLinking: true,openBridgeChannel: 'datachannel',prejoinPageEnabled: true,TOOLBAR_ALWAYS_VISIBLE:true}
};
const api = new JitsiMeetExternalAPI(domain, options);


api.addEventListener('participantRoleChanged', function(event) {
  onConferenceJoined();
  console.log(event.role);
  
    if (event.role === "moderator") {
      @if($isModerator==true) 
      //i dont know what is hapening here, i need call "toggleRaiseHand" 2 times to set moderation , "const options" above is member
    api.executeCommand('toggleRaiseHand');
    api.executeCommand('toggleRaiseHand');
    @endif
    
    }
    
});
</script>

I don’t use jitsi in iframe. Therefore I have no such a script.

jwt:'mytoken',

Do you put yout token here?