Start by finding where the request fails. A readable token is not necessarily valid, and a valid signature does not prove that its room or moderator permissions match your policy. Use the JWT setup guide for a new issuer; use this page for an existing failure. [S2][S3][S4][S5]
Source scope: reviewed 24 October 2026 against docker-jitsi-meet stable-11248-2, commit 74d3e6e, and Jitsi Meet 11248 source at f765042b. We tested the local inspection and log-filter examples with synthetic data. We have not reproduced every failure on a live server; this revision does not extend the older stable-11031 production baseline. [S1][S2][S3]
1. Identify the failing stage
| Symptom | Inspect next |
|---|---|
| Login prompt or no token seen | Correct server, complete token delivery and active authentication mode |
| Token rejected before room entry | Format, algorithm, signature, issuer, audience and time claims |
| Token validates but one room fails | Literal room, domain/tenant mapping and room admission policy |
| Guests enter unexpectedly or cannot enter | Anonymous domain, empty-token allowance and host/lobby policy |
| Participant becomes a moderator | Affiliation module and other owner policies |
| Call disconnects after joining | Establish whether auth failed; also investigate transport and other components |
These are diagnostic branches, not proof of a cause. The reviewed authentication and room modules perform different checks. A generic disconnect can require connection troubleshooting. [S2][S3][S4][S5]
Record the running component image references or package versions first using version inventory. A tutorial’s defaults do not establish your installed configuration. [S1]
2. Inspect a token locally without exposing its payload
Save the following as inspect_jitsi_jwt.py on your own machine. Run it as a file in a normal terminal; do not run it through a heredoc that also consumes standard input. The helper uses Python’s standard library and makes no network requests. It supports a compact three-segment token and has a 32 KiB inspection limit, which is a helper limit, not a Jitsi protocol limit. [S6][S7]
import base64
import binascii
import getpass
import json
import math
import re
import time
import warnings
def inspect_token(token, now=None):
token = token.strip()
parts = token.split(".")
if len(token) > 32768 or len(parts) != 3 or any(
not re.fullmatch(r"[A-Za-z0-9_-]+", part) for part in parts
):
raise ValueError("Unsupported token format")
def read_json(part):
raw = base64.b64decode(part + "=" * (-len(part) % 4),
altchars=b"-_", validate=True)
value = json.loads(raw.decode("utf-8"))
if not isinstance(value, dict):
raise ValueError("Expected JSON object")
return value
header, payload = read_json(parts[0]), read_json(parts[1])
now = time.time() if now is None else now
def time_state(name):
if name not in payload:
return "missing"
value = payload[name]
if type(value) not in (int, float):
return "invalid"
try:
if not math.isfinite(value):
return "invalid"
except OverflowError:
return "invalid"
if name == "exp":
return "expired" if value <= now else "future"
return "not-yet-valid" if value > now else "active"
alg = header.get("alg")
return {
"signature_verified": False,
"algorithm": alg if alg in ("HS256", "HS384", "HS512", "RS256", "RS384", "RS512") else "other",
"typ_is_JWT": header.get("typ") == "JWT",
"string_claims": {name: isinstance(payload.get(name), str)
for name in ("iss", "aud", "sub", "room")},
"room_is_wildcard": payload.get("room") == "*",
"subject_is_wildcard": payload.get("sub") == "*",
"exp_state": time_state("exp"),
"nbf_state": time_state("nbf"),
}
def main():
try:
with warnings.catch_warnings():
warnings.simplefilter("error", getpass.GetPassWarning)
token = getpass.getpass("JWT (hidden, local inspection): ")
print(json.dumps(inspect_token(token), indent=2))
except (ValueError, UnicodeError, binascii.Error,
getpass.GetPassWarning, EOFError):
raise SystemExit("Cannot inspect: check token format and use a terminal with hidden input.")
if __name__ == "__main__":
main()Run it from the directory where you saved the file: [S6]
python3 inspect_jitsi_jwt.pyOn Windows, use python inspect_jitsi_jwt.py if that is your Python launcher. The helper stops if hidden input is unavailable. Do not place the token in a shell argument or print it to work around a terminal problem. [S6]
The report intentionally omits claim values and user details. signature_verified: false means this helper has not checked the signature; future is a local time observation, not an acceptance result. Compare the actual issuer, audience, subject and room privately in your trusted backend and server configuration. Do not paste a real token into an online decoder. [S2][S3][S7]
3. Compare claims with the active configuration
| Field | Check | Correction to investigate |
|---|---|---|
alg and signature |
Configured algorithm and the intended signing key | Align issuer and verifier; do not accept an algorithm just because the token requests it |
iss |
Actual accepted issuer list | Correct environment or issuer configuration |
aud |
Actual accepted audience list | Use the scalar audience expected by this pinned verifier and the setup example |
sub |
Present; domain mapping when verification is enabled | Use the configured domain for the intended deployment |
room |
Intended room and literal/pattern mode | Issue a new authorized room-specific token |
exp |
Numeric Unix seconds and server time | Fix the issuer’s lifetime and clock; do not drop expiry to hide failure |
nbf, if used |
Numeric Unix seconds, not in the future when admission is intended | Correct the intended activation time or clock; do not automatically backdate tokens |
The reviewed JWT library compares issuer/audience claims with its configured lists; do not assume another library’s array or wildcard behavior transfers unchanged. It checks exp and nbf when those claims are present. A missing expiry is an issuer-policy problem even if a verifier permits it. Use the signing helpers to calculate a short lifetime after authorization. [S2][S3][S7]
Docker domain and room checks
The reviewed Docker template defaults domain verification off and XMPP_DOMAIN to meet.jitsi. Our setup guide explicitly enables domain verification. For a single-domain setup with that check on, use the actual main XMPP domain as sub; it can differ from the public meeting hostname. Tenant setups require their own mapping review. [S1][S2]
The pinned room utility lowercases literal room claims. Use one consistent room convention and verify the actual room/tenant mapping; do not diagnose every room failure as letter case. Pattern claims and older implementations need separate checks. [S2]
Do not use a wildcard test token as proof that your issuer works. It broadens access and can hide the exact mismatch being investigated. Issue a short-lived token through your backend for one disposable authorized room. Verify that it works there and is denied for a different room. Do not put a signing secret into a pasted command or bypass the application’s membership check. [S2][S7]
4. Read error categories without copying sensitive log lines
The pinned room verifier can log a full token on a rejected join. Its startup debug message can include the configured app secret. Other messages can include room or user information. Inspect logs privately and avoid turning on verbose production logging as a first response. Removing a signature from a JWT does not remove its payload. [S2][S4][S7]
For Docker, use the same project and Compose options as the running deployment. This pipeline prints only allowlisted constant phrases from the last 100 Prosody log lines, not the matching full lines. The grep expression is a local reporting filter; it does not alter the server. [S2][S3][S4][S8]
docker compose logs --no-color --tail 100 prosody 2>/dev/null |
grep -oF \
-e 'token required' \
-e 'Invalid signature' \
-e 'Invalid or incorrect alg' \
-e 'Not acceptable by exp' \
-e 'Not acceptable by nbf' \
-e "invalid 'iss' claim" \
-e "invalid 'aud' claim" \
-e 'room claim is missing' \
-e 'sub claim is missing' \
-e 'Room and token mismatched' |
sort -uNo output does not prove that authentication succeeded: there may be no matching recent entry, a different component/version, missing logs or a command failure. This intentionally suppresses command-error output; inspect command failures locally if needed, without posting raw logs. The filter is not a general log sanitizer. [S8]
Error-to-check reference
These are exact phrases or prefixes in the pinned source. Installed versions may differ. Do not infer one certain root cause from the generic not-allowed response. [S2][S3][S4]
| Phrase | Check next |
|---|---|
token required |
Token delivery and intended empty-token policy |
Invalid signature |
Same intended key, intact token and matching algorithm; distinguish malformed signature encoding from failed verification |
Invalid or incorrect alg |
Header algorithm versus configured verifier algorithm |
Not acceptable by exp |
Expiry, seconds units and issuer/server clocks |
Not acceptable by nbf |
Activation time and clocks |
invalid 'iss' claim / invalid 'aud' claim |
Accepted list and actual claim values |
room claim is missing / sub claim is missing |
Issuer payload and configured requirements |
Room and token mismatched |
Domain/room mapping, including active verification settings |
For missing-key or key-fetch errors, inspect the configured HS shared-secret or asymmetric key-service mode; they are different integrations. Do not copy RS256 settings into an HS256 setup or loosen accepted claims merely to suppress an error. [S2]
5. Token delivery, configuration changes and clocks
- Login prompt: confirm the correct server, a complete token in the
jwtIFrame API option or intended JWT URL parameter, and the active token authentication mode. Do not share the full URL. [S1][S2][S9] - Changed
.envbut no effect: preserve the existing project, environment files, profiles and overlays, then recreate changed services in a maintenance window.docker compose up -dapplies changed Compose configuration;restartalone does not. Do not regenerate internal service passwords. [S1][S10] - Time failures: compare the issuer and server clocks. Correct synchronization and issue a new authorized token with intended
exp/nbfvalues. Milliseconds can produce an excessively long expiry, so a future timestamp alone does not establish a sensible lifetime. [S3][S7] - Expiry after joining: test fresh join and reconnect separately. JWT expiry is not a guaranteed timer that ends an established meeting. Refresh tokens only after repeating application authorization. [S2][S3]
6. Guests and moderator permissions
ENABLE_GUESTS controls an anonymous guest domain; JWT_ALLOW_EMPTY controls empty-token acceptance on the token host. Also inspect custom allowlists and modules. The reviewed verifier has exceptions for administrative or allowlisted identities; ordinary participant acceptance needs its own test. [S1][S2][S4]
For the setup guide’s all-participants-token policy, verify both ENABLE_GUESTS=0 and JWT_ALLOW_EMPTY=0, then test a separate tokenless browser. If guests are intentional, test admission before and after host arrival and with the lobby enabled. Flags alone do not prove host-waiting behavior. [S1][S2][S11]
An accepted participant token does not establish correct role enforcement. Check the active token-affiliation module, its supported claims and other automatic owner policies. In the reviewed affiliation source, an owner-like affiliation can grant ownership even when a moderator field is false. Follow the moderator diagnostic checklist and test participant-first, host-rejoin and lobby cases separately. [S5]
Debian and Ubuntu package differences
Package installations do not read Docker .env inputs. Inspect the main Prosody virtual host, commonly /etc/prosody/conf.avail/meet.example.com.cfg.lua, and the web configuration, commonly /etc/jitsi/meet/meet.example.com-config.js. Replace the example with the actual host. Current upstream package guidance describes token authentication and conference modules with a different Jicofo arrangement from Docker; use it instead of transplanting Docker settings. [S11]
Use private local service logs and the installed package’s configuration. The same constant error categories may help, but versions and module wiring differ. A service restart is an operational change, so plan it after identifying a configuration fix rather than issuing it as a read-only diagnostic. [S3][S11]
Verify the correction and prevent repeats
After correcting the evidenced mismatch in a non-production test setup:
- Issue a token through the real authorized backend for one disposable room.
- Confirm the intended user enters that room and cannot use the token for another room.
- Test altered signatures, wrong issuer/audience and expired tokens on fresh connections.
- Check tokenless access, host/participant permissions and lobby behavior independently.
- Repeat fresh join and reconnect after token refresh, without granting broader access.
These are proposed acceptance checks, not results from your server. Keep a record of component versions, intended claim policy and sanitized error categories. Add issuer tests for the corrected failure so the next deployment does not silently reintroduce it. [S2][S3][S4][S5]
Open questions and maintenance
The local decoder and constant-only log filter were tested with synthetic tokens and logs. We checked error text and policy branches in pinned upstream source. No real token, credential, account or server configuration was changed or submitted, and no full Prosody admission, browser call, moderator or package test was completed for this revision.
Recheck after JWT-library, Docker-template or role-module changes. If you need help, share sanitized versions, setting names and constant error phrases through integration support, not a token with its signature removed. For broader connection failures, continue with disconnect diagnostics.
Sources
Reviewed 24 October 2026; source snapshots do not attest to every installed image.
- [S1] Docker stable-11248-2, Compose file and Prosody template: environment, domains and modules.
- [S2] Pinned Jitsi Meet 11248 token utility and authentication module: token checks, time of verification and room/domain mapping.
- [S3] Pinned JWT library: exact verification errors, optional time claims and scalar accepted-claim comparisons.
- [S4] Pinned room verifier: admission errors, exceptions and potentially sensitive logging.
- [S5] Pinned token affiliation and Docker Jicofo template: role policy and automatic ownership settings.
- [S6] Python 3.14 getpass, base64 and JSON: local inspection helper APIs. This helper is our diagnostic example, not a signature verifier.
- [S7] RFC 7519: claims, NumericDate and signed payload handling.
- [S8] Docker Compose logs and GNU grep: bounded log selection and fixed-string, matched-text-only filtering.
- [S9] Jitsi IFrame API: token delivery through the API.
- [S10] Docker Compose up and restart: changed configuration and recreation.
- [S11] Jitsi token authentication handbook and current authentication architecture: package setup, guest/host modules and differences from Docker.