Skip to content

Instantly share code, notes, and snippets.

@chriscorcoran
Last active July 9, 2021 21:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chriscorcoran/a43f5a995b9c5f3b8d0cf1e7b42fb231 to your computer and use it in GitHub Desktop.
Save chriscorcoran/a43f5a995b9c5f3b8d0cf1e7b42fb231 to your computer and use it in GitHub Desktop.

Code snippets for a tutorial on building a video app with JaaS, React and Vercel

ALLOW_LIST='walter@gooddogs.com,scout@gooddogs.com'
ALLOW_LIST=
JAAS_ROOM_NAME=
JAAS_APP_ID=
JAAS_KEY_ID=
JAAS_PRIVATE_KEY=
JAAS_ROOM_NAME=AllAboutThatJAAS
cat {Key Name}.pk | base64
rm ./src/App.css ./src/index.css ./src/logo.svg
npx create-react-app jaas-hands
cd jaas-hands
npm start
npm install bootstrap bootstrap-react uuid jsonwebtoken
npm install --global vercel
const generateAvatar = (email) => {
const hash = crypto.createHash('md5').update(email).digest("hex");
return 'https://www.gravatar.com/avatar/' + hash;
}
// JWT Generator -- Takes user's name and email address
const generateJWT = (name, email, roomName) => {
// Configure the JWT Header
const key = process.env.JAAS_KEY_ID;
const header = { algorithm: 'RS256', keyid: key };
// Get the current time and set the time boundaries of the JWT
const now = new Date();
const expiration = Math.round(now.setHours(now.getHours() + 3) / 1000);
const notBefore = (Math.round((new Date).getTime() / 1000) - 10);
// Build the JWT Payload
const avatar = generateAvatar(email);
const id = uuid();
const payload = {
aud: 'jitsi',
context: {
user: {
id,
name,
avatar,
email: email,
moderator: 'true'
},
features: {
livestreaming: 'true',
recording: 'true',
transcription: 'true',
"outbound-call": 'true'
}
},
iss: 'chat',
room: '*',
sub: process.env.JAAS_APP_ID,
exp: expiration,
nbf: notBefore
};
// Load and decode the Private Key from the ENV
let buff = Buffer.from(process.env.JAAS_PRIVATE_KEY, 'base64');
const privateKey = buff.toString('ascii')
// Finally, sign the JWT
return jsonwebtoken.sign(
payload,
privateKey,
header
);
}
const isAllowed = (email) => {
const friendsList = process.env.ALLOW_LIST.split(',');
return (friendsList.indexOf(email) >= 0);
}
var jsonwebtoken = require('jsonwebtoken');
var uuid = require('uuid-random');
var crypto = require('crypto');
// Helper function to generate an avatar with the help of Gravatar
const generateAvatar = (email) => {
const hash = crypto.createHash('md5').update(email).digest("hex");
return 'https://www.gravatar.com/avatar/' + hash;
}
// Our super secure Access Management System :)
const isAllowed = (email) => {
const friendsList = process.env.ALLOW_LIST.split(',');
return (friendsList.indexOf(email) >= 0);
}
// Generate a Room Name
const generateRoomName = () => {
return process.env.JAAS_APP_ID + '/' + process.env.JAAS_ROOM_NAME;
}
// JWT Generator -- Takes user's name and email address
const generateJWT = (name, email, roomName) => {
// Configure the JWT Header
const key = process.env.JAAS_KEY_ID;
const header = { algorithm: 'RS256', keyid: key };
// Get the current time and set the time boundaries of the JWT
const now = new Date();
const expiration = Math.round(now.setHours(now.getHours() + 3) / 1000);
const notBefore = (Math.round((new Date).getTime() / 1000) - 10);
// Build the JWT Payload
const avatar = generateAvatar(email);
const id = uuid();
const payload = {
aud: 'jitsi',
context: {
user: {
id,
name,
avatar,
email: email,
moderator: 'true'
},
features: {
livestreaming: 'true',
recording: 'true',
transcription: 'true',
"outbound-call": 'true'
}
},
iss: 'chat',
room: '*',
sub: process.env.JAAS_APP_ID,
exp: expiration,
nbf: notBefore
};
// Load and decode the Private Key from the ENV
let buff = Buffer.from(process.env.JAAS_PRIVATE_KEY, 'base64');
const privateKey = buff.toString('ascii')
// Finally, sign the JWT
return jsonwebtoken.sign(
payload,
privateKey,
header
);
}
// Request Handler
export default async (req, res) => {
// Parse the JSON request
const { body } = req;
const email = body.email.trim().toLowerCase();
const roomName = generateRoomName();
// Construct our default response payload
res.statusCode = 403;
const payload = {
email: body.email,
success: false
};
// Check to see if the user is allowed, if so make them a JWT.
if (isAllowed(body.email)) {
payload.key = generateJWT(email, email, roomName);
payload.room = roomName;
payload.success = true;
res.statusCode = 200;
}
// Construct Response
res.setHeader("Content-Type", "application/json");
res.send(JSON.stringify(payload));
}
// Request Handler
export default async (req, res) => {
// Parse the JSON request
const { body } = req;
const email = body.email.trim().toLowerCase();
const roomName = generateRoomName();
// Construct our default response payload
res.statusCode = 403;
const payload = {
email: body.email,
success: false
};
// Check to see if the user is allowed, if so make them a JWT.
if (isAllowed(body.email)) {
payload.key = generateJWT(email, email, roomName);
payload.room = roomName;
payload.success = true;
res.statusCode = 200;
}
// Construct Response
res.setHeader("Content-Type", "application/json");
res.send(JSON.stringify(payload));
}
import { Container, Form, Button } from "react-bootstrap";
function App() {
return (
<div className="App">
<Container>
<h2>Lets JaaS It Up</h2>
<Form
style={{ display: allowed ? "none" : "block" }}>
<Form.Group>
<Form.Label htmlFor="email">
Email Address
</Form.Label>
<Form.Control
value={email}
onChange={(e) => setEmail(e.target.value)} />
</Form.Group>
<Button variant="primary" type="submit">Join</Button>
</Form>
<div style={{ display: allowed ? "block" : "none" }}>
<div id="jaas-container" style={{height: "700px"}}></div>
</div>
</Container>
</div>
);
}
import React, { useState, useEffect } from "react";
import { Container, Form, Button } from "react-bootstrap";
function App() {
// Setup our Hooks to manage state
const [email, setEmail] = useState("");
const [allowed, setAllowed] = useState(false);
return (
<div className="App">
<Container>
<h2>Lets JaaS It Up</h2>
<Form
style={{ display: allowed ? "none" : "block" }}>
<Form.Group>
<Form.Label htmlFor="email">
Email Address
</Form.Label>
<Form.Control
value={email}
onChange={(e) => setEmail(e.target.value)} />
</Form.Group>
<Button variant="primary" type="submit">Join</Button>
</Form>
<div style={{ display: allowed ? "block" : "none" }}>
<div id="jaas-container" style={{height: "700px"}}></div>
</div>
</Container>
</div>
);
}
import 'bootstrap/dist/css/bootstrap.min.css';
// Setup our Hooks to manage state
const [email, setEmail] = useState("");
const [allowed, setAllowed] = useState(false);
<Form onSubmit={onSubmit} style={{ display: allowed ? "none" : "block" }}>
useEffect(() => {
const script = document.createElement('script');
script.src = "https://8x8.vc/external_api.js";
script.async = true;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
}
}, []);
// Handle the user submission
const onSubmit = async (e) => {
await e.preventDefault();
const res = await fetch("/api/join", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({email: email})
});
const data = await res.json();
if(data.success) {
setAllowed(data.success);
setTimeout(() => { initJaas(data); }, 500 );
}
}
// Init the JaaS iFrame API once we have a JWT
const initJaas = (data) => {
new JitsiMeetExternalAPI("8x8.vc", {
roomName: data.room,
parentNode: document.querySelector('#jaas-container'),
jwt: data.key
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment