Can't get JWT authentication working

Hi, please help me understand what I did wrong in configuring JWT authentication. I was able to get Jitsi up & running without authentication, and with letsencrypt SSL using the installer, on Ubuntu 16.04 and everything worked fine with public rooms at https://conference.leadpods.com . Now I’m trying to get JWT authentication working, and I’m having issues. I installed prosody-trunk and jitsi-meet-tokens and modified the config file per instructions at https://github.com/jitsi/lib-jitsi-meet/blob/master/doc/tokens.md . I went to https://jwt.io/ and created a token as follows:

Header:
{
“alg”: “HS256”,
“typ”: “JWT”
}

Payload:
{
“sub”: “conference.leadpods.com”,
“iss”: “my_app_id”,
“room”: “dennis1”
}

Verify Signature:
HMACSHA256(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
my_secret_code
)

Although not listed in the instructions, I uncommented the bosh line in prosody.cfg.lua:
– HTTP modules
“bosh”; – Enable BOSH clients, aka “Jabber over HTTP”
–“websocket”; – XMPP over WebSockets

I set
c2s_require_encryption=false

I tried adding this:
consider_bosh_secure = true

I tried adding tis line for the letsencrypt certificate:
https_certificate = “/etc/letsencrypt/live/conference.leadpods.com/fullchain.pem”

My virtual host section looks like this:

VirtualHost “conference.leadpods.com
authentication = “token”;
app_id = “my_app_id”;
app_secret = “my_secret”;
allow_empty_token = false;
certificate = “/etc/letsencrypt/live/conference.leadpods.com/fullchain.pem”

I added these lines at the end:

Component “conference.leadpods.com” “muc”
modules_enabled = { “token_verification” }

Include “conf.d/*.cfg.lua”

I try a link like this in Chrome:
https://conference.leadpods.com/dennis1?jwt=aaaaaa.bbbbbb.ccccc-ccccc

I get a gray screen followed by a message I’ve been disconnected & a countdown to retry in X seconds. In console, I see these errors:

strophe.js:5662 POST https://conference.leadpods.com/http-bind?room=dennis1&token=aaaaaa.bbbbbb.ccccc-ccccc 502 (Bad Gateway)
l @ strophe.js:5662
_processRequest @ strophe.js:5677
_throttledRequestHandler @ strophe.js:5823
_connect @ strophe.js:5170
connect @ strophe.js:3051
value @ xmpp.js:309
value @ xmpp.js:369
c.connect @ JitsiConnection.js:61
e @ connection.js:38
(anonymous) @ connection.js:149
s @ connection.js:86
c @ connection.js:179
H @ conference.js:151
createInitialLocalTracksAndConnect @ conference.js:677
init @ conference.js:716
(anonymous) @ actions.web.js:28
(anonymous) @ index.js:11
(anonymous) @ middleware.js:41
(anonymous) @ middleware.js:27
(anonymous) @ middleware.js:13
(anonymous) @ middleware.js:21
(anonymous) @ middleware.js:22
(anonymous) @ middleware.js:18
(anonymous) @ middleware.js:67
(anonymous) @ middleware.js:43
(anonymous) @ middleware.web.js:36
(anonymous) @ middleware.any.js:55
(anonymous) @ middleware.js:29
(anonymous) @ middleware.js:99
(anonymous) @ middleware.js:17
(anonymous) @ middleware.js:30
(anonymous) @ middleware.js:20
(anonymous) @ middleware.js:39
(anonymous) @ middleware.js:12
(anonymous) @ middleware.js:29
(anonymous) @ middleware.js:25
(anonymous) @ middleware.web.js:23
(anonymous) @ middleware.any.js:94
(anonymous) @ middleware.js:65
(anonymous) @ middleware.js:33
(anonymous) @ middleware.js:25
(anonymous) @ middleware.js:29
(anonymous) @ middleware.js:44
(anonymous) @ middleware.js:100
(anonymous) @ middleware.js:62
(anonymous) @ middleware.js:24
(anonymous) @ middleware.js:42
(anonymous) @ middleware.js:38
(anonymous) @ middleware.js:27
(anonymous) @ middleware.js:23
(anonymous) @ middleware.js:25
(anonymous) @ middleware.js:45
(anonymous) @ middleware.js:42
(anonymous) @ middleware.js:18
(anonymous) @ middleware.js:130
(anonymous) @ middleware.js:126
value @ Conference.web.js:273
value @ Conference.web.js:155
Na @ react-dom.production.min.js:228
ja @ react-dom.production.min.js:220
Pa @ react-dom.production.min.js:219
Oa @ react-dom.production.min.js:216
Zi @ react-dom.production.min.js:214
enqueueSetState @ react-dom.production.min.js:134
w.setState @ react.production.min.js:13
n @ I18n.js:135
t @ I18n.js:147
(anonymous) @ EventEmitter.js:46
e.emit @ EventEmitter.js:45
(anonymous) @ i18next.js:162
(anonymous) @ i18next.js:265
(anonymous) @ i18next.js:281
t.load @ BackendConnector.js:183
(anonymous) @ i18next.js:215
t.load @ CacheConnector.js:40
t.loadResources @ i18next.js:214
r @ i18next.js:280
t.changeLanguage @ i18next.js:286
s @ i18next.js:159
setTimeout (async)
t.init @ i18next.js:171
(anonymous) @ i18next.js:68
n @ bootstrap:19
(anonymous) @ actionTypes.js:21
n @ bootstrap:19
(anonymous) @ actionTypes.js:10
n @ bootstrap:19
(anonymous) @ actions.js:265
(anonymous) @ functions.js:97
n @ bootstrap:19
(anonymous) @ AnalyticsEvents.js:630
n @ bootstrap:19
(anonymous) @ index.js:1
(anonymous) @ actions.js:714
n @ bootstrap:19
(anonymous) @ index.js:67
n @ bootstrap:19
(anonymous) @ actions.js:5
(anonymous) @ actions.js:265
n @ bootstrap:19
(anonymous) @ index.js:3
n @ bootstrap:19
(anonymous) @ actions.js:20
n @ bootstrap:19
(anonymous) @ possibleConstructorReturn.js:16
n @ bootstrap:19
(anonymous) @ actions.js:17
n @ bootstrap:19
(anonymous) @ AuthHandler.js:1
(anonymous) @ AuthHandler.js:229
n @ bootstrap:19
(anonymous) @ connection.js:1
(anonymous) @ connection.js:189
n @ bootstrap:19
(anonymous) @ Tooltip.js:289
(anonymous) @ conference.js:2723
n @ bootstrap:19
(anonymous) @ withAnalyticsEvents.js:111
n @ bootstrap:19
(anonymous) @ bootstrap:83
(anonymous) @ bootstrap:83
Logger.js:125 [JitsiMeetJS.js] <Object.getGlobalOnErrorHandler>: UnhandledError: null Script: null Line: null Column: null StackTrace: Error: Strophe: request id 1.1 error 502 happened
at Object.i.Strophe.log (strophe.util.js:89)
at Object.error (strophe.js:2083)
at e.Bosh._onRequestStateChange (strophe.js:5565)
i @ Logger.js:125
getGlobalOnErrorHandler @ JitsiMeetJS.js:535
window.onerror @ middleware.js:100
callErrorHandler @ GlobalOnErrorHandler.js:61
i.Strophe.log @ strophe.util.js:89
error @ strophe.js:2083
_onRequestStateChange @ strophe.js:5565
XMLHttpRequest.send (async)
l @ strophe.js:5662
_processRequest @ strophe.js:5677
_throttledRequestHandler @ strophe.js:5823
_connect @ strophe.js:5170
connect @ strophe.js:3051
value @ xmpp.js:309
value @ xmpp.js:369
c.connect @ JitsiConnection.js:61
e @ connection.js:38
(anonymous) @ connection.js:149
s @ connection.js:86
c @ connection.js:179
H @ conference.js:151
createInitialLocalTracksAndConnect @ conference.js:677
init @ conference.js:716
(anonymous) @ actions.web.js:28
(anonymous) @ index.js:11
(anonymous) @ middleware.js:41
(anonymous) @ middleware.js:27
(anonymous) @ middleware.js:13
(anonymous) @ middleware.js:21
(anonymous) @ middleware.js:22
(anonymous) @ middleware.js:18
(anonymous) @ middleware.js:67
(anonymous) @ middleware.js:43
(anonymous) @ middleware.web.js:36
(anonymous) @ middleware.any.js:55
(anonymous) @ middleware.js:29
(anonymous) @ middleware.js:99
(anonymous) @ middleware.js:17
(anonymous) @ middleware.js:30
(anonymous) @ middleware.js:20
(anonymous) @ middleware.js:39
(anonymous) @ middleware.js:12
(anonymous) @ middleware.js:29
(anonymous) @ middleware.js:25
(anonymous) @ middleware.web.js:23
(anonymous) @ middleware.any.js:94
(anonymous) @ middleware.js:65
(anonymous) @ middleware.js:33
(anonymous) @ middleware.js:25
(anonymous) @ middleware.js:29
(anonymous) @ middleware.js:44
(anonymous) @ middleware.js:100
(anonymous) @ middleware.js:62
(anonymous) @ middleware.js:24
(anonymous) @ middleware.js:42
(anonymous) @ middleware.js:38
(anonymous) @ middleware.js:27
(anonymous) @ middleware.js:23
(anonymous) @ middleware.js:25
(anonymous) @ middleware.js:45
(anonymous) @ middleware.js:42
(anonymous) @ middleware.js:18
(anonymous) @ middleware.js:130
(anonymous) @ middleware.js:126
value @ Conference.web.js:273
value @ Conference.web.js:155
Na @ react-dom.production.min.js:228
ja @ react-dom.production.min.js:220
Pa @ react-dom.production.min.js:219
Oa @ react-dom.production.min.js:216
Zi @ react-dom.production.min.js:214
enqueueSetState @ react-dom.production.min.js:134
w.setState @ react.production.min.js:13
n @ I18n.js:135
t @ I18n.js:147
(anonymous) @ EventEmitter.js:46
e.emit @ EventEmitter.js:45
(anonymous) @ i18next.js:162
(anonymous) @ i18next.js:265
(anonymous) @ i18next.js:281
t.load @ BackendConnector.js:183
(anonymous) @ i18next.js:215
t.load @ CacheConnector.js:40
t.loadResources @ i18next.js:214
r @ i18next.js:280
t.changeLanguage @ i18next.js:286
s @ i18next.js:159
setTimeout (async)
t.init @ i18next.js:171
(anonymous) @ i18next.js:68
n @ bootstrap:19
(anonymous) @ actionTypes.js:21
n @ bootstrap:19
(anonymous) @ actionTypes.js:10
n @ bootstrap:19
(anonymous) @ actions.js:265
(anonymous) @ functions.js:97
n @ bootstrap:19
(anonymous) @ AnalyticsEvents.js:630
n @ bootstrap:19
(anonymous) @ index.js:1
(anonymous) @ actions.js:714
n @ bootstrap:19
(anonymous) @ index.js:67
n @ bootstrap:19
(anonymous) @ actions.js:5
(anonymous) @ actions.js:265
n @ bootstrap:19
(anonymous) @ index.js:3
n @ bootstrap:19
(anonymous) @ actions.js:20
n @ bootstrap:19
(anonymous) @ possibleConstructorReturn.js:16
n @ bootstrap:19
(anonymous) @ actions.js:17
n @ bootstrap:19
(anonymous) @ AuthHandler.js:1
(anonymous) @ AuthHandler.js:229
n @ bootstrap:19
(anonymous) @ connection.js:1
(anonymous) @ connection.js:189
n @ bootstrap:19
(anonymous) @ Tooltip.js:289
(anonymous) @ conference.js:2723
n @ bootstrap:19
(anonymous) @ withAnalyticsEvents.js:111
n @ bootstrap:19
(anonymous) @ bootstrap:83
(anonymous) @ bootstrap:83
Logger.js:125 [modules/xmpp/strophe.util.js] <Object.i.Strophe.log>: Strophe: request id 1.1 error 502 happened
i @ Logger.js:125
i.Strophe.log @ strophe.util.js:90
error @ strophe.js:2083
_onRequestStateChange @ strophe.js:5565
XMLHttpRequest.send (async)
l @ strophe.js:5662
_processRequest @ strophe.js:5677
_throttledRequestHandler @ strophe.js:5823
_connect @ strophe.js:5170
connect @ strophe.js:3051
value @ xmpp.js:309
value @ xmpp.js:369
c.connect @ JitsiConnection.js:61
e @ connection.js:38
(anonymous) @ connection.js:149
s @ connection.js:86
c @ connection.js:179
H @ conference.js:151
createInitialLocalTracksAndConnect @ conference.js:677
init @ conference.js:716
(anonymous) @ actions.web.js:28
(anonymous) @ index.js:11
(anonymous) @ middleware.js:41
(anonymous) @ middleware.js:27
(anonymous) @ middleware.js:13
(anonymous) @ middleware.js:21
(anonymous) @ middleware.js:22
(anonymous) @ middleware.js:18
(anonymous) @ middleware.js:67
(anonymous) @ middleware.js:43
(anonymous) @ middleware.web.js:36
(anonymous) @ middleware.any.js:55
(anonymous) @ middleware.js:29
(anonymous) @ middleware.js:99
(anonymous) @ middleware.js:17
(anonymous) @ middleware.js:30
(anonymous) @ middleware.js:20
(anonymous) @ middleware.js:39
(anonymous) @ middleware.js:12
(anonymous) @ middleware.js:29
(anonymous) @ middleware.js:25
(anonymous) @ middleware.web.js:23
(anonymous) @ middleware.any.js:94
(anonymous) @ middleware.js:65
(anonymous) @ middleware.js:33
(anonymous) @ middleware.js:25
(anonymous) @ middleware.js:29
(anonymous) @ middleware.js:44
(anonymous) @ middleware.js:100
(anonymous) @ middleware.js:62
(anonymous) @ middleware.js:24
(anonymous) @ middleware.js:42
(anonymous) @ middleware.js:38
(anonymous) @ middleware.js:27
(anonymous) @ middleware.js:23
(anonymous) @ middleware.js:25
(anonymous) @ middleware.js:45
(anonymous) @ middleware.js:42
(anonymous) @ middleware.js:18
(anonymous) @ middleware.js:130
(anonymous) @ middleware.js:126
value @ Conference.web.js:273
value @ Conference.web.js:155
Na @ react-dom.production.min.js:228
ja @ react-dom.production.min.js:220
Pa @ react-dom.production.min.js:219
Oa @ react-dom.production.min.js:216
Zi @ react-dom.production.min.js:214
enqueueSetState @ react-dom.production.min.js:134
w.setState @ react.production.min.js:13
n @ I18n.js:135
t @ I18n.js:147
(anonymous) @ EventEmitter.js:46
e.emit @ EventEmitter.js:45
(anonymous) @ i18next.js:162
(anonymous) @ i18next.js:265
(anonymous) @ i18next.js:281
t.load @ BackendConnector.js:183
(anonymous) @ i18next.js:215
t.load @ CacheConnector.js:40
t.loadResources @ i18next.js:214
r @ i18next.js:280
t.changeLanguage @ i18next.js:286
s @ i18next.js:159
setTimeout (async)
t.init @ i18next.js:171
(anonymous) @ i18next.js:68
n @ bootstrap:19
(anonymous) @ actionTypes.js:21
n @ bootstrap:19
(anonymous) @ actionTypes.js:10
n @ bootstrap:19
(anonymous) @ actions.js:265
(anonymous) @ functions.js:97
n @ bootstrap:19
(anonymous) @ AnalyticsEvents.js:630
n @ bootstrap:19
(anonymous) @ index.js:1
(anonymous) @ actions.js:714
n @ bootstrap:19
(anonymous) @ index.js:67
n @ bootstrap:19
(anonymous) @ actions.js:5
(anonymous) @ actions.js:265
n @ bootstrap:19
(anonymous) @ index.js:3
n @ bootstrap:19
(anonymous) @ actions.js:20
n @ bootstrap:19
(anonymous) @ possibleConstructorReturn.js:16
n @ bootstrap:19
(anonymous) @ actions.js:17
n @ bootstrap:19
(anonymous) @ AuthHandler.js:1
(anonymous) @ AuthHandler.js:229
n @ bootstrap:19
(anonymous) @ connection.js:1
(anonymous) @ connection.js:189
n @ bootstrap:19
(anonymous) @ Tooltip.js:289
(anonymous) @ conference.js:2723
n @ bootstrap:19
(anonymous) @ withAnalyticsEvents.js:111
n @ bootstrap:19
(anonymous) @ bootstrap:83
(anonymous) @ bootstrap:83
Logger.js:125 [modules/xmpp/strophe.util.js] <Object.i.Strophe.log>: Strophe: request errored, status: 502, number of errors: 1

Can you check prosody logs for errors and also which version of prosody are you using?
Try with trunk 747.

Thanks, I see this in prosody.log:

Jan 14 19:24:24 startup info Hello and welcome to Prosody version trunk nightly build 1054 (2019-01-10, d844e197eedf)
Jan 14 19:24:24 startup info Prosody is using the select backend for connection handling
Jan 14 19:24:24 socket warn server.lua, :5222: address already in use
Jan 14 19:24:24 portmanager error Failed to open server port 5222 on , check that Prosody or another XMPP server is not already running and using this port
Jan 14 19:24:24 socket warn server.lua, [::]:5222: address already in use
Jan 14 19:24:24 portmanager error Failed to open server port 5222 on ::, check that Prosody or another XMPP server is not already running and using this port
Jan 14 19:24:24 portmanager info Activated service ‘c2s’ on no ports
Jan 14 19:24:24 portmanager info Activated service ‘legacy_ssl’ on no ports
Jan 14 19:24:24 socket warn server.lua, [
]:5269: address already in use
Jan 14 19:24:24 portmanager error Failed to open server port 5269 on *, check that Prosody or another XMPP server is not already running and using this port
Jan 14 19:24:24 socket warn server.lua, [::]:5269: address already in use
Jan 14 19:24:24 portmanager error Failed to open server port 5269 on ::, check that Prosody or another XMPP server is not already running and using this port
Jan 14 19:24:24 portmanager info Activated service ‘s2s’ on no ports
Jan 14 19:24:24 mod_s2s warn s2s not listening on any ports, outgoing connections may fail
Jan 14 19:24:24 mod_posix error Danger, Will Robinson! Prosody doesn’t need to be run as root, so don’t do it!
Jan 14 19:24:24 mod_posix error For more information on running Prosody as root, see https://prosody.im/doc/root
Jan 14 19:24:24 startup info Shutting down: Refusing to run as root
Jan 14 19:24:24 mod_posix info Prosody is about to detach from the console, disabling further console output

I got it working. It helped a lot to look at the log file per your suggestion. I also found the command, “prosodyctl status” helpful. I needed to install a lua dependency and changed some things in the config file. I also added another subdomain auth.conference.leadpods.com with my hosting company - not sure if it was necessary, and ended up using another made-up subdomain, “auth2.conference.leadpods.com” in the muc line in config. One thing I did when creating the token was to set both iss & aud to my app ID - is this the right way to do it? Here’s everything I did on ubuntu 16.04 after doing a fresh install of jitsi meet with letsencrypt ssl - maybe it’ll help someone else who’s stuck (replace “smart quotes” with plain quotes):

wget https://packages.prosody.im/debian/pool/main/p/prosody-trunk/prosody-trunk_1nightly747-1~xenial_amd64.deb

sudo dpkg -i prosody-trunk_1nightly747-1~xenial_amd64.deb

apt-get install jitsi-meet-tokens

[in the window that opened, I added my custom application ID & secret code that I came up with]

pico /etc/prosody/prosody.cfg.lua

Uncommented the bosh line, to read like this:

“bosh”; – Enable BOSH clients, aka “Jabber over HTTP”

Changed this line:

c2s_require_encryption=true

To read:

c2s_require_encryption=false

Added these lines at end:

Include “conf.d/*.cfg.lua”

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

Component “auth2.conference.leadpods.com” “muc”

modules_enabled = { “token_verification” }

Added this section below the example virtual host:

VirtualHost “conference.leadpods.com

authentication = “token”;

app_id = “my_custom_app_id”; – application identifier

app_secret = “my_custom_app_secret”; – application secret known only to your token

– generator and the plugin

allow_empty_token = false; – tokens are verified only if they are supplied by the client

ssl = {

key = “/etc/letsencrypt/live/conference.leadpods.com/privkey.pem”;

certificate = “/etc/letsencrypt/live/conference.leadpods.com/fullchain.pem”;

}

Went to my hosting control panel & added the domain auth.conference.leadpods.com as an A record pointing to the IP address

Tried command:

prosodyctl status

and got an error:


Prosody was unable to find lua-bitops

This package can be obtained in the following ways:

Source: http://bitop.luajit.org/

Debian/Ubuntu: sudo apt-get install lua-bitop

luarocks: luarocks install luabitop

WebSocket support will not be available

More help can be found on our website, at https://prosody.im/doc/depends


Added lua-bitop per above instructions

Ran these commands without errors:

prosodyctl stop

prosodyctl start

Went to https://jwt.io/ and entered these values:

Header:

{
“alg”: “HS256”,
“typ”: “JWT”
}

Payload:

{

“sub”: “leadpods.com”,
“iss”: “my_custom_app_id”,
“room”: “dennis1”,
“aud”: “my_custom_app_id”

}

Verify Signature:

HMACSHA256(

base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
my_custom_secret_code

)

Tried this link successfully, with the token generated on the website:

https://conference.leadpods.com/dennis1?jwt=aaaa.bbbb.cccc