Skip to content

Instantly share code, notes, and snippets.

@SamWhited
Last active August 31, 2016 15:22
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 SamWhited/0ceab67b05668e6c6dcf8c05aab1243c to your computer and use it in GitHub Desktop.
Save SamWhited/0ceab67b05668e6c6dcf8c05aab1243c to your computer and use it in GitHub Desktop.
Jitsi Meet mod_token_auth ASAP test server
testtoken
main
*.sw[op]
// The testtoken command creates a new ASAP token for a Jitsi Meet instance,
// signs it, and serves the public key which can be used to verify that
// signature in such a way that Jitsi Meet's mod_token_auth Prosody plugin can
// find it.
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"errors"
"flag"
"fmt"
"log"
"net/http"
"net/url"
"path"
"time"
"golang.org/x/oauth2/jws"
)
// Number of random bytes generated for random room names.
const randLen = 8
func main() {
var appid, listen, root, room, kid, dur string
flag.StringVar(&root, "url", "https://sam.jitsi.net", "The root URL to create a token for.")
flag.StringVar(&room, "room", "", "The room name for the token (random if empty).")
flag.StringVar(&kid, "kid", "/myapp/mykey.pem", "The key ID (path on the keyserver) for an ASAP public key.")
flag.StringVar(&dur, "dur", "1h", "The duration of the token.")
flag.StringVar(&listen, "listen", ":8080", "The address to listen on.")
flag.StringVar(&appid, "iss", "lonely", "The issuer (application ID) of the token.")
flag.Parse()
var err error
if room == "" {
if room, err = genRoomName(); err != nil {
log.Fatal("Error while generating room name:", err)
}
}
rootURL, err := url.Parse(root)
if err != nil {
log.Fatalf(`Error while parsing URL "%s": %v`, root, err)
}
// Get values for the time claims (expiration and issued at)
now := time.Now()
duration, err := time.ParseDuration(dur)
if err != nil {
log.Fatal("Error while parsing token duration:", err)
}
exp := now.Unix() + int64(duration.Seconds())
// Generate a private key to use
privkey, err := rsa.GenerateKey(cryptoRand{}, 2048)
if err != nil {
log.Fatal("Error generating private key:", err)
}
x509pubkey, err := x509.MarshalPKIXPublicKey(privkey.Public())
if err != nil {
log.Fatal("Error saving RSA public key:", err)
}
header := &jws.Header{
Algorithm: "RS256",
Typ: "JWT",
KeyID: kid,
}
claims := &jws.ClaimSet{
Exp: exp,
Iss: appid,
Iat: now.Unix(),
PrivateClaims: map[string]interface{}{
"room": room,
},
}
token, err := jws.Encode(header, claims, privkey)
if err != nil {
log.Fatalf("Error while encoding token:", err)
}
rootURL.Path = path.Join(rootURL.Path, room)
rootURL.RawQuery = "jwt=" + token
fmt.Println(rootURL.String())
pubBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: x509pubkey,
}
h := sha256.New()
if _, err = h.Write([]byte(kid)); err != nil {
log.Fatalf("Error while processing file path:", err)
}
pathBytes := h.Sum(nil)
p := hex.EncodeToString(pathBytes) + ".pem"
http.HandleFunc(p, func(w http.ResponseWriter, r *http.Request) {
time.Sleep(30 * time.Second)
if err := pem.Encode(w, pubBlock); err != nil {
log.Println("Error while saving key to output stream:", err)
}
})
fmt.Printf("Serving public key at %s…\n", path.Join(listen, p))
log.Fatal(http.ListenAndServe(listen, nil))
}
// Generate a random room name.
func genRoomName() (string, error) {
b := make([]byte, randLen)
n, err := rand.Read(b)
switch {
case err != nil:
return "", err
case n != randLen:
return "", errors.New("Failed to read enough randomness")
}
return base64.RawURLEncoding.EncodeToString(b), nil
}
type cryptoRand struct{}
func (cryptoRand) Read(p []byte) (int, error) {
return rand.Read(p)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment