Private Videos
Restricting Playback
This guide is helpful for when you have a video you want to secure. This video could be paid content or sensitive.
By signing, even if someone has the playback ID, they cannot play the video back.
What This Does Not Accomplish
This does not make a video bulletproof unpirate-able.
DRMs protect videos, not JWTs.
It does, however, make the process of stealing and disseminating a video much more challenging.
High-Level Workflow
- Lock the video. Create or update an
Edge IDwith theis_privateboolean set to true. - Sign. Your backend code will generate a JSON Web Token (JWT) using your private signing key.
- Play. Take the JWT you generate and add it to the URL of the video.
Step 1: Generating Your Signing Key
Under “API Keys” in your app.hesedvid.com dashboard, select the option to create a new signing key.
You can choose to create a signing key which works for all your environments in your organization, or scoped to development, staging or production.
Save both the signing key and the key ID.
Step 2: Creating a JWT For Playback
When you create your JWT, there are a few arguments you should be aware of which you’ll be using in your code.
| Field | Type | Description |
|---|---|---|
sub | string | Subject. Pass through one edge ID or ”*” to allow playback of all signed videos. |
kid | string | Key ID. Include the ID of the key you generated in step 1. |
expiresIn | number | Expiration. How long the video can be watched, in seconds, before a new token is needed. |
import jwt from "jsonwebtoken";
const privateKey = Buffer.from( process.env.HESEDVID_PRIVATE_KEY!, "base64" // If you saved your private key as base64, you need to do this. If it's just a string, omit this.);
function signPlaybackToken( // Edge ID of the video you want to playback // Pass through "*" to make the JWT work for all edge IDs edgeID: string, // The ID of the key you generated in step 1. keyID: string, expiresInSeconds: number): string { return jwt.sign( { sub: edgeID }, privateKey, { algorithm: "RS256", expiresIn: expiresInSeconds, keyid: keyID } );}import base64import osfrom datetime import datetime, timedelta, timezone
import jwt # PyJWT
PRIVATE_KEY = base64.b64decode(os.environ["HESEDVID_PRIVATE_KEY"])KEY_ID = os.environ["HESEDVID_KEY_ID"]
def sign_playback_token(edge_id: str, expires_in_seconds: int) -> str: """ edge_id: Private edge ID the viewer may access. Pass through "*" to make the JWT work for all edge IDs expires_in_seconds: Lifetime of the token in seconds. """ payload = { "sub": edge_id, "exp": datetime.now(timezone.utc) + timedelta(seconds=expires_in_seconds) }
headers = {"kid": KEY_ID} # Signing key ID from the Hesedvid dashboard
return jwt.encode(payload, PRIVATE_KEY, algorithm="RS256", headers=headers)
token = sign_playback_token("edge_private_123", 15 * 60)print(token)package main
import ( "encoding/base64" "log" "os" "time"
"github.com/golang-jwt/jwt/v5")
// Helper function to sign a private edge ID (or "*" for all videos)func signPlaybackToken( // Edge ID of the video you want to playback // Pass through "*" to make the JWT work for all edge IDs edgeID, // The ID of the key you generated in step 1. keyID string, expiresIn time.Duration ) (string, error) { // edgeID: private edge ID the viewer may access // keyID: signing key ID from the Hesedvid dashboard // expiresIn: lifetime of the token
keyBytes, err := base64.StdEncoding.DecodeString(os.Getenv("HESEDVID_PRIVATE_KEY")) if err != nil { return "", err }
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(keyBytes) if err != nil { return "", err }
claims := jwt.RegisteredClaims{ Subject: edgeID, ExpiresAt: jwt.NewNumericDate(time.Now().Add(expiresIn)), }
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) token.Header["kid"] = keyID
return token.SignedString(privateKey)}
func main() { token, err := signPlaybackToken( "edge_private_123", os.Getenv("HESEDVID_KEY_ID"), 15*time.Minute, ) if err != nil { log.Fatal(err) }
log.Println("Signed JWT:", token)}Step 3: Playing Back Your Video
From step 2 you have a string. This is your JWT.
When you embed a URL, this key must be present to play your video.
Whereas before you might have had a video look like this:
<!-- Iframe example for public video --><iframe src="https://worker.hesedvid.com/v1/{edgeID}/master.m3u8" frameborder="0" allowfullscreen></iframe>Your protected video needs to look like this:
<!------------------------- Add the key as a URL parameter here ⬇️ --><iframe src="https://worker.hesedvid.com/v1/{edgeID}/master.m3u8?jwt={jwt}" frameborder="0" allowfullscreen></iframe>Caution
TODO: Verify that thumbnails are made private
Using Other Players
Under “Playing Videos” on the left, we have a few examples of video players you can use.
With each player, you pass through a URL like:
https://player.hesedvid.com/v1/{edgeID}/master.m3u8
Similar to the iframe, add the jwt URL param like so:
https://player.hesedvid.com/v1/{edgeID}/master.m3u8?jwt={jwt}
Secure Thumbnails
When a video is secure, so is a thumbnail. Similar to videos, when referencing a private thumbnail, you must pass through the key.
Whereas before you may have passed through a thumbnail to a public Edge ID like so:
<video id="video" controls poster="https://player.hesedvid.com/v1/{edgeID}/thumbnail.png"></video>Similarly to the video process, add your key as a URL parameter.
<!-- Add the key as a URL parameter here ⬇️ --><video id="video" controls poster="https://player.hesedvid.com/v1/{edgeID}/thumbnail.png?jwt={jwt}"></video>