#! /usr/bin/python3
#
# A FastCGI script used to authenticate users into Big Blue Button
# using a JSON web token signed with the Big Blue Button API key.
#
# Such tokens are generated by the bbb-mklogin script.
#
# In addition to the standard JWT claims 'sub' (Subject) and 'exp'
# (expiration time, which is required), we also require 'role'
# (either 'm' for moderator or 'v' for viewer).
#
# Optional claim: 'mtg' for meeting ID (default is hostname)
#
# Moderators will start the meeting when they enter. Viewers will
# get an error message if the meeting isn't already running.
import os
import re
import jwt
import socket
import fastcgi
import bigbluebutton
# These passwords don't have to be very secure because the API key
# is what really protects everything. If you have the API key
# can get these passwords from the meeting's XML data anyway.
moderatorPW = 'jidtyv7RG8g0gsGMLq5M'
attendeePW = 'aQxdAAEi2fQq27TB6rTf'
# maps first letter of 'role' claim to the join password to be used
role_password = {'m' : moderatorPW,
'a' : attendeePW,
'v' : attendeePW,
}
# FASTCGI server
@fastcgi.fastcgi(sock='/run/bbb-auth-jwt/fastcgi.sock')
def login():
try:
JWT = os.environ['SCRIPT_NAME'].split('/')[-1]
jwt_options = {'require_exp' : True}
jwt_algorithms = ['HS256']
decoded = jwt.decode(jwt = JWT, key = bigbluebutton.securitySalt(),
options = jwt_options,
algorithms = jwt_algorithms)
fullName = decoded['sub']
if 'mtg' in decoded:
meetingID = decoded['mtg']
else:
meetingID = socket.gethostname()
roomName = meetingID
password = role_password[decoded['role'].lower()[0]]
# This API call will quietly fail if the meeting is already running.
if password == moderatorPW:
bigbluebutton.create(name = roomName,
meetingID = meetingID,
attendeePW = attendeePW,
moderatorPW = moderatorPW,
isBreakoutRoom = False,
)
if False:
# using redirect = False and using the URL from the XML
# seems to be buggy right now (2.3.0~alpha7)
response = bigbluebutton.join(meetingID = meetingID,
fullName = fullName,
password = password,
redirect = False,
)
url = response.xpath('.//url')[0].text
print(f"Location: {url}\n")
else:
# to evade the bug, use an internal package function to
# construct the join URL without calling it, then redirect
# to that URL instead
response = bigbluebutton._APIurl('join', {'meetingID': meetingID,
'fullName': fullName,
'password': password,
'redirect': 'true',
})
print(f"Location: {response}\n")
except Exception as ex:
print(f"""Content-type: text/html
<HTML>
<HEAD>
<TITLE>Login Failed</TITLE>
</HEAD>
<BODY>
<CENTER><H3>Login Failed</H3></CENTER>
Something went wrong!
<PRE>
{repr(ex)}
</PRE>
</BODY>
</HTML>
""")