How-to: BigBlueButton (Greenlight) with Univention Nubus – Single Sign-On, Single Logout and kind of an user lifecycle

Based on some tests (no production usage), this how-to describes how to connect BigBlueButton (BBB) with its official frontend Greenlight v3 to Univention Nubus. Once it is set up:

  • Single Sign-On: users sign in to BigBlueButton with their Nubus account, through OpenID Connect and the Keycloak included in Nubus.
  • Single Logout: signing out of BigBlueButton also ends the Nubus session.
  • User lifecycle: BigBlueButton accounts follow the Nubus user objects. Accounts are created at first login, keep a stable identifier (univentionObjectIdentifier) and are updated from Nubus. Users who are disabled or deleted in Nubus can no longer sign in.

The how-to focuses on the configuration. For installing BigBlueButton, Greenlight and Nubus, follow the upstream documentation linked in Deployment.


Background: authentication in BigBlueButton

A BigBlueButton server (media server, HTML5 client, recordings) has no user management of its own. Users, rooms and recordings are managed by the frontend, which by default is “Greenlight”, which talks to the BBB server API. So identity integration happens in the frontend:

               OIDC (SSO / logout)                 BBB API (shared secret)
Browser ───────────────────────► Greenlight ─────────────────────────► BigBlueButton server(s)
   │                                                                   (or Scalelite load balancer)
   ▼
Nubus Keycloak ◄──── LDAP federation ──── Nubus directory (OpenLDAP)

Greenlight v3 has no LDAP support (it was removed in v3). The connection to Nubus runs only by Single Sign-On with Keycloak. All user data Greenlight gets from Nubus arrives as OIDC claims.

Following this Howto the result is:

Greenlight v3 + Nubus
Single Sign-On via OIDC :white_check_mark:
Logout in Greenlight also signs out of Nubus (RP-initiated logout) :white_check_mark:
Logout in Nubus or another application also signs out of Greenlight (back-channel logout) :x: Greenlight doesn’t implement it; keep the Greenlight session timeout short
Accounts created at first login (just-in-time) :white_check_mark:
Stable account identifier across renames (univentionObjectIdentifier) :white_check_mark:
Name and email updated at each login :white_check_mark: with the site setting Resync on login
Disabled or deleted Nubus users can no longer sign in :white_check_mark:
Roles from Nubus groups :x: Greenlight only supports role rules by email domain
Automatic removal of Greenlight accounts deleted in Nubus :x: an administrator needs to remove them in Greenlight

Deployment

Use the upstream documentation for installing the components:

The Howto has been tested with Nubus on Kubernetes, but should also work with Nubus on UCS.

Placeholders used in this how-to

Placeholder Meaning
id.example.org Hostname of the Nubus Keycloak (global.subDomains.keycloak.global.domain, default id.<domain>)
nubus Keycloak realm of Nubus (default)
bbb.example.org Hostname of Greenlight
<release>, <nubus-namespace> Helm release name and namespace of Nubus for Kubernetes

The OIDC issuer of Nubus is https://id.example.org/realms/nubus. You can check it at https://id.example.org/realms/nubus/.well-known/openid-configuration.

Prerequisite: users need an email address

Greenlight requires the claims name and email. The Nubus Keycloak takes email from the Nubus user property Primary e-mail address (mailPrimaryAddress). Users without it wil run into an error message during login: Greenlight shows You can’t be authenticated, and its log shows Error during authentication: undefined method 'downcase' for nil.


Step 1: Provide univentionObjectIdentifier in Keycloak

Greenlight identifies an account by a single claim (OPENID_CONNECT_UID_FIELD). Choose a claim that never changes for the lifetime of the Nubus user:

  • univentionObjectIdentifier is the permanent identifier Nubus assigns to every directory object. It survives renames, moves between containers, and re-imports into Keycloak.
  • sub, the default, is the ID of Keycloak’s cached copy of the LDAP user, not an identifier from Nubus. If Keycloak ever re-imports the user, sub changes and Greenlight creates a second account.
  • preferred_username changes when the user is renamed.

Out of the box, the Nubus Keycloak doesn’t map univentionObjectIdentifier from LDAP (this will be provided in future Nubus versions). Add an attribute mapper to the LDAP user federation. Either option below works.

Option A: Helm value of Nubus for Kubernetes

Add the mapper to your custom_values.yaml and apply the configuration. The keycloak-bootstrap job then creates it; see Configure additional LDAP mappers in the Operation Manual.

nubusKeycloakBootstrap:
  bootstrap:
    ldapMappers:
      univentionObjectIdentifier:
        alwaysReadFromLdap: true

This keeps the mapper in your deployment configuration together with the rest of Nubus.

Option B: Keycloak Admin Console

  1. Sign in to the Keycloak Admin Console at https://id.example.org/admin/. The Operation Manual section Keycloak Admin Console describes the admin user and its password.
  2. In the realm selector at the top left, switch to the realm nubus.
  3. Open User federation → ldap-provider → tab Mappers → Add mapper.
  4. Fill in the mapper:
    • Name: univentionObjectIdentifier
    • Mapper type: user-attribute-ldap-mapper
    • User Model Attribute: univentionObjectIdentifier
    • LDAP Attribute: univentionObjectIdentifier
    • Read Only: On
    • Always Read Value From LDAP: On. With Off, users already imported into Keycloak wouldn’t get the attribute until they are re-imported.
    • Is Mandatory In LDAP: Off
    • Force a Default Value: Off
    • Is Binary Attribute: Off
  5. Click Save.

You check the result at the end of step 2, with the generated ID token.


Step 2: Create the OIDC client for Greenlight

In the Keycloak Admin Console, realm nubus, go to Clients → Create client. The wizard has three pages:

  1. General settings
    • Client type: OpenID Connect
    • Client ID: bbb-greenlight
    • Name (optional): BigBlueButton
  2. Capability config
    • Client authentication: On (confidential client)
    • Authorization: Off
    • Authentication flow: only Standard flow checked. Uncheck Direct access grants.
    • PKCE Method: leave empty. Greenlight doesn’t use PKCE.
  3. Login settings
    • Root URL and Home URL: https://bbb.example.org
    • Valid redirect URIs: https://bbb.example.org/auth/openid_connect/callback
    • Valid post logout redirect URIs: https://bbb.example.org/*
    • Web origins: https://bbb.example.org

Click Save. Then complete the client:

  • Logout settings. On the Settings tab, scroll down to Logout settings, set Front channel logout to Off, and click Save. Leave Backchannel logout URL empty, because Greenlight has no endpoint for it.

  • Client secret. On the Credentials tab, copy the Client Secret. You need it in step 3.

  • Identifier claim. Open the tab Client scopes → bbb-greenlight-dedicated, then Configure a new mapper (or Add mapper → By configuration) → User Attribute:

    • Name: univentionObjectIdentifier
    • User Attribute: univentionObjectIdentifier
    • Token Claim Name: univentionObjectIdentifier
    • Claim JSON Type: String
    • Add to ID token: On
    • Add to access token: Off
    • Add to userinfo: On
    • Multivalued: Off

    Click Save.

The default client scopes profile and email of the Nubus realm provide the remaining claims Greenlight uses: name, email and locale.

Check the result. Open Clients → bbb-greenlight → Client scopes → Evaluate, select a Nubus user, and click Generated ID token. The token must contain univentionObjectIdentifier, name and email:

{
  "iss": "https://id.example.org/realms/nubus",
  "aud": "bbb-greenlight",
  "univentionObjectIdentifier": "c0b35f7f-24c6-4098-988a-33df42ce6d07",
  "name": "Jane Doe",
  "given_name": "Jane",
  "family_name": "Doe",
  "preferred_username": "jdoe",
  "email": "jane.doe@example.org"
}
Steps 1 (option B) and 2 with kcadm.sh (command line)

In Nubus for Kubernetes, kcadm.sh is available in the Keycloak pod:

kubectl -n <nubus-namespace> exec -it <release>-keycloak-0 -c main -- bash
K=/opt/keycloak/bin/kcadm.sh
$K config credentials --server http://localhost:8080 --realm master --user kcadmin   # asks for the password

# Step 1: LDAP federation mapper
LID=$($K get components -r nubus -q name=ldap-provider --fields id --format csv --noquotes)
$K create components -r nubus -f - <<EOF
{
  "name": "univentionObjectIdentifier",
  "providerId": "user-attribute-ldap-mapper",
  "providerType": "org.keycloak.storage.ldap.mappers.LDAPStorageMapper",
  "parentId": "$LID",
  "config": {
    "ldap.attribute": ["univentionObjectIdentifier"],
    "user.model.attribute": ["univentionObjectIdentifier"],
    "read.only": ["true"],
    "always.read.value.from.ldap": ["true"],
    "is.mandatory.in.ldap": ["false"]
  }
}
EOF

# Step 2: client
$K create clients -r nubus -f - <<'EOF'
{
  "clientId": "bbb-greenlight",
  "protocol": "openid-connect",
  "publicClient": false,
  "secret": "<generate-a-long-random-secret>",
  "standardFlowEnabled": true,
  "directAccessGrantsEnabled": false,
  "rootUrl": "https://bbb.example.org",
  "redirectUris": ["https://bbb.example.org/auth/openid_connect/callback"],
  "webOrigins": ["https://bbb.example.org"],
  "frontchannelLogout": false,
  "attributes": { "post.logout.redirect.uris": "https://bbb.example.org/*" }
}
EOF

# Step 2: identifier claim
ID=$($K get clients -r nubus -q clientId=bbb-greenlight --fields id --format csv --noquotes)
$K create clients/$ID/protocol-mappers/models -r nubus -f - <<'EOF'
{
  "name": "univentionObjectIdentifier",
  "protocol": "openid-connect",
  "protocolMapper": "oidc-usermodel-attribute-mapper",
  "config": {
    "user.attribute": "univentionObjectIdentifier",
    "claim.name": "univentionObjectIdentifier",
    "jsonType.label": "String",
    "id.token.claim": "true",
    "userinfo.token.claim": "true",
    "access.token.claim": "false"
  }
}
EOF

Step 3: Configure Greenlight

Add these variables to the Greenlight environment, in addition to the base settings from the Greenlight install guide: SECRET_KEY_BASE, DATABASE_URL, REDIS_URL, URL_HOST, BIGBLUEBUTTON_ENDPOINT and BIGBLUEBUTTON_SECRET.

OPENID_CONNECT_ISSUER=https://id.example.org/realms/nubus
OPENID_CONNECT_CLIENT_ID=bbb-greenlight
OPENID_CONNECT_CLIENT_SECRET=<client secret from Keycloak>
# Base URL of Greenlight; Greenlight appends "/auth/openid_connect/callback"
OPENID_CONNECT_REDIRECT=https://bbb.example.org/
# Stable account identifier (claim from step 2)
OPENID_CONNECT_UID_FIELD=univentionObjectIdentifier
# Enables logout towards Nubus (path relative to the issuer)
OPENID_CONNECT_LOGOUT_PATH=/protocol/openid-connect/logout

Restart Greenlight afterwards.

Notes:

  • When OPENID_CONNECT_ISSUER is set, Greenlight hides its local sign-up and sign-in forms and shows only the Sign in button, which leads to Nubus.
  • Greenlight must trust the TLS certificate of id.example.org. With a private CA, add the CA to the container’s trust store, for example by mounting a CA bundle and setting SSL_CERT_FILE.
  • Greenlight stores the ID token in its session cookie, which makes response headers large. If the reverse proxy in front of Greenlight answers 502 upstream sent too big header right after login, increase its buffers:
    • ingress-nginx: add the annotation nginx.ingress.kubernetes.io/proxy-buffer-size: "16k";
    • plain nginx: see greenlight#6065.
  • BIGBLUEBUTTON_ENDPOINT must end in /bigbluebutton/api/.

Existing Greenlight accounts

If Greenlight already has accounts, you must migrate them when you switch to OIDC or change OPENID_CONNECT_UID_FIELD. Those accounts don’t have univentionObjectIdentifier as their identifier yet, and a login of such a user fails with /?error=SignupError. The Greenlight log shows Validation failed: Email has already been taken.

For the migration period, set

USE_EMAIL_AS_EXTERNAL_ID_FALLBACK=true

Greenlight then matches the existing account by email address at the next login and stores the new identifier; rooms and recordings are kept. Remove the variable again after all users have signed in once, so that from then on only the permanent identifier is used.


Step 4: First administrator

With SSO enabled, the local administrator sign-in is no longer available, so promote a Nubus user instead. Let the user sign in once, then run:

# Docker Compose
docker exec -it greenlight-v3 bundle exec rake "user:set_admin_role[admin@example.org]"
# Kubernetes
kubectl exec -it deploy/greenlight -- bundle exec rake "user:set_admin_role[admin@example.org]"

Single Logout behaviour

  • Sign out in Greenlight → signed out of Nubus.
    1. Greenlight redirects the browser to the Keycloak end_session_endpoint, passing id_token_hint, client_id and post_logout_redirect_uri=https://bbb.example.org/?success=LogoutSuccessful.
    2. Keycloak ends the SSO session. This signs the user out of the Nubus Portal and of all other applications that support back-channel logout.
    3. Keycloak returns the user to Greenlight.
  • Sign out in Nubus or another application → Greenlight session stays. Greenlight implements neither OIDC back-channel nor front-channel logout. Its session stays valid until the user signs out in Greenlight or the session expires. Keep the Greenlight session lifetime short: Administrator Panel → Site Settings → Session timeout.

User lifecycle

Greenlight has limited user lifecycle capabilities as described in the following table. There are alternate frontends with other capabilities (for example PILOS seems to support LDAP for user lifecycle).

Event in Nubus Effect in Greenlight
User created No effect until the first sign-in. At first sign-in, Greenlight creates the account just-in-time, with a personal room and the Default role (Administrator Panel → Site Settings).
User renamed or moved Same Greenlight account, because univentionObjectIdentifier doesn’t change.
Name or email changed Updated at the next sign-in if Administrator Panel → Registration → Resync on login is enabled.
User disabled or deleted The user can no longer sign in, because Keycloak rejects the login. An already open Greenlight session lasts until it expires. The Greenlight account, its rooms and its recordings stay until an administrator deletes them in Administrator Panel → Manage Users.
Group membership changed No effect. Greenlight can’t map claims or groups to roles.

Further options in Administrator Panel → Registration:

  • Registration method:
    • Approve/Decline: new users wait in Pending until an administrator approves them.
    • Join by invitation: only Nubus users who received an invitation are admitted.
  • Role mapping: assigns roles at account creation by email suffix, for example Moderator=@staff.example.org. This is the only automatic role assignment in Greenlight v3. Requests for claim-based roles: greenlight#5994, greenlight#6141.

Verification

  1. Open https://bbb.example.org and click Sign in. You are redirected to the Nubus login page, and back to Greenlight after login.
  2. Open the Nubus Portal in the same browser. You are signed in without a second password prompt (SSO). The Keycloak Admin Console shows one session under Users → user → Sessions.
  3. Start a meeting in your Greenlight room. The BigBlueButton HTML5 client opens.
  4. Sign out in Greenlight. You land on https://bbb.example.org/?success=LogoutSuccessful. Reload the Nubus Portal: you are signed out there, too, and the Keycloak session is gone.
  5. Disable the user in Nubus and try to sign in again. Keycloak rejects the login.

Troubleshooting

Symptom Cause / fix
Greenlight shows You can’t be authenticated; the log shows Error during authentication: undefined method 'downcase' for nil The ID token has no email, because the Nubus user has no Primary e-mail address. Set it. If the user has signed in to Keycloak before, also run Sync changed users; see Prerequisite: users need an email address.
Greenlight redirects to /?error=SignupError; the log shows Validation failed: Email has already been taken An existing Greenlight account was created with another identifier. See Existing Greenlight accounts.
The ID token has no univentionObjectIdentifier Step 1 is missing, or Always Read Value From LDAP is off and the user was imported into Keycloak before the mapper existed.
Greenlight: 502 upstream sent too big header Increase the proxy buffer size, see step 3.
Greenlight log: SSL_connect … certificate verify failed The Greenlight container doesn’t trust the certificate of id.example.org.
Keycloak: Invalid redirect uri or Invalid post logout redirect uri Compare the URIs in the client with step 2. Greenlight sends …/?success=LogoutSuccessful, which the wildcard https://bbb.example.org/* covers.
Meetings don’t start BIGBLUEBUTTON_ENDPOINT must end in /bigbluebutton/api/. Check the secret with bbb-conf --secret.

Tested with

  • Nubus for Kubernetes 1.23.0 (Keycloak 26.7) on a local kind cluster, with the LDAP mapper created as in step 1 option B
  • Greenlight v3.9.0.1: SSO, portal SSO, RP-initiated logout, migration with USE_EMAIL_AS_EXTERNAL_ID_FALLBACK, disabled users, meeting start and join
  • BigBlueButton: public test server test-install.blindsidenetworks.com

Links