JWT failures almost always come down to one claim not matching one setting. Decode the token (the middle part is base64url JSON) and check each row:
| Claim | Must match | Typical mistake |
|---|---|---|
iss |
JWT_APP_ID / JWT_ACCEPTED_ISSUERS |
A different string in dev and prod |
aud |
JWT_APP_ID / JWT_ACCEPTED_AUDIENCES |
Set to jitsi from an old tutorial |
sub |
The public domain, e.g. meet.example.com |
Using the internal meet.jitsi |
room |
The room in the URL, or * |
Case differs, or URL-encoded characters |
exp |
Later than the server’s current time | Seconds vs milliseconds, clock skew |
| signature | Signed with JWT_APP_SECRET, HS256 |
Secret has a trailing newline or space |
The token is ignored and a login dialog appears
- The server is not in JWT mode. Check
.envhasENABLE_AUTH=1andAUTH_TYPE=jwt, then recreate (not just restart) the containers:docker compose up -d --force-recreate. - The token is not in the URL. It must be
?jwt=<token>on the room URL, or thejwtoption in the IFrame API.
“Not allowed” or immediate disconnect
One of the claims in the table is wrong. The Prosody log names it:
docker compose logs --tail 100 prosody | grep -i token
Tokens work for a while, then fail
expis too short, or set in milliseconds (a date far in the future is suspicious, a date in 1970 is milliseconds divided wrongly).- The server clock drifts. Check
timedatectland keep NTP enabled.
Works for one room, not others
The token’s room is a specific name. Use * for any room, or mint one token per room. Keep room names lowercase in both the URL and the token.
Guests cannot join, or can join without a token
That is ENABLE_GUESTS. 1 lets people without a token join rooms that a token holder has started; 0 requires a token for everyone. Recreate the containers after changing it.
Everyone gets moderator rights
Authenticated users are moderators by default. Whether context.user.moderator: false demotes someone depends on your release and Prosody modules. Test with a host token and a guest token in two browsers before relying on it.
Still failing?
Send us a token with the signature removed (never the secret) and the Prosody log lines, and we will spot the mismatch. We also build Jitsi JWT integrations end to end.