I have a docker jitsi installation, I want to use JWT to authenticate users, and I want to assign them the moderator or non moderator role based on JWT content, using mod_token_affiliation.
When users connect, they are moderators despite the fact that “affiliation” is set to “member”.
How to reproduce the problem.
- I downloaded docker-jitsi-meet, version 8319:
GitHub - jitsi/docker-jitsi-meet at stable-8319
and followed the instructions, using this .env file
# shellcheck disable=SC2034
################################################################################
################################################################################
# Welcome to the Jitsi Meet Docker setup!
#
# This sample .env file contains some basic options to get you started.
# The full options reference can be found here:
# https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker
################################################################################
################################################################################
#
# Basic configuration options
#
# Directory where all configuration will be stored
CONFIG=~/.jitsi-meet-cfg
# Exposed HTTP port
HTTP_PORT=80
# Exposed HTTPS port
HTTPS_PORT=443
# System time zone
TZ=UTC
# Public URL for the web service (required)
PUBLIC_URL=https://luigi.cloudz.it
# Media IP addresses to advertise by the JVB
# This setting deprecates DOCKER_HOST_ADDRESS, and supports a comma separated list of IPs
# See the "Running behind NAT or on a LAN environment" section in the Handbook:
# https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker#running-behind-nat-or-on-a-lan-environment
JVB_ADVERTISE_IPS=101.58.99.205,192.168.0.248
#
# JaaS Components (beta)
# https://jaas.8x8.vc
#
# Enable JaaS Components (hosted Jigasi)
# NOTE: if Let's Encrypt is enabled a JaaS account will be automatically created, using the provided email in LETSENCRYPT_EMAIL
#ENABLE_JAAS_COMPONENTS=0
#
# Let's Encrypt configuration
#
# Enable Let's Encrypt certificate generation
ENABLE_LETSENCRYPT=1
# Domain for which to generate the certificate
LETSENCRYPT_DOMAIN=luigi.cloudz.it
# E-Mail for receiving important account notifications (mandatory)
LETSENCRYPT_EMAIL=my@email.com
# Use the staging server (for avoiding rate limits while testing)
LETSENCRYPT_USE_STAGING=0
#
# Etherpad integration (for document sharing)
#
# Set etherpad-lite URL in docker local network (uncomment to enable)
#ETHERPAD_URL_BASE=http://etherpad.meet.jitsi:9001
# Set etherpad-lite public URL, including /p/ pad path fragment (uncomment to enable)
#ETHERPAD_PUBLIC_URL=https://etherpad.my.domain/p/
# Name your etherpad instance!
ETHERPAD_TITLE=Video Chat
# The default text of a pad
ETHERPAD_DEFAULT_PAD_TEXT="Welcome to Web Chat!\n\n"
# Name of the skin for etherpad
ETHERPAD_SKIN_NAME=colibris
# Skin variants for etherpad
ETHERPAD_SKIN_VARIANTS="super-light-toolbar super-light-editor light-background full-width-editor"
#
# Basic Jigasi configuration options (needed for SIP gateway support)
#
# SIP URI for incoming / outgoing calls
#JIGASI_SIP_URI=test@sip2sip.info
# Password for the specified SIP account as a clear text
#JIGASI_SIP_PASSWORD=passw0rd
# SIP server (use the SIP account domain if in doubt)
#JIGASI_SIP_SERVER=sip2sip.info
# SIP server port
#JIGASI_SIP_PORT=5060
# SIP server transport
#JIGASI_SIP_TRANSPORT=UDP
#
# Authentication configuration (see handbook for details)
#
# Enable authentication
ENABLE_AUTH=1
# Enable guest access
ENABLE_GUESTS=0
# Select authentication type: internal, jwt, ldap or matrix
AUTH_TYPE=jwt
# JWT authentication
#
# Application identifier
JWT_APP_ID=my_jitsi_app_id
# Application secret known only to your token generator
JWT_APP_SECRET=my_jitsi_app_secret
# (Optional) Set asap_accepted_issuers as a comma separated list
#JWT_ACCEPTED_ISSUERS=my_web_client,my_app_client
# (Optional) Set asap_accepted_audiences as a comma separated list
#JWT_ACCEPTED_AUDIENCES=my_server1,my_server2
# LDAP authentication (for more information see the Cyrus SASL saslauthd.conf man page)
#
# LDAP url for connection
#LDAP_URL=ldaps://ldap.domain.com/
# LDAP base DN. Can be empty
#LDAP_BASE=DC=example,DC=domain,DC=com
# LDAP user DN. Do not specify this parameter for the anonymous bind
#LDAP_BINDDN=CN=binduser,OU=users,DC=example,DC=domain,DC=com
# LDAP user password. Do not specify this parameter for the anonymous bind
#LDAP_BINDPW=LdapUserPassw0rd
# LDAP filter. Tokens example:
# %1-9 - if the input key is user@mail.domain.com, then %1 is com, %2 is domain and %3 is mail
# %s - %s is replaced by the complete service string
# %r - %r is replaced by the complete realm string
#LDAP_FILTER=(sAMAccountName=%u)
# LDAP authentication method
#LDAP_AUTH_METHOD=bind
# LDAP version
#LDAP_VERSION=3
# LDAP TLS using
#LDAP_USE_TLS=1
# List of SSL/TLS ciphers to allow
#LDAP_TLS_CIPHERS=SECURE256:SECURE128:!AES-128-CBC:!ARCFOUR-128:!CAMELLIA-128-CBC:!3DES-CBC:!CAMELLIA-128-CBC
# Require and verify server certificate
#LDAP_TLS_CHECK_PEER=1
# Path to CA cert file. Used when server certificate verify is enabled
#LDAP_TLS_CACERT_FILE=/etc/ssl/certs/ca-certificates.crt
# Path to CA certs directory. Used when server certificate verify is enabled
#LDAP_TLS_CACERT_DIR=/etc/ssl/certs
# Wether to use starttls, implies LDAPv3 and requires ldap:// instead of ldaps://
# LDAP_START_TLS=1
#
# Security
#
# Set these to strong passwords to avoid intruders from impersonating a service account
# The service(s) won't start unless these are specified
# Running ./gen-passwords.sh will update .env with strong passwords
# You may skip the Jigasi and Jibri passwords if you are not using those
# DO NOT reuse passwords
#
# XMPP password for Jicofo client connections
JICOFO_AUTH_PASSWORD=67634b47f522b4d938a54de4db0ed304
# XMPP password for JVB client connections
JVB_AUTH_PASSWORD=d6af48b16a1528f6c1cdb6243613af01
# XMPP password for Jigasi MUC client connections
JIGASI_XMPP_PASSWORD=5aadcadbbb3e79dca5b86ac5f3c918bd
# XMPP recorder password for Jibri client connections
JIBRI_RECORDER_PASSWORD=e7b6eefa4cfe2a14a5541dbc72de9e7e
# XMPP password for Jibri client connections
JIBRI_XMPP_PASSWORD=b7b4754c0654cfa06b6e0e7d87b13951
#
# Docker Compose options
#
# Container restart policy
#RESTART_POLICY=unless-stopped
# Jitsi image version (useful for local development)
#JITSI_IMAGE_VERSION=latest
ENABLE_RECORDING=0
ENABLE_AUTO_OWNER=0
LOG_LEVEL=debug
- copied the file mod_token_affiliation.lua (prosody-plugins/mod_token_affiliation.lua at main · jitsi-contrib/prosody-plugins · GitHub) in .jitsi-meet-cfg\prosody\prosody-plugins-custom
local LOGLEVEL = "debug"
local is_admin = require "core.usermanager".is_admin
local is_healthcheck_room = module:require "util".is_healthcheck_room
module:log(LOGLEVEL, "loaded")
local function _is_admin(jid)
return is_admin(jid, module.host)
end
module:hook("muc-occupant-joined", function (event)
local room, occupant = event.room, event.occupant
if is_healthcheck_room(room.jid) or _is_admin(occupant.jid) then
module:log(LOGLEVEL, "skip affiliation, %s", occupant.jid)
return
end
if not event.origin.auth_token then
module:log(LOGLEVEL, "skip affiliation, no token")
return
end
local affiliation = "member"
local context_user = event.origin.jitsi_meet_context_user
if context_user then
if context_user["affiliation"] == "owner" then
affiliation = "owner"
elseif context_user["affiliation"] == "moderator" then
affiliation = "owner"
elseif context_user["affiliation"] == "teacher" then
affiliation = "owner"
elseif context_user["moderator"] == "true" then
affiliation = "owner"
elseif context_user["moderator"] == true then
affiliation = "owner"
end
end
module:log(LOGLEVEL, "affiliation: %s", affiliation)
room:set_affiliation(true, occupant.bare_jid, affiliation)
end)
- modified in prosody container the existing file /defaults/conf.d/jitsi-meet.cfg.lua (I appended the following lines)
Component "conference.{{ $XMPP_DOMAIN }}" "muc"
modules_enabled = {
"token_verification";
"token_affiliation";
}
-
launched docker-compose up
-
verified that plugin in enable in prosody container: /config/conf.d/jitsi-meet.cfg.lua
admins = {
"jigasi@auth.meet.jitsi",
"jibri@auth.meet.jitsi",
"focus@auth.meet.jitsi",
"jvb@auth.meet.jitsi"
}
unlimited_jids = {
"focus@auth.meet.jitsi",
"jvb@auth.meet.jitsi"
}
plugin_paths = { "/prosody-plugins/", "/prosody-plugins-custom" }
muc_mapper_domain_base = "meet.jitsi";
muc_mapper_domain_prefix = "muc";
http_default_host = "meet.jitsi"
consider_bosh_secure = true;
consider_websocket_secure = true;
VirtualHost "meet.jitsi"
authentication = "token"
app_id = "my_jitsi_app_id"
app_secret = "my_jitsi_app_secret"
allow_empty_token = false
enable_domain_verification = false
ssl = {
key = "/config/certs/meet.jitsi.key";
certificate = "/config/certs/meet.jitsi.crt";
}
modules_enabled = {
"bosh";
"websocket";
"smacks"; -- XEP-0198: Stream Management
"pubsub";
"ping";
"speakerstats";
"conference_duration";
"room_metadata";
"end_conference";
"muc_lobby_rooms";
"muc_breakout_rooms";
"av_moderation";
}
main_muc = "muc.meet.jitsi"
lobby_muc = "lobby.meet.jitsi"
breakout_rooms_muc = "breakout.meet.jitsi"
speakerstats_component = "speakerstats.meet.jitsi"
conference_duration_component = "conferenceduration.meet.jitsi"
end_conference_component = "endconference.meet.jitsi"
av_moderation_component = "avmoderation.meet.jitsi"
c2s_require_encryption = false
VirtualHost "auth.meet.jitsi"
ssl = {
key = "/config/certs/auth.meet.jitsi.key";
certificate = "/config/certs/auth.meet.jitsi.crt";
}
modules_enabled = {
"limits_exception";
}
authentication = "internal_hashed"
Component "internal-muc.meet.jitsi" "muc"
storage = "memory"
modules_enabled = {
"ping";
}
restrict_room_creation = true
muc_room_locking = false
muc_room_default_public_jids = true
Component "muc.meet.jitsi" "muc"
storage = "memory"
modules_enabled = {
"muc_meeting_id";
"token_verification";
"polls";
"muc_domain_mapper";
}
muc_room_cache_size = 1000
muc_room_locking = false
muc_room_default_public_jids = true
Component "focus.meet.jitsi" "client_proxy"
target_address = "focus@auth.meet.jitsi"
Component "speakerstats.meet.jitsi" "speakerstats_component"
muc_component = "muc.meet.jitsi"
Component "conferenceduration.meet.jitsi" "conference_duration_component"
muc_component = "muc.meet.jitsi"
Component "endconference.meet.jitsi" "end_conference"
muc_component = "muc.meet.jitsi"
Component "avmoderation.meet.jitsi" "av_moderation_component"
muc_component = "muc.meet.jitsi"
Component "lobby.meet.jitsi" "muc"
storage = "memory"
restrict_room_creation = true
muc_room_locking = false
muc_room_default_public_jids = true
Component "breakout.meet.jitsi" "muc"
storage = "memory"
restrict_room_creation = true
muc_room_locking = false
muc_room_default_public_jids = true
modules_enabled = {
"muc_meeting_id";
"muc_domain_mapper";
"polls";
}
Component "metadata.meet.jitsi" "room_metadata_component"
muc_component = "muc.meet.jitsi"
breakout_rooms_component = "breakout.meet.jitsi"
Component "conference.meet.jitsi" "muc"
modules_enabled = {
"token_verification";
"token_affiliation";
}
- verified that the prosody affiliation plugin gets loaded:
2023-05-25 11:19:33 conference.meet.jitsi:token_affiliation debug loaded
- verified that “jicofo.conference.enable-auto-owner” is false in /config/jicofo.conf
jicofo {
authentication {
enabled = true
// The type of authentication. Supported values are XMPP, JWT or SHIBBOLETH (default).
type = JWT
login-url = "meet.jitsi"
enable-auto-login=true
}
// Configuration related to jitsi-videobridge
bridge {
brewery-jid = "jvbbrewery@internal-muc.meet.jitsi"
}
// Configure the codecs and RTP extensions to be used in the offer sent to clients.
codec {
video {
}
}
conference {
enable-auto-owner = false
}
octo {
// Whether or not to use Octo. Note that when enabled, its use will be determined by
// $jicofo.bridge.selection-strategy. There's a corresponding flag in the JVB and these
// two MUST be in sync (otherwise bridges will crash because they won't know how to
// deal with octo channels).
enabled = false
}
sctp {
enabled = false
}
xmpp {
client {
enabled = true
hostname = "xmpp.meet.jitsi"
port = "5222"
domain = "auth.meet.jitsi"
xmpp-domain = "meet.jitsi"
username = "focus"
password = "67634b47f522b4d938a54de4db0ed304"
conference-muc-jid = "muc.meet.jitsi"
client-proxy = "focus.meet.jitsi"
disable-certificate-verification = true
}
}
}
- moderator connects with the payload:
{
"aud": "my_jitsi_app_id",
"iss": "my_jitsi_app_id",
"sub": "meet.jitsi",
"iat": 1601366000,
"exp": 1905930115,
"room": "*",
"context": {
"user": {
"name": "mymoderator",
"email": "myname@mydomain.com",
"affiliation": "owner"
}
}
}
Here is the complete file:
<!DOCTYPE html>
<html>
<head>
<script src='https://luigi.cloudz.it/external_api.js'></script>
<style>
html,
body,
#jaas-container {
height: 100%;
}
</style>
</head>
<body>
<div id="jaas-container" />
<script type="text/javascript">
const domain = 'luigi.cloudz.it';
window.onload = () => {
const api = new JitsiMeetExternalAPI(domain, {
roomName: "room1",
parentNode: document.querySelector('#jaas-container'),
jwt: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJteV9qaXRzaV9hcHBfaWQiLCJpc3MiOiJteV9qaXRzaV9hcHBfaWQiLCJzdWIiOiJtZWV0LmppdHNpIiwiaWF0IjoxNjAxMzY2MDAwLCJleHAiOjE5MDU5MzAxMTUsInJvb20iOiIqIiwiY29udGV4dCI6eyJ1c2VyIjp7Im5hbWUiOiJteW1vZGVyYXRvciIsImVtYWlsIjoibXluYW1lQG15ZG9tYWluLmNvbSIsImFmZmlsaWF0aW9uIjoib3duZXIifX19.RCt1Xclxw-8U9wVeDFh0hXbYsPVSUUlEkaznLnjIQiQ"
})
}
</script>
</body>
</html>
- user connects with the following payload:
{
"aud": "my_jitsi_app_id",
"iss": "my_jitsi_app_id",
"sub": "meet.jitsi",
"iat": 1601366000,
"exp": 1905930115,
"room": "*",
"context": {
"user": {
"name": "myuser",
"email": "myuser@mydomain.com",
"affiliation": "member"
}
}
}
here is the complete file:
<!DOCTYPE html>
<html>
<head>
<script src='https://luigi.cloudz.it/external_api.js'></script>
<style>
html,
body,
#jaas-container {
height: 100%;
}
</style>
</head>
<body>
<div id="jaas-container" />
<script type="text/javascript">
const domain = 'luigi.cloudz.it';
window.onload = () => {
const api = new JitsiMeetExternalAPI(domain, {
roomName: "room1",
parentNode: document.querySelector('#jaas-container'),
jwt: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJteV9qaXRzaV9hcHBfaWQiLCJpc3MiOiJteV9qaXRzaV9hcHBfaWQiLCJzdWIiOiJtZWV0LmppdHNpIiwiaWF0IjoxNjAxMzY2MDAwLCJleHAiOjE5MDU5MzAxMTUsInJvb20iOiIqIiwiY29udGV4dCI6eyJ1c2VyIjp7Im5hbWUiOiJteXVzZXIiLCJlbWFpbCI6Im15dXNlckBteWRvbWFpbi5jb20iLCJhZmZpbGlhdGlvbiI6Im1lbWJlciJ9fX0.Pk2zQZL9WGRf4El72zZoZLBC6-XRkp79mavu91Hyhwo"
})
}
</script>
</body>
</html>
I verify that both are moderators.