JWT Authentication: Invalid json


#1

Hi there,

I’m trying to configure a Jitsi Meet setup to use JWT authentication; I’ve followed the guide for setting up and everything appears to be installed and configured correctly (prosody-trunk build 747, jitsi-meet-tokens installs etc.), however prosody isn’t accepting the tokens that I create, reporting an error as follows:

general warn    Error verifying token err:not-allowed, reason:Invalid json

This would suggest that the JSON form of my token is in some way malformed, or is not encoded properly, but if I decode it myself it all seems correct to me.

I’m generating my tokens in PHP, and while the class I’m using to do-so is fairly simple, so is the JWT standard; it’s just a case of creating JSON (json_encode of an object in PHP) then encoding as base64 without any padding.

The JWT token verifies correctly using the debugger at jwt.io, here’s a sample URL with a token that I’ve generated:

https://chat.uprospr.com/MyCompanyCabeoBathgate?jwt="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIqIiwiaXNzIjoidXByb3NwciIsInN1YiI6InVwcm9zcHIuY29tIiwicm9vbSI6Ik15Q29tcGFueUNhYmVvQmF0aGdhdGUifQ.vPotxB4FRVpJZNF_i_UyS3M-HSJVBvUqWNve-PSh_yo"

That’s as simple as I could make it, yet prosody still complains that it is invalid JSON; anyone have any ideas why that might be?

I’m using HS256 (SHA256) as the signing algorithm.


#2

Your token is not valid I think.
Check that iss int the token is the same value as the one setup in your prosody config as app_id. The value of sub should be chat.uprospr.com looking at your config. Changing that, does it work?
But looking at the reason Invalid json … are you using the same secret to encode the token?


#3

Check that iss int the token is the same value as the one setup in your prosody config as app_id .

These are definitely the same.

The value of sub should be chat.uprospr.com looking at your config. Changing that, does it work?

Unfortunately not; I used uprospr.com as the guide seemed to imply the sub-domain part wasn’t required:

'sub' contains the name of the domain used when authenticating with this token. By default assuming that we have full MUC 'conference1@muc.server.net' then 'server.net' should be used here.

But changing it makes no difference.

But looking at the reason Invalid json … are you using the same secret to encode the token?

Yup, I’ve tried using a much shorter secret as well in case there was some kind of limit but no difference either.

Does prosody require the JSON to have any specific amount of whitespace or such? It looks like PHP’s json_encode is compact by default (no new-lines, indentation etc.).

In case it helps you any, here’s a variant of the token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIqIiwiaXNzIjoidXByb3NwciIsInN1YiI6ImNoYXQudXByb3Nwci5jb20iLCJyb29tIjoiTXlDb21wYW55Q2FiZW9CYXRoZ2F0ZSJ9.-4u64dx4JThu-8iczuwA5q67FAOvUwasNUQ_MjvS7qg

This one was encoded with the secret 80385194037914f which I’ve used for testing, the production secret is longer.


#4

So I’m still struggling to figure out why the token is producing an “invalid json” error; I’ve tried searching the prosody source code but can’t even find where the error might be originating.

There’s a util/json.lua file that seems like the obvious place for JSON decoding, but it’s not exactly the most readable code (and I’m not especially familiar with Lua to begin with) but there’s nothing obvious that stands out that should fail for my tokens; it seems to ignore whitespace, handle strings properly, and none of my strings contain anything strange.

But it also doesn’t contain an “invalid json” error anywhere, nor does any file in the source that I can find, could it be somewhere else?

From what I can find it looks like if the error were with any of my values then I’d be getting a more specific error about an invalid audience, issuer etc., so the fact that it’s invalid json means the error must be occurring earlier, I just can’t figure out where. I feel like if I could find where the exact error is coming from I might be able to figure out what’s going on, e.g- maybe it’s not being decoded from base64 properly first?

Can anyone point me in the right direction? I just can’t seem to find the right error message anywhere.


#5

Did you compare the tokens produced by your script and if you use the same token on jwt.io.
I suppose the error you see is coming from luajwtjitsi.
https://github.com/jitsi/jitsi-meet/blob/master/debian/jitsi-meet-tokens.postinst#L63


#6

Did you compare the tokens produced by your script and if you use the same token on jwt.io.

Yep, the tokens I’ve provided all decode correctly, and I’ve provided one with its secret so you can see the signature is valid as well.

I suppose the error you see is coming from luajwtjitsi.

Where can I find the source code for luajwtjitsi? The file you linked to just appears to be where the luajwtjitsi install is triggered, but this seems to have succeeded (or else I would have received an error during installation, but installation went smoothly for me).


#7

https://luarocks.org/modules/pawelgawel88/luajwtjitsi/1.3-7


#8

Thanks!

So I can’t find anything wrong with luajwtjitsi itself, if I pass tokens into it manually it decodes them correctly without issue, for example:

local jwt = require "luajwtjitsi"
local key = "80385194037914f"
local token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIqIiwiaXNzIjoidXByb3NwciIsInN1YiI6ImNoYXQudXByb3Nwci5jb20iLCJyb$
local decoded, err = jwt.decode(token, key, true)
if err then
        print(err)
else
        for key,value in pairs(decoded) do
                print(key, value)
        end
end

Running this prints:

aud     *
iss     uprospr
room    MyCompanyCabeoBathgate
sub     chat.uprospr.com

Which is entirely correct.

So why are my tokens returning “invalid json” in prosody when I try to use them with Jitsi? That seems to suggest that the token being passed into luajwtjitsi isn’t what I add to my URLs, because what I’m adding is valid JWT, quoted as the documentation says to:

https://github.com/jitsi/lib-jitsi-meet/blob/master/doc/tokens.md

Here’s my /etc/prosody/prosody.cfg.lua (trimmed down a bit):

admins = { }
use_libevent = true
plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
modules_enabled = {
                "roster"; 
                "saslauth"; 
                "tls"; 
                "dialback"; 
                "disco"; 
                "private"; 
                "vcard"; 
                "version"; 
                "uptime"; 
                "time"; 
                "ping"; 
                "pep"; 
                "register"; 
                "admin_adhoc"; 
}
modules_disabled = {}
allow_registration = false
ssl = {
        key = "/etc/prosody/certs/localhost.key";
        certificate = "/etc/prosody/certs/localhost.crt";
}
c2s_require_encryption = false
s2s_secure_auth = false
pidfile = "/var/run/prosody/prosody.pid"
authentication = "internal_plain"
log = {
        info = "/var/log/prosody/prosody.log"; 
        error = "/var/log/prosody/prosody.err";
        "*syslog";
}
VirtualHost "localhost"
VirtualHost "example.com"
        enabled = false 
        ssl = {
                key = "/etc/prosody/certs/example.com.key";
                certificate = "/etc/prosody/certs/example.com.crt";
        }
Include "conf.d/*.cfg.lua"

And here’s my config for /etc/prosody/conf.d/chat.uprospr.com.cfg.lua:

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

VirtualHost "chat.uprospr.com"
        -- 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="uprospr"
        app_secret="snip";
        allow_empty_token = true;       -- Disallow this for production
        -- 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/chat.uprospr.com.key";
                certificate = "/etc/prosody/certs/chat.uprospr.com.crt";
        }
        -- we need bosh
        modules_enabled = {
            "bosh";
            "pubsub";
            "ping"; -- Enable mod_ping
        }

        c2s_require_encryption = false

Component "conference.chat.uprospr.com" "muc"
    storage = "internal"
    modules_enabled = { "token_verification" }
admins = { "focus@auth.chat.uprospr.com" }

Component "jitsi-videobridge.chat.uprospr.com"
    component_secret = "snip"

VirtualHost "auth.chat.uprospr.com"
    ssl = {
        key = "/etc/prosody/certs/auth.chat.uprospr.com.key";
        certificate = "/etc/prosody/certs/auth.chat.uprospr.com.crt";
    }
    authentication = "internal_plain"

Component "focus.chat.uprospr.com"
    component_secret = "snip"

I’ve removed all secrets but otherwise this is exactly as I’m using.

All of this seems correct to me, yet somehow my JWT tokens are not being passed correctly to luajwtjitsi by either Jitsi itself or by prosody. Any ideas what’s happening here?


#9

The easiest way for debugging this is to modify prosody modules adding some prints and see are your assumptions correct.
Here is where token is extracted from the URL params:


You can add something like:

log("info", "My token:%s", session.auth_token);

Restart prosody, test again and check the prosody logs.


#10

You can also add some prints around the code that passes the token to the library:


#11

Actually have you tried without the quotes?


#12

Actually have you tried without the quotes?

Yeah, I thought the inclusion of quotes in the guide was weird, but when I remove them prosody behaves as if no token was provided at all.

I’ll try to add debug statements sometime today if I can, thanks!


#13

Hey, I’ve been having this same issue, and I figured it out thanks to this thread. I hope you have the same issue and this helps. I tried those debug statements, and with the quotes, it does in fact pass through the token as if it had quotes:

general info    My token:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJqaXRzaSIsImlzcyI6ImVtZWRsZXktaml0c2kiLCJzdWIiOiJqaXRzaS5lbWVkbGV5LmNvbSIsInJvb20iOiIqIn0.3DpaETYr1N9r9njm5ERCjy_LmpPGO26tcdKbV5KBTHA"

vs

general info    My token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJqaXRzaSIsImlzcyI6ImVtZWRsZXktaml0c2kiLCJzdWIiOiJqaXRzaS5lbWVkbGV5LmNvbSIsInJvb20iOiIqIn0.3DpaETYr1N9r9njm5ERCjy_LmpPGO26tcdKbV5KBTHA

It authenticates with the second, but then I had an error related to storage. I fixed that by setting storage = "internal" in the conference component in /etc/prosody/conf.d/mydomain.com.cfg.lua. I hope that helps, and at least it tells you that you should be providing the token without the quotes. The setup docs should be updated to address that, since it seems to be just wrong.


#14

@eward Thank you for your patience. And sorry for the trouble.


#15

Thanks! And no problem, glad to help improve the docs.


#16

It authenticates with the second, but then I had an error related to storage. I fixed that by setting storage = "internal" in the conference component in /etc/prosody/conf.d/mydomain.com.cfg.lua . I hope that helps, and at least it tells you that you should be providing the token without the quotes. The setup docs should be updated to address that, since it seems to be just wrong.

I believe that was it!

I also encountered some issues that seem to be related to my attempting to pass data for users (in the context object) but for users who’ve already connected to the server before and set a custom name.

I believe I’ve cleared out all traces of user data and fingers crossed it seems to be behaving itself now.

Thanks both of you for responding!