Skip to content

Secure video playback

By default, public videos can be viewed by anyone with just a video id. If you want to give access only to certain users, you have to make the video private and use signed URLs. After you mark a video private, it will no longer be accessed publicly. Instead, the user will need a signed url token to watch or download the video.

Common use cases for using signed URLs:

  • Restricting access to only logged in members
  • Allowing access only for a limited time period (ie. 24 hours)

1. Making videos require signed URLs

Only private videos require signed urls. You can set a video to private either through the GUI or the edit video ⧉ API endpoint.

2. Creating signing keys

Next, we'll need to create signing keys. They are used to generate JWT tokens to access private videos.

Signing keys can be managed from the Signing keys ⧉ API endpoints.

Note

Signing keys are different from API keys.

3. Generate JWT tokens

Tokens are generated on the client side using open source libraries. This way, you do not need to call a HeapStream endpoint each time you need to generate a token.

Claim Code Description Value
sub Subject of the JWT video.id
aud Audience (intended application of the token) v for access to all endpoints
exp Expiration time UNIX Epoch seconds when the token expires
nbf Not Before value UNIX Epoch seconds before which the token will not work

Code examples

# /// script
# requires-python = ">=3.13"
# dependencies = [
#     "pyjwt",
# ]
# ///
import base64
import time

import jwt

video_id = ""  # Enter your video id here
signing_key_id = ""  # Enter your signing key id here
private_key_base64_encoded_from_api = ""  # Enter your base64 encoded private key here


private_key = base64.b64decode(private_key_base64_encoded_from_api)

token = {
    "sub": video_id,
    "aud": "v",
    "exp": int(time.time()) + 3600,
}
headers = {"kid": signing_key_id}

json_web_token = jwt.encode(
    token,
    private_key,
    algorithm="RS256",
    headers=headers,
)

print(json_web_token)
package main

import (
    "encoding/base64"
    "fmt"
    "log"
    "time"

    "github.com/golang-jwt/jwt/v4"
)

func main() {
    // Your signing key id
    keyId := ""
    // Your base64 encoded private key
    base64privateKey := ""
    // Your video_id
    videoId := ""

    decodedKey, err := base64.StdEncoding.DecodeString(base64privateKey)
    if err != nil {
        log.Fatalf("Can not base64 decode private key: %v", err)
    }

    signKey, err := jwt.ParseRSAPrivateKeyFromPEM(decodedKey)
    if err != nil {
        log.Fatalf("Can not parse RSA private key: %v", err)
    }

    token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
        "sub": videoId,
        "aud": "v",
        "exp": time.Now().Add(time.Hour * 15).Unix(),
        "kid": keyId,
    })

    tokenString, err := token.SignedString(signKey)
    if err != nil {
        log.Fatalf("Can not generate token: %v", err)
    }

    fmt.Println(tokenString)
}

Include JWT token in the url

A list of urls that accept the JWT token:

Video page

This url will open the video page in full screen:

https://app.heapstream.com/e/<project_id>/<video_id>/?token=<jwt_token>

You can embed this url in an iFrame like so:

<iframe src="https://app.heapstream.com/e/<project_id>/<video_id>/?token=<jwt_token>" width="1280" height="720" frameborder="0" \
        allowFullScreen 
        style="border: none;"
        allow="fullscreen; accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; \
        picture-in-picture"></iframe>

HLS master playlist url

This url will point to the master HLS playlist:

https://app.heapstream.com/mmpl/<project_id>/<video_id>/?token=<jwt_token>

Poster url

https://app.heapstream.com/pstv/<project_id>/<poster_id>/?token=<jwt_token>

Text track HLS playlist

https://app.heapstream.com/mplt/<project_id>/<text_track_id>/?token=<jwt_token>

Preset HLS playlist

https://app.heapstream.com/mpl/<project_id>/<preset_id>/?token=<jwt_token>

Static MP4 rendition

https://app.heapstream.com/stcr4/<project_id>/<preset_id>/?token=<jwt_token>

Storyboard VTT playlist

https://app.heapstream.com/sbt/<int:project_id>/<int:storyboard_id>/?token=<jwt_token>

Video original url

Download the original video upload:

https://app.heapstream.com/dlo/<project_id>/<video_id>/?token=<jwt_token>

Warning

Project keep_originals setting needs to be True (default) for the download to work.