How do I add Keycloak or Entra ID single sign-on to Jitsi Meet?

Short answer

Jitsi only trusts JWTs that Prosody can verify, signed with your app secret or a key it can fetch, and it reads them from the ?jwt= URL parameter, so an OIDC login needs a bridge. The practical bridge is an adapter such as jitsi-contrib/jitsi-oidc-adapter: Jitsi sends users to it through tokenAuthUrl, it runs the OIDC code flow with Keycloak or Entra ID, then mints a room-scoped HS256 Jitsi token. On Docker set ENABLE_AUTH=1, AUTH_TYPE=jwt, JWT_APP_ID, JWT_APP_SECRET, ENABLE_GUESTS=1 and TOKEN_AUTH_URL. With Docker defaults every signed-in user becomes a moderator and guests wait for a host.

Who this is for

You run Jitsi Meet and want people to sign in with your company identity provider (Keycloak, Microsoft Entra ID, Authentik or any OpenID Connect provider) before they can start a meeting. Guests should still be able to join once a host is there. You want this to work in the browser and in the Jitsi mobile apps.

How it works

Jitsi only understands its own JWT. Prosody’s token plugin checks a JWT when the browser connects. The token is passed as the token parameter of the BOSH or WebSocket URL, and Jitsi Meet takes it from the page URL as ?jwt=. Prosody verifies the signature with app_secret (HS256), or with a public key from asap_key_server or cache_keys_url (RS256, needs a kid header). It then checks iss, aud, room, sub and exp.

Why an OIDC token does not just work. An identity provider’s id_token is signed with the provider’s keys, not your app secret. It has no room claim, and it comes back in the URL fragment (#id_token=) or a form POST, which Jitsi does not read. Native OIDC redirect support was requested in jitsi-meet issue #16576. A maintainer said in January 2026 that an intermediate step would still be needed. The issue was closed as stale on 2026-03-29.

Bridge patterns. Issue #16576 lists three:

  1. Adapter or token service (recommended). A small service runs the OIDC login, then signs a Jitsi JWT with your app secret. jitsi-contrib/jitsi-oidc-adapter is the maintained community option. The older nordeck/jitsi-keycloak-adapter README now tells users to switch to it.
  2. Prosody verifies the provider’s token directly. Since PR #16649 (2.0.10655), cache_keys_url accepts a JWKS URL. You still need a page that copies the fragment token into ?jwt=, the implicit flow, and asap_require_room_claim = false.
  3. Pre-signed links from your own app. These suit portals and bots, not interactive sign-in.

A reverse proxy login (for example in front of the web page) is not enough on its own. Prosody still needs a JWT on the XMPP connection.

The login redirect. When a user who is not signed in needs to log in, Jitsi Meet sends them to tokenAuthUrl. It fills in {room} and {state}; state holds the room, tenant, config overrides and ios=true, android=true or electron=true. Without tokenAuthUrl, Jitsi shows the username and password dialog instead. The mobile apps open tokenAuthUrl in the system browser and wait for a deep link that carries the JWT back. The adapter returns org.jitsi.meet:// links for iOS, an Android intent:// link, and jitsi-meet:// for the Electron app.

Who becomes moderator. In Docker, JICOFO_ENABLE_AUTH defaults to ENABLE_AUTH, and the Jicofo auth type follows AUTH_TYPE. So with AUTH_TYPE=jwt, Jicofo authentication is on. Jicofo then grants owner (moderator) to every authenticated member. Guests join on the anonymous domain and are not authenticated. To decide moderators from a token claim, the bundled mod_token_affiliation reads context.user.moderator (true or "true") or context.user.affiliation (owner, moderator, teacher).

Before you start

  • Jitsi with HTTPS at https://meet.example.com: docker-jitsi-meet stable-11248 or packages 2.0.11248.
  • An OIDC provider reachable by users and by the adapter. Keycloak 26.8.0 is current as of 2026-10-01.
  • A random app secret shared by the adapter and Prosody, for example openssl rand -hex 32.
  • For Keycloak: a realm (here example) and admin access. The issuer is https://auth.example.com/realms/example.
  • For Entra ID: an app registration. The issuer is https://login.microsoftonline.com/<tenant-id>/v2.0.

Steps

1. Create the OIDC client

Keycloak (adapter docs):

  • Create a client, for example jitsi.
  • Valid redirect URIs: https://meet.example.com/oidc/tokenize
  • Web origins: https://meet.example.com
  • Either turn off Client authentication (public client), or leave it on and copy the client secret.

Entra ID: register a web app with redirect URI https://meet.example.com/oidc/tokenize and create a client secret. The adapter reads endpoints from the issuer’s discovery document. Entra’s discovery document lists a userinfo endpoint. This path is untested (see Open questions).

2. Docker (docker-jitsi-meet stable-11248)

Add the adapter as a service in docker-compose.override.yml. Compose merges that file automatically:

YAML
services:
  oidc-adapter:
    image: ghcr.io/jitsi-contrib/jitsi-oidc-adapter
    restart: unless-stopped
    environment:
      - OIDC_ISSUER_URL=https://auth.example.com/realms/example
      - OIDC_CLIENT_ID=jitsi
      - OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}
      - JWT_APP_ID=${JWT_APP_ID}
      - JWT_APP_SECRET=${JWT_APP_SECRET}
      - JWT_EXP_SECOND=3600
      - AUTO_RETURN_TO_APP=true
    networks:
      meet.jitsi:
  web:
    depends_on:
      - oidc-adapter

The variable names and the port (9000) come from the adapter image. The adapter’s own Docker guide publishes port 9000 on the host instead and is marked “NOT READY YET”.

Proxy /oidc/ through Jitsi’s web container. Create ~/.jitsi-meet-cfg/web/nginx-custom/oidc.conf. The stable-11248 web image copies /config into /run/web/config and includes nginx-custom/*.conf:

Nginx
location ~ /oidc/ {
    proxy_pass http://oidc-adapter:9000;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Host $http_host;
}

Keep the Host header: the adapter builds https://<host>/oidc/tokenize from it.

Set .env:

Config
ENABLE_AUTH=1
AUTH_TYPE=jwt
JWT_APP_ID=meet_example
JWT_APP_SECRET=REPLACE_WITH_RANDOM_SECRET
OIDC_CLIENT_SECRET=REPLACE_WITH_CLIENT_SECRET
ENABLE_GUESTS=1
TOKEN_AUTH_URL=https://meet.example.com/oidc/auth?state={state}
XMPP_MODULES=persistent_lobby
XMPP_MUC_MODULES=muc_wait_for_host

TOKEN_AUTH_URL becomes config.tokenAuthUrl. ENABLE_GUESTS adds the anonymousdomain, so guests can wait for a host.

Start it:

Terminal
docker compose up -d

3. Debian/Ubuntu packages (2.0.11248)

  1. Install token support. It asks for the app ID and secret and sets authentication = "token":

    Terminal
    sudo apt-get install jitsi-meet-tokens
  2. Add the guest VirtualHost to the Prosody config, and anonymousdomain to /etc/jitsi/meet/meet.example.com-config.js:

    Lua
    VirtualHost "guest.meet.example.com"
        authentication = "jitsi-anonymous"
        c2s_require_encryption = false
    JavaScript
    anonymousdomain: 'guest.meet.example.com',
  3. Add persistent_lobby to the main VirtualHost modules_enabled, and muc_wait_for_host to the conference component.

  4. Install the adapter as a systemd service. Proxy /oidc/ to http://127.0.0.1:9000 with the adapter’s oidc.conf in /etc/jitsi/meet/jaas/.

  5. Set the login URL in /etc/jitsi/meet/meet.example.com-config.js:

    JavaScript
    config.tokenAuthUrl = 'https://meet.example.com/oidc/auth?state={state}';
  6. Restart:

    Terminal
    sudo systemctl restart prosody jicofo jitsi-videobridge2 nginx

4. Choose how moderators are decided

  • Every signed-in user is a moderator (Docker default). Leave Jicofo auth on.
  • Only some users are moderators. Add moderator: true under context.user for chosen users. The adapter builds context.user in src/context.ts. Then load token_affiliation and stop Jicofo from promoting everyone. In Docker that means XMPP_MUC_MODULES=muc_wait_for_host,token_affiliation, JICOFO_ENABLE_AUTH=0, ENABLE_AUTO_OWNER=0 and WAIT_FOR_HOST_DISABLE_AUTO_OWNERS=1. We derived this from source, so test it first.

5. Direct JWKS verification (alternative, packages)

Packages 2.0.10655 and newer can verify Keycloak tokens without an adapter. A maintainer gave this Prosody setup in #16576:

Lua
asap_accepted_issuers = { "https://auth.example.com/realms/example" }
asap_accepted_audiences = { "jitsi" }
asap_require_room_claim = false;
VirtualHost "meet.example.com"
    authentication = "token"
    cache_keys_url = "https://auth.example.com/realms/example/protocol/openid-connect/certs";

You also need a plugin.head.html script that turns #id_token= into ?jwt=, plus the implicit flow. Keycloak warns that implicit-flow tokens are “very hard to invalidate”. docker-jitsi-meet has no cache_keys_url variable.

Configuration reference

Name Where Default What it does
ENABLE_AUTH Docker .env 0 Turns on authentication
AUTH_TYPE Docker .env internal jwt selects token auth
JWT_APP_ID Docker .env unset Prosody app_id. Default accepted issuer
JWT_APP_SECRET Docker .env unset Prosody app_secret, HS256 key
JWT_ACCEPTED_ISSUERS / JWT_ACCEPTED_AUDIENCES Docker .env issuers: app ID; audiences: * Allowed iss and aud
JWT_ENABLE_DOMAIN_VERIFICATION Docker .env false When on, sub must equal XMPP_DOMAIN (default meet.jitsi) or *
JWT_ALLOW_EMPTY Docker .env 0 Lets users connect without a token
ENABLE_GUESTS Docker .env 0 Adds the anonymous guest domain
TOKEN_AUTH_URL Docker .env unset Sets config.tokenAuthUrl
JICOFO_ENABLE_AUTH Docker .env value of ENABLE_AUTH Jicofo auth. When on, every authenticated user is moderator
ENABLE_AUTO_OWNER Docker .env Jicofo default First member becomes moderator when Jicofo auth is off
JICOFO_AUTH_LIFETIME Docker .env 24 hours Jicofo auth session lifetime
XMPP_MODULES / XMPP_MUC_MODULES Docker .env unset Extra Prosody modules
tokenAuthUrl config.js unset Login service URL with {room}, {state}, {code_challenge}
tokenLogoutUrl config.js unset Logout service URL
cache_keys_url Prosody VirtualHost unset JWKS URL for RS256 tokens, 2.0.10655+
asap_require_room_claim Prosody true Requires the room claim
OIDC_ISSUER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_SCOPES adapter env client jitsi, scopes openid profile email Provider settings
JWT_EXP_SECOND adapter env 10800 Lifetime of minted Jitsi tokens
AUTO_RETURN_TO_APP adapter env false Redirect mobile and desktop apps back automatically

Claims the adapter puts in its token: aud and iss = JWT_APP_ID; sub = the tenant or meeting host; room = the requested room; iat, nbf, exp; context.user with id (the OIDC sub), name, email, lobby_bypass and security_bypass.

Common mistakes

  • Pasting the provider’s token as ?jwt= while Prosody uses app_secret. Prosody logs Error verifying token err:not-allowed, reason:Invalid signature.
  • Putting the token in the fragment. Jitsi parses hash values as JSON, so #jwt= needs the value in quotes. ?jwt= does not.
  • Setting tokenAuthUrlAutoRedirect. The adapter docs set it, but it is not in jitsi-meet’s config type on master as of 2026-10-05.
  • Everyone is a moderator. That is Jicofo auth with the Docker defaults. See step 4.
  • 'kid' claim is missing after adding cache_keys_url. With a key URL set, Prosody verifies only RS-signed tokens with kid, so HS256 tokens from other tools fail.
  • Room does not match the room from token. The adapter scopes each token to one room, so the same link cannot open another room.
  • Wrong sub with domain verification on. On Docker, sub must be meet.jitsi (XMPP_DOMAIN) or *. With the Docker default (off), sub is only required to exist. On packages, the domain is your public hostname.
  • Redirect URI mismatch in Keycloak. It must be exactly https://meet.example.com/oidc/tokenize.
  • Proxy drops the Host header, so the adapter builds a wrong redirect URI.

Verify

  1. Adapter health. The adapter answers on /oidc/health:

    Terminal
    curl -s https://meet.example.com/oidc/health

    Expect healthy.

  2. The adapter found the provider. At startup, docker compose logs oidc-adapter shows AUTH_ENDPOINT:, TOKEN_ENDPOINT: and USERINFO_ENDPOINT: lines.

  3. Open https://meet.example.com/testroom in a private window. Click the log-in button on the wait-for-host screen. You should go to Keycloak, then back to /testroom?jwt=....

  4. Decode the token’s middle part and confirm room is testroom, aud and iss equal JWT_APP_ID, and exp is about one hour ahead.

  5. In a second browser, open the same room without signing in. The guest should wait until the host is in, then join without moderator rights.

If it still fails

  • Prosody: docker compose logs prosody | grep -i token. Look for Error verifying token with a reason such as Invalid signature, room claim is missing or token required.
  • Jicofo: docker compose logs jicofo | grep -i ownership shows Granting ownership to for each promoted user.
  • Adapter: docker compose logs oidc-adapter. Discovery or userinfo errors point at OIDC_ISSUER_URL or the client secret.
  • Mobile app does not return: test with Firefox on Android. The adapter docs report a “broken tab” problem with Chromium browsers when AUTO_RETURN_TO_APP=true.

What we have not confirmed yet

We checked everything above against the Jitsi token library, Jicofo, the docker-jitsi-meet templates, the adapter source and maintainer comments on 2026-10-05. We have not yet run the full Keycloak or Entra ID flow on stable-11248. These points are still open:

  • The adapter’s Docker guide says “NOT READY YET” and was tested with stable-10741. The standalone guide was tested on Debian 12 with 2.0.10741. Neither is confirmed on 11248.
  • The compose override (adapter on the meet.jitsi network, proxy to oidc-adapter:9000) is our adaptation of the adapter docs. Untested.
  • Claim-based moderators: the combination JICOFO_ENABLE_AUTH=0, ENABLE_AUTO_OWNER=0, WAIT_FOR_HOST_DISABLE_AUTO_OWNERS=1 and token_affiliation is derived from source and needs a real test. Includes whether the wait-for-host login still triggers with Jicofo auth off.
  • Nothing in jitsi-meet’s Prosody plugins reads the adapter’s lobby_bypass and security_bypass flags (code search, 2026-10-05). Their effect is unconfirmed.
  • Entra ID with the adapter: discovery and userinfo should work, but untested. Entra’s multi-tenant discovery uses {tenantid} in the issuer, so use the tenant-specific issuer URL.
  • Direct JWKS on Docker: XMPP_CONFIGURATION lines are rendered into the main VirtualHost, so cache_keys_url could be added that way. Untested.
  • When tokenAuthUrlAutoRedirect was removed from jitsi-meet, and whether it had any replacement.
  • White-label mobile apps use their own URL scheme. The adapter hardcodes org.jitsi.meet and jitsi-meet.
  • Tokens travel in the page URL (?jwt=), so they can appear in browser history. Whether the default nginx access log records them was not checked.

Sources

Need a hand?

Single sign-on touches your identity provider, Prosody and Jicofo at once. Our integration service sets it up and tests host, guest and mobile logins, or contact our engineers with the Prosody lines that mention Error verifying token.

Frequently asked questions

Can Jitsi use Keycloak tokens directly, without an adapter?

Partly. Since 2.0.10655 Prosody can check RS256 tokens against a JWKS URL, but you still need a fragment-to-query script and lose room scoping.

Does this work with Microsoft Entra ID or Azure AD?

The adapter uses standard OIDC discovery and userinfo, which Entra provides. We have not tested it end to end.

Do guests need an account?

No. With `ENABLE_GUESTS=1` they join on the anonymous domain, and with `muc_wait_for_host` they wait until a signed-in host arrives.

How do I log users out?

Set `tokenLogoutUrl` to your provider's logout URL. Docker has no variable for it, so put it in `custom-config.js`.

How do I rotate the app secret?

Change `JWT_APP_SECRET` for both Jitsi and the adapter, then recreate both. Prosody holds one `app_secret`, so tokens signed with the old secret stop working.

Stuck, or would rather not do this by hand?

Deploy it in one click

A private Jitsi server in your own AWS account with SSL, your domain and optional recording, transcription and JWT. Free 15 minute trial.

Start free trial

Talk to a Jitsi engineer

Setup, fixes, branding, recording, scaling. Tell us what is happening and we reply with a plan and a quote.

Get expert help

Related

Recently updated