forked from switnet/quick-jibri-installer
				
			Compare commits
	
		
			4 Commits
		
	
	
		
			d591495e41
			...
			5536451299
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 5536451299 | |
|  | a3ffd2cd82 | |
|  | 3a421bacba | |
|  | 6cb53ce880 | 
							
								
								
									
										5
									
								
								jm-bm.sh
								
								
								
								
							
							
						
						
									
										5
									
								
								jm-bm.sh
								
								
								
								
							|  | @ -23,6 +23,7 @@ if ! [ "$(id -u)" = 0 ]; then | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| DOMAIN="$(find /etc/prosody/conf.d/ -name \*.lua|awk -F'.cfg' '!/localhost/{print $1}'|xargs basename)" | DOMAIN="$(find /etc/prosody/conf.d/ -name \*.lua|awk -F'.cfg' '!/localhost/{print $1}'|xargs basename)" | ||||||
|  | MEET_CONF="/etc/jitsi/meet/$DOMAIN-config.js" | ||||||
| CSS_FILE="/usr/share/jitsi-meet/css/all.css" | CSS_FILE="/usr/share/jitsi-meet/css/all.css" | ||||||
| TITLE_FILE="/usr/share/jitsi-meet/title.html" | TITLE_FILE="/usr/share/jitsi-meet/title.html" | ||||||
| INT_CONF="/usr/share/jitsi-meet/interface_config.js" | INT_CONF="/usr/share/jitsi-meet/interface_config.js" | ||||||
|  | @ -87,10 +88,10 @@ sed -i "s| powered by the Jitsi Videobridge||g" "$TITLE_FILE" | ||||||
| sed -i "/appNotInstalled/ s|{{app}}|$MOVILE_APP_NAME|g" /usr/share/jitsi-meet/lang/* | sed -i "/appNotInstalled/ s|{{app}}|$MOVILE_APP_NAME|g" /usr/share/jitsi-meet/lang/* | ||||||
| 
 | 
 | ||||||
| #Custom UI changes | #Custom UI changes | ||||||
| if [ -f "$INT_CONF_ETC" ]; then | if [ -f "$INT_CONF"] && [ -f "$INT_CONF_ETC" ]; then | ||||||
|     echo "Static interface_config.js exists, skipping modification..." |     echo "Static interface_config.js exists, skipping modification..." | ||||||
| else | else | ||||||
|     echo "This setup doesn't have a static interface_config.js, checking changes..." |     echo "This setup doesn't have a static interface_config.js, seting it up and applying changes..." | ||||||
|     echo -e "\nPlease note that brandless mode will also overwrite support links.\n" |     echo -e "\nPlease note that brandless mode will also overwrite support links.\n" | ||||||
|     sed -i "21,32 s|Jitsi Meet|$APP_NAME|g" "$INT_CONF" |     sed -i "21,32 s|Jitsi Meet|$APP_NAME|g" "$INT_CONF" | ||||||
|     sed -i  "s|\([[:space:]]\)APP_NAME:.*| APP_NAME: \'$APP_NAME\',|" "$INT_CONF" |     sed -i  "s|\([[:space:]]\)APP_NAME:.*| APP_NAME: \'$APP_NAME\',|" "$INT_CONF" | ||||||
|  |  | ||||||
							
								
								
									
										59
									
								
								mode/jwt.sh
								
								
								
								
							
							
						
						
									
										59
									
								
								mode/jwt.sh
								
								
								
								
							|  | @ -14,16 +14,18 @@ done | ||||||
| 
 | 
 | ||||||
| #DEBUG | #DEBUG | ||||||
| if [ "$MODE" = "debug" ]; then | if [ "$MODE" = "debug" ]; then | ||||||
| set -x |     set -x | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| DOMAIN="$(find /etc/prosody/conf.d/ -name \*.lua|awk -F'.cfg' '!/localhost/{print $1}'|xargs basename)" | DOMAIN="$(find /etc/prosody/conf.d/ -name \*.lua|awk -F'.cfg' '!/localhost/{print $1}'|xargs basename)" | ||||||
| MEET_CONF="/etc/jitsi/meet/$DOMAIN-config.js" | MEET_CONF="/etc/jitsi/meet/$DOMAIN-config.js" | ||||||
| JICOFO_SIP="/etc/jitsi/jicofo/sip-communicator.properties" | JICOFO_SIP="/etc/jitsi/jicofo/sip-communicator.properties" | ||||||
|  | JICOFO_CONF="/etc/jitsi/jicofo/jicofo.conf" | ||||||
| PROSODY_FILE="/etc/prosody/conf.d/$DOMAIN.cfg.lua" | PROSODY_FILE="/etc/prosody/conf.d/$DOMAIN.cfg.lua" | ||||||
| PROSODY_SYS="/etc/prosody/prosody.cfg.lua" | PROSODY_SYS="/etc/prosody/prosody.cfg.lua" | ||||||
| APP_ID="$(tr -dc "a-zA-Z0-9" < /dev/urandom | fold -w 16 | head -n1)" | APP_ID="$(tr -dc "a-zA-Z0-9" < /dev/urandom | fold -w 16 | head -n1)" | ||||||
| SECRET_APP="$(tr -dc "a-zA-Z0-9" < /dev/urandom | fold -w 64 | head -n1)" | SECRET_APP="$(tr -dc "a-zA-Z0-9" < /dev/urandom | fold -w 64 | head -n1)" | ||||||
|  | ROOM="Two-Hour-Test-Room" | ||||||
| SRP_STR="$(grep -n "VirtualHost \"$DOMAIN\"" "$PROSODY_FILE" | head -n1 | cut -d ":" -f1)" | SRP_STR="$(grep -n "VirtualHost \"$DOMAIN\"" "$PROSODY_FILE" | head -n1 | cut -d ":" -f1)" | ||||||
| SRP_END="$((SRP_STR + 10))" | SRP_END="$((SRP_STR + 10))" | ||||||
| 
 | 
 | ||||||
|  | @ -37,6 +39,15 @@ if command -v prosodyctl >/dev/null 2>&1; then | ||||||
|   esac |   esac | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
|  | # Custom 5.4 lua workaround for prosody 0.12 | ||||||
|  | echo "Warning: Ubuntu 22.04/24.04 don't ship the required lua inspect module 5.4," | ||||||
|  | echo "         so, we work arround it, be careful on further upgrades or changes." | ||||||
|  | install -d -m 755 /usr/share/lua/5.4 | ||||||
|  | ln -sf /usr/share/lua/5.3/inspect.lua /usr/share/lua/5.4/inspect.lua | ||||||
|  | systemctl restart prosody jicofo jitsi-videobridge2 | ||||||
|  | 
 | ||||||
|  | sleep .1 | ||||||
|  | 
 | ||||||
| # Install dependencies | # Install dependencies | ||||||
| apt-get -y install python3-jwt | apt-get -y install python3-jwt | ||||||
| 
 | 
 | ||||||
|  | @ -45,41 +56,36 @@ echo "set jitsi-meet-tokens/appsecret password $SECRET_APP" | debconf-set-select | ||||||
| 
 | 
 | ||||||
| apt-get install -y jitsi-meet-tokens | apt-get install -y jitsi-meet-tokens | ||||||
| 
 | 
 | ||||||
| # Setting up | # Setting up prosody | ||||||
| sed -i "s|c2s_require_encryption = true|c2s_require_encryption = false|" "$PROSODY_SYS" | sed -i "s|c2s_require_encryption = true|c2s_require_encryption = false|" "$PROSODY_SYS" | ||||||
| #- | #- | ||||||
| sed -i "$SRP_STR,$SRP_END{s|authentication = \"jitsi-anonymous\"|authentication = \"token\"|}" "$PROSODY_FILE" | sed -i "$SRP_STR,$SRP_END{s|authentication = \"jitsi-anonymous\"|authentication = \"token\"|}" "$PROSODY_FILE" | ||||||
| sed -i "s|--app_id=\"example_app_id\"|app_id=\"$APP_ID\"|" "$PROSODY_FILE" | sed -i "s|--app_id=\"example_app_id\"|app_id=\"$APP_ID\"|" "$PROSODY_FILE" | ||||||
| sed -i "s|--app_secret=\"example_app_secret\"|app_secret=\"$SECRET_APP\"|" "$PROSODY_FILE" | sed -i "s|--app_secret=\"example_app_secret\"|app_secret=\"$SECRET_APP\"|" "$PROSODY_FILE" | ||||||
| sed -i "/app_secret/a \\\\" "$PROSODY_FILE" | sed -i "/app_secret/a \\\\" "$PROSODY_FILE" | ||||||
|  | ## Only token owners can create, open the room and become moderators: allow_empty_token = false | ||||||
|  | ## other participants are redirected authentication to guest. | ||||||
| sed -i "/app_secret/a \ \ \ \ allow_empty_token = false" "$PROSODY_FILE" | sed -i "/app_secret/a \ \ \ \ allow_empty_token = false" "$PROSODY_FILE" | ||||||
| sed -i "/app_secret/a \\\\" "$PROSODY_FILE" | sed -i "/app_secret/a \\\\" "$PROSODY_FILE" | ||||||
| sed -i "/app_secret/a \ \ \ \ asap_accepted_issuers = { \"$APP_ID\" }" "$PROSODY_FILE" | sed -i "/app_secret/a \ \ \ \ asap_accepted_issuers = { \"$APP_ID\" }" "$PROSODY_FILE" | ||||||
| sed -i "/app_secret/a \ \ \ \ asap_accepted_audiences = { \"$APP_ID\", \"RocketChat\" }" "$PROSODY_FILE" | sed -i "/app_secret/a \ \ \ \ asap_accepted_audiences = { \"$APP_ID\" }" "$PROSODY_FILE" | ||||||
| sed -i "/app_secret/a \\\\" "$PROSODY_FILE" | sed -i "/app_secret/a \\\\" "$PROSODY_FILE" | ||||||
| sed -i "s|--allow_empty_token =.*|allow_empty_token = false|" "$PROSODY_FILE" | sed -i "s|--allow_empty_token =.*|allow_empty_token = false|" "$PROSODY_FILE" | ||||||
| sed -i 's|--"token_verification"|"token_verification"|' "$PROSODY_FILE" | sed -i 's|--"token_verification"|"token_verification"|' "$PROSODY_FILE" | ||||||
|  | sed -i "/muc_lobby_rooms/a \ \ \ \ \ \ \ \ \"persistent_lobby\";" "$PROSODY_FILE" | ||||||
|  | sed -i "/token_verification/a \ \ \ \ \ \ \ \ \"muc_wait_for_host\";" "$PROSODY_FILE" | ||||||
| 
 | 
 | ||||||
| # Request auth | # Set JWT and Guest settings | ||||||
| ## JWT via Prosody: don't touch Jicofo | ## Harden JWT auth, preventing "free" moderator by racing into room, | ||||||
| #sed -i "s|#org.jitsi.jicofo.auth.URL=EXT_JWT:|org.jitsi.jicofo.auth.URL=EXT_JWT:|" "$JICOFO_SIP" | ## only participants with token with moderator:true. | ||||||
|  | sed -i '1ijicofo.conference.enable-auto-owner = false' "$JICOFO_CONF" | ||||||
|  | ## config.js | ||||||
| sed -i "s|// anonymousdomain: 'guest.example.com'|anonymousdomain: \'guest.$DOMAIN\'|" "$MEET_CONF" | sed -i "s|// anonymousdomain: 'guest.example.com'|anonymousdomain: \'guest.$DOMAIN\'|" "$MEET_CONF" | ||||||
| 
 | 
 | ||||||
| # Enable jibri recording |  | ||||||
| cat  << REC-JIBRI >> "$PROSODY_FILE" |  | ||||||
| 
 |  | ||||||
| VirtualHost "recorder.$DOMAIN" |  | ||||||
|   modules_enabled = { |  | ||||||
|     "ping"; |  | ||||||
|   } |  | ||||||
|   authentication = "internal_hashed" |  | ||||||
| 
 |  | ||||||
| REC-JIBRI |  | ||||||
| 
 |  | ||||||
| # Setup guests and lobby | # Setup guests and lobby | ||||||
| cat << P_SR >> "$PROSODY_FILE" | cat << P_SR >> "$PROSODY_FILE" | ||||||
| VirtualHost "guest.$DOMAIN" | VirtualHost "guest.$DOMAIN" | ||||||
|     authentication = "jitsi-anonymous" |     authentication = "anonymous" | ||||||
|     c2s_require_encryption = false |     c2s_require_encryption = false | ||||||
|     speakerstats_component = "speakerstats.$DOMAIN" |     speakerstats_component = "speakerstats.$DOMAIN" | ||||||
| 
 | 
 | ||||||
|  | @ -93,15 +99,12 @@ echo -e "\nUse the following for your App (e.g. Rocket.Chat):\n" | ||||||
| echo -e "\nAPP_ID: $APP_ID" && \ | echo -e "\nAPP_ID: $APP_ID" && \ | ||||||
| echo -e "SECRET_APP: $SECRET_APP\n" | echo -e "SECRET_APP: $SECRET_APP\n" | ||||||
| 
 | 
 | ||||||
| echo -e "You can test JWT authentication with the following token for the next hour:\n" | echo -e "You can test JWT authentication with the following token for the next 2 hours:\n" | ||||||
| pyjwt3 --key="$SECRET_APP" \ | python3 tools/jitsi_token_maker_features.py \ | ||||||
|     encode \ |   --app-id "$APP_ID" --secret-file - \ | ||||||
|     --alg HS256 \ |   --domain "$DOMAIN" --room "$ROOM" \ | ||||||
|     group="Rocket.Chat" \ |   --moderator --features-all \ | ||||||
|     aud="$APP_ID" \ |   --minutes 120 --nbf-offset 300 --include-iat \ | ||||||
|     iss="$APP_ID" \ |   --url "https://$DOMAIN/" <<<"$APP_SECRET" | ||||||
|     sub="$DOMAIN" \ |  | ||||||
|     room="*" \ |  | ||||||
|     exp="$(($(date +%s)+3600))" |  | ||||||
| 
 | 
 | ||||||
| read -n 1 -s -r -p $'\n'"Press any key to continue..."$'\n' | read -n 1 -s -r -p $'\n'"Press any key to continue..."$'\n' | ||||||
|  |  | ||||||
|  | @ -819,9 +819,6 @@ restart_services() { | ||||||
|     check_jibri |     check_jibri | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| # Configure Jvb2 |  | ||||||
| sed -i "/shard.HOSTNAME/s|localhost|$DOMAIN|" "$JVB2_SIP" |  | ||||||
| 
 |  | ||||||
| #-------------------------------------------------- | #-------------------------------------------------- | ||||||
| print_title "Configure Jibri" | print_title "Configure Jibri" | ||||||
| #-------------------------------------------------- | #-------------------------------------------------- | ||||||
|  | @ -839,16 +836,18 @@ if [ "$ENABLE_SC" = "yes" ]; then | ||||||
| 
 | 
 | ||||||
| fi | fi | ||||||
| sleep .1 | sleep .1 | ||||||
| #Enable jibri recording |  | ||||||
| cat  << REC-JIBRI >> "$PROSODY_FILE" |  | ||||||
| 
 | 
 | ||||||
| VirtualHost "recorder.$DOMAIN" | # Test built-in setup for jibri recorder. | ||||||
|   modules_enabled = { | ##Enable jibri recording by defatul (SC & JWT) | ||||||
|     "ping"; | #cat  << REC-JIBRI >> "$PROSODY_FILE" | ||||||
|   } | # | ||||||
|   authentication = "internal_hashed" | #VirtualHost "recorder.$DOMAIN" | ||||||
| 
 | #  modules_enabled = { | ||||||
| REC-JIBRI | #    "ping"; | ||||||
|  | #  } | ||||||
|  | #  authentication = "internal_hashed" | ||||||
|  | # | ||||||
|  | #REC-JIBRI | ||||||
| 
 | 
 | ||||||
| #Enable Jibri withelist | #Enable Jibri withelist | ||||||
| sed -i "s|-- muc_lobby_whitelist|muc_lobby_whitelist|" "$PROSODY_FILE" | sed -i "s|-- muc_lobby_whitelist|muc_lobby_whitelist|" "$PROSODY_FILE" | ||||||
|  |  | ||||||
|  | @ -0,0 +1,158 @@ | ||||||
|  | #!/usr/bin/env python3 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | """ | ||||||
|  | JWT generator for self‑hosted Jitsi (also compatible with JAAS if desired) | ||||||
|  | - HS256 (HMAC) signing using only Python standard library (no external deps). | ||||||
|  | - Flags to omit exp/nbf (test tokens), include iat, and read secret from file/STDIN. | ||||||
|  | - Flags to populate context.features: recording, livestreaming, transcription, sip-in/out. | ||||||
|  | - Robust URL construction (escapes the room name). | ||||||
|  | """ | ||||||
|  | import argparse, base64, hashlib, hmac, json, time, sys | ||||||
|  | from urllib.parse import quote | ||||||
|  | 
 | ||||||
|  | def b64url(data: bytes) -> str: | ||||||
|  |     return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii") | ||||||
|  | 
 | ||||||
|  | def sign_hs256(secret: str, signing_input: str) -> str: | ||||||
|  |     sig = hmac.new(secret.encode("utf-8"), signing_input.encode("ascii"), hashlib.sha256).digest() | ||||||
|  |     return b64url(sig) | ||||||
|  | 
 | ||||||
|  | def main(): | ||||||
|  |     p = argparse.ArgumentParser(description="JWT generator for Jitsi (HS256)") | ||||||
|  |     # Identity / target | ||||||
|  |     p.add_argument("--app-id", required=True, help="app_id configured in Prosody/JAAS") | ||||||
|  |     p.add_argument("--secret", required=False, help="app_secret (HMAC/HS256)") | ||||||
|  |     p.add_argument("--secret-file", help="Read secret from file or '-' for STDIN") | ||||||
|  |     p.add_argument("--domain", help="Jitsi domain (e.g. meet.example.com) used as 'sub' in self-hosted") | ||||||
|  |     p.add_argument("--room", default="*", help="Target room (or '*' for all)") | ||||||
|  |     # Time | ||||||
|  |     p.add_argument("--minutes", type=int, default=60, help="Validity (minutes). Ignored if --no-exp") | ||||||
|  |     p.add_argument("--no-exp", action="store_true", help="Do not include 'exp' (tests only)") | ||||||
|  |     p.add_argument("--nbf-offset", type=int, default=10, help="Backdating seconds for 'nbf' (default: 10)") | ||||||
|  |     p.add_argument("--no-nbf", action="store_true", help="Do not include 'nbf' (tests only)") | ||||||
|  |     p.add_argument("--include-iat", action="store_true", help="Include 'iat'=now") | ||||||
|  |     # User | ||||||
|  |     p.add_argument("--user-name", default=None, help="User display name") | ||||||
|  |     p.add_argument("--user-email", default=None, help="User email") | ||||||
|  |     p.add_argument("--user-id", default=None, help="User unique ID") | ||||||
|  |     p.add_argument("--avatar", default=None, help="Avatar URL") | ||||||
|  |     p.add_argument("--moderator", action="store_true", help="Grant moderator role via token") | ||||||
|  |     p.add_argument("--moderator-as-string", action="store_true", | ||||||
|  |                    help="Use 'moderator': 'true'/'false' (string) instead of boolean") | ||||||
|  |     # Features (self-hosted with enableFeaturesBasedOnToken) | ||||||
|  |     p.add_argument("--feature-recording", action="store_true", help="Enable 'recording' in context.features") | ||||||
|  |     p.add_argument("--feature-livestreaming", action="store_true", help="Enable 'livestreaming' in context.features") | ||||||
|  |     p.add_argument("--feature-transcription", action="store_true", help="Enable 'transcription' in context.features") | ||||||
|  |     p.add_argument("--feature-sip-in", action="store_true", help="Enable 'sip-inbound-call' in context.features") | ||||||
|  |     p.add_argument("--feature-sip-out", action="store_true", help="Enable 'sip-outbound-call' in context.features") | ||||||
|  |     p.add_argument("--features-all", action="store_true", help="Enable all the features above") | ||||||
|  |     # Overrides / modes | ||||||
|  |     p.add_argument("--aud", default=None, help="Override 'aud' (default: app_id in self-hosted)") | ||||||
|  |     p.add_argument("--iss", default=None, help="Override 'iss' (default: app_id in self-hosted)") | ||||||
|  |     p.add_argument("--jaas", action="store_true", | ||||||
|  |                    help="JAAS mode: aud='jitsi', iss='chat', sub=app_id (ignores --domain for 'sub')") | ||||||
|  |     # Output | ||||||
|  |     p.add_argument("--url", default=None, | ||||||
|  |                    help="If provided (e.g. 'https://meet.example.com/'), prints full join URL with ?jwt=") | ||||||
|  |     p.add_argument("--print-json", action="store_true", help="Print payload JSON to STDERR (debug)") | ||||||
|  | 
 | ||||||
|  |     args = p.parse_args() | ||||||
|  | 
 | ||||||
|  |     # Secret: --secret-file takes precedence | ||||||
|  |     secret = args.secret | ||||||
|  |     if args.secret_file: | ||||||
|  |         if args.secret_file == "-": | ||||||
|  |             secret = sys.stdin.read().strip() | ||||||
|  |         else: | ||||||
|  |             with open(args.secret_file, "r", encoding="utf-8") as fh: | ||||||
|  |                 secret = fh.read().strip() | ||||||
|  |     if not secret: | ||||||
|  |         p.error("You must provide --secret or --secret-file (or --secret-file - for STDIN).") | ||||||
|  | 
 | ||||||
|  |     now = int(time.time()) | ||||||
|  |     exp = None if args.no_exp else (now + args.minutes * 60) | ||||||
|  |     nbf = None if args.no_nbf else (now - max(args.nbf_offset, 0)) | ||||||
|  | 
 | ||||||
|  |     # Header | ||||||
|  |     header = {"typ": "JWT", "alg": "HS256"} | ||||||
|  | 
 | ||||||
|  |     # Base claims by mode | ||||||
|  |     if args.jaas: | ||||||
|  |         aud = "jitsi" | ||||||
|  |         iss = "chat" | ||||||
|  |         sub = args.app_id | ||||||
|  |     else: | ||||||
|  |         if not args.domain: | ||||||
|  |             p.error("--domain is required in self-hosted mode (without --jaas).") | ||||||
|  |         aud = args.aud or args.app_id | ||||||
|  |         iss = args.iss or args.app_id | ||||||
|  |         sub = args.domain | ||||||
|  | 
 | ||||||
|  |     # User / contexto | ||||||
|  |     user = {} | ||||||
|  |     if args.user_id: user["id"] = args.user_id | ||||||
|  |     if args.user_name: user["name"] = args.user_name | ||||||
|  |     if args.user_email: user["email"] = args.user_email | ||||||
|  |     if args.avatar: user["avatar"] = args.avatar | ||||||
|  |     if args.moderator: | ||||||
|  |         if args.moderator_as_string: | ||||||
|  |             user["moderator"] = "true" | ||||||
|  |         else: | ||||||
|  |             user["moderator"] = True | ||||||
|  | 
 | ||||||
|  |     # Features | ||||||
|  |     features = {} | ||||||
|  |     if args.features_all: | ||||||
|  |         features = { | ||||||
|  |             "recording": True, | ||||||
|  |             "livestreaming": True, | ||||||
|  |             "transcription": True, | ||||||
|  |             "sip-inbound-call": True, | ||||||
|  |             "sip-outbound-call": True | ||||||
|  |         } | ||||||
|  |     else: | ||||||
|  |         if args.feature_recording:     features["recording"] = True | ||||||
|  |         if args.feature_livestreaming: features["livestreaming"] = True | ||||||
|  |         if args.feature_transcription: features["transcription"] = True | ||||||
|  |         if args.feature_sip_in:        features["sip-inbound-call"] = True | ||||||
|  |         if args.feature_sip_out:       features["sip-outbound-call"] = True | ||||||
|  | 
 | ||||||
|  |     context = {} | ||||||
|  |     if user: context["user"] = user | ||||||
|  |     if features: context["features"] = features | ||||||
|  | 
 | ||||||
|  |     payload = { | ||||||
|  |         "aud": aud, | ||||||
|  |         "iss": iss, | ||||||
|  |         "sub": sub, | ||||||
|  |         "room": args.room, | ||||||
|  |     } | ||||||
|  |     if context: | ||||||
|  |         payload["context"] = context | ||||||
|  |     if exp is not None: | ||||||
|  |         payload["exp"] = exp | ||||||
|  |     if nbf is not None: | ||||||
|  |         payload["nbf"] = nbf | ||||||
|  |     if args.include_iat: | ||||||
|  |         payload["iat"] = now | ||||||
|  | 
 | ||||||
|  |     # Build JWT manually | ||||||
|  |     signing_input = f"{b64url(json.dumps(header, separators=(',', ':'), ensure_ascii=False).encode())}." \ | ||||||
|  |                     f"{b64url(json.dumps(payload, separators=(',', ':'), ensure_ascii=False).encode())}" | ||||||
|  |     signature = sign_hs256(secret, signing_input) | ||||||
|  |     token = f"{signing_input}.{signature}" | ||||||
|  | 
 | ||||||
|  |     if args.print_json: | ||||||
|  |         print(json.dumps(payload, indent=2, ensure_ascii=False), file=sys.stderr) | ||||||
|  | 
 | ||||||
|  |     if args.url: | ||||||
|  |         base = args.url if args.url.endswith("/") else args.url + "/" | ||||||
|  |         room_path = "" if args.room == "*" else quote(args.room, safe="") | ||||||
|  |         join_url = base + room_path | ||||||
|  |         sep = "&" if "?" in join_url else "?" | ||||||
|  |         print(f"{join_url}{sep}jwt={token}") | ||||||
|  |     else: | ||||||
|  |         print(token) | ||||||
|  | 
 | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     main() | ||||||
		Loading…
	
		Reference in New Issue