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:
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:
Poster url¶
Text track HLS playlist¶
Preset HLS playlist¶
Static MP4 rendition¶
Storyboard VTT playlist¶
Video original url¶
Download the original video upload:
Warning
Project keep_originals
setting needs to be True (default) for the download to work.