Prevent unauthorized viewers from downloading your content or embedding your player on sites that you do not own.
JWP enables you to create expiring, signed URLs to secure your content and player. Using signed URLs for your public content or player prevents unauthorized viewers from downloading your content or embedding your player on sites that you do not own.
To enable URL signing on your media content and players, you must complete two general processes:
- Create signed (non-JWT or JWT) URLs.
- Enable URL signing functionality
For additional information about video URL signing by content and players, see Secure signing behavior.
Types of Signed URLs
The Delivery API supports two methods of signing URLs:
- JSON web token (JWT) tokenized
- Signed non-JWT (non-JWT)
The following table identifies when to use each type of signed URL.
Signing Type | Description |
---|---|
JWT | Applies when you reference any v2 endpoints and routes
Advertising Schedule1 Media
|
Non-JWT | Applies when you reference any v2 endpoints and routes
Players1 Poster Image Streaming Manifests Text Tracks Video Files |
1. Denotes content that can be accessed only through this route of the Delivery API.
Signed URL Creation
Create a signed JWT URL
To create a JWT URL, you must append a token parameter to the URL of the content or player. The token parameter is comprised of three Base64-URL strings separated by dots that can be easily passed in HTML and HTTP environments.
To generate the token parameter you need a header, payload, and signature. The next three subsections define the header, payload, and signature. Then, the Generate the JWT signed URL subsection provides code examples to generate the JWT signed URL.
Header
The header specifies the cryptographic algorithm and token type. JW Player currently supports a single algorithm and token type. All headers should include an object that contains alg
and typ
.
{
"alg": "HS256",
"typ": "JWT"
}
Parameter | Description |
---|---|
alg* string | Signing algorithm used
This is always HMAC SHA256 ( HS256 )
|
type* string | Type of token
This is always JWT .
|
Payload
The payload consists of claims that specify a resource (resource
) being requested, an expiration time (exp
), and any parameters the route accepts. All URL parameters that you want to include must be included in the payload. Any URL parameter added to a JWT-signed request will be ignored if it is not within the payload.
The following code example includes related_media_id
as an additional parameter.
Be sure that all UNIX timestamps are in seconds. Smaller units of time (such as milliseconds) will not be interpreted correctly.
{
"resource": "/v2/playlists/Xw0oaD4q",
"exp": 1893456000,
"related_media_id": "RltV8MtT"
}
Parameter | Description |
---|---|
exp* number | Expiration date of the token, as a UNIX timestamp in seconds, for example, 1271338236.
Generated URLs should be valid between a minute and a few hours. The shorter you make the duration until an expiration date, the more secure you make your content. Once a link has expired, even download tools will not be able to grab the content. However, overly short expirations can result in a bad user experience due to small discrepancies in server time or delays in clients requesting resources. If you have a high-volume website, you should cache signed URLs (for example in intervals of 5 minutes) to prevent performance issues that may occur due to generating signed URLs. Signed requests do not have to be unique. |
resource* string | Content or player that is being requested
This can be a relative or absolute URL. This property ensures that generated tokens cannot be applied to unintended resources. |
Signature
The signature is comprised of the encoded header, the encoded payload, and the property secret.
Use the following steps to locate your property secret:
- From your JW Player dashboard, click the gear next to your name > API Credentials.
- In the v1 API Credentials section, click SHOW CREDENTIALS next to a property name.
- Copy the Secret.
Generate the JWT signed URL
The following code examples show approaches to programmatically generate a JWT signed URL. Links are valid for 1 hour and must be normalized to 6 minutes to promote better caching.
The following samples are provided for guidance and may not work in your environment. If you use any of these samples, be sure to test the functionality in a development environment before deploying it into a production environment.
const jwt = require('jsonwebtoken');
const Time = new Date();
const API_SECRET = 'V1APISECRET';
/**
* Generatea a URL with signature.
* @param {string} path
* @param {string} host
* @returns {string} signed URL
*/
function jwt_signed_url(path, host = 'https://cdn.jwplayer.com') {
const token = jwt.sign(
{
exp: Math.ceil((Time.getTime() / 1000 + 3600) / 300) * 300,
resource: path,
},
API_SECRET,
{
noTimestamp: true
}
);
return `${host}${path}?token=${token}`;
}
const media_id = 'MEDIAID';
const path = `/v2/media/${media_id}`;
const url = jwt_signed_url(path);
console.log(url);
import math
import time
import urllib.parse
# Requires installing third party packages:
# $ pip install python-jose requests
from jose import jwt
import requests
API_SECRET = os.environ["JWPLATFORM_V1API_SECRET"]
def jwt_signed_url(path, host="https://cdn.jwplayer.com"):
"""
Generate url with signature.
Args:
path (str): url path
host (str): url host
"""
# Link is valid for 1 hour but normalized to 6 minutes to promote better caching
exp = math.ceil((time.time() + 3600) / 300) * 300
params = {}
params["resource"] = path
params["exp"] = exp
# Generate token
# note that all parameters must be included here
token = jwt.encode(params, API_SECRET, algorithm="HS256")
url = "{host}{path}?token={token}".format(host=host, path=path, token=token)
return url
media_id = "YOUR MEDIA ID" # Replace
path = "/v2/media/{media_id}".format(media_id=media_id)
url = jwt_signed_url(path)
r = requests.get(url)
print(r.json())
Create a signed non-JWT URL
To create a non-JWT URL, you must append two parameters to the URL of the content or player: exp
and sig
.
http://cdn.jwplayer.com/videos/nPripu9l.mp4?exp=1371335018&sig=a0124258c73177029d09bb82c6608392
Parameter | Description |
---|---|
exp* | Expiration date of the URL as a UNIX timestamp in seconds, for example, 1271338236
Typically, generated URLs should be valid between a minute and a few hours. The shorter you make the duration until an expiration date, the more secure you make your content. Once a link has expired, even download tools will not be able to grab the content. However, overly short expirations can result in a bad user experience due to small discrepancies in server time or delays in clients requesting resources. |
sig* | Signature used to authorize the request
See: sig parameter |
sig parameter
The signature (sig
) is an MD5 digest of the path, the expiration date, and the account secret.
md5(CONTENT_PATH:EXPIRATION_STAMP:ACCOUNT_SECRET)
Property | Description |
---|---|
ACCOUNT_SECRET | Property secret from the v1 API
Use the following steps to locate your property secret:
|
CONTENT_PATH | Only the path portion of the URL without the domain or leading slash, for example, videos/nPripu9l.mp4
|
EXPIRATION_STAMP | Expiration date of the URL as a UNIX timestamp in seconds, for example, 1271338236
|
Generate the non-JWT signed URL
The following code examples show approaches to programmatically generate a non-JWT signed URL. Links are valid for 1 hour and must be normalized to 5 minutes to promote better caching.
The following samples are provided for guidance and may not work in your environment. If you use any of these samples, be sure to test the functionality in a development environment before deploying it into a production environment.
const MD5 = require('crypto-js/md5');
const Time = new Date();
const API_SECRET = 'V1APISECRET';
/**
* Returns a signed url, can be used for any "non-JWT" endpoint
* @param {string} path
* @param {int} expires
* @param {string} secret
* @param {string}host
* @returns {string} A signed url
*/
function signed_url(
path,
expires = 6000,
secret = API_SECRET,
host = 'https://cdn.jwplayer.com'
) {
const base = `${path}:${expires}:${secret}`;
const signature = MD5(base);
return `${host}/${path}?exp=${expires}&sig=${signature}`;
}
/**
* Return signed url for the single line embed javascript
* @param {string} mediaid The media id (also referred to as video key)
* @param {string} playerid The player id (also referred to as player key)
* @returns
*/
function get_response(mediaid, playerid) {
const path = `players/${mediaid}-${playerid}.js`;
const expires = Math.ceil((Time.getTime() / 1000 + 3600) / 300) * 300;
return signed_url(path, expires);
}
console.log(get_response('MEDIAID', 'PLAYERID'));
import hashlib
import math
import time
API_SECRET = os.environ["JWPLATFORM_V1API_SECRET"] # Replace
def signed_url(path, expires, secret=API_SECRET, host="https://cdn.jwplayer.com"):
"""
returns a signed url, can be used for any "non-JWT" endpoint
Args:
path(str): the jw player route
expires(int): the expiration time for the URL
secret(str): JW account secret
host:(str): url host
"""
s = "{path}:{exp}:{secret}".format(path=path, exp=str(expires), secret=API_SECRET)
signature = hashlib.md5(s.encode("utf-8")).hexdigest()
signed_params=dict(exp=expires, sig=signature)
return "{host}/{path}?{params}".format(
host=host, path=path, params=urllib.parse.urlencode(signed_params)
)
def get_signed_player(media_id, player_id):
"""
Return signed url for the single line embed javascript.
Args:
media_id (str): the media id (also referred to as video key)
player_id (str): the player id (also referred to as player key)
"""
path = "players/{media_id}-{player_id}.js".format(
media_id=media_id, player_id=player_id
)
# Link is valid for 1 hour but normalized to 5 minutes to promote better caching
expires = math.ceil((time.time() + 3600) / 300) * 300
# Generate signature
return signed_url(path, expires)
media_id = "YOUR MEDIA ID" # Replace
player_id = "YOUR PLAYER ID" # Replace
signed_url = get_signed_player(media_id, player_id)
print(signed_url)
print(
"<script type='text/javascript' src='{signed_url}'></script>".format(
signed_url=signed_url
)
)
Enable URL signing functionality
After you have created signed URLs for all of your content, you must enable URL signing functionality for your properties.
- From the Properties page of your JW Player dashboard, click the name of the property. The settings page for the property appears.
- On the Content Protection tab in the URL Signing section at the bottom, click the toggle to Secure Video URLs or Secure Player Embeds. Depending on your use case, you can also enable both settings at the same time.
- Click SAVE.
Error handling
Error code | Error message | Possible conditions |
---|---|---|
200 | Success | Content is requested via a signed URL when URL signing is enabled for all publisher content. |
403 | Access forbidden | Content is requested via an unsigned URL when URL signing is enabled for all publisher content. Content is requested via an incorrectly signed URL when URL signing is enabled for all publisher content. |
FAQ
Does URL token signing work the same for JWP hosted and externally hosted (registered) media?
No.
For JW Platform hosted media, both the request to the Delivery API and all media URLs will be signed.
For externally hosted (registered) media, only the request to the Delivery API and the returned media URL will be signed. The signed media URL from the Delivery API response will redirect (HTTP status code 302
) to the externally hosted media URL. JW Platform will not sign the externally hosted media URL.