Mux

The Mux Developer Hub

Welcome to the Mux developer hub. You'll find comprehensive guides and documentation to help you start working with Mux as quickly as possible, as well as support if you get stuck. Let's jump right in!

Get Started

Security: Signed URLs

Mux Playback IDs have two types: public and signed. Public playback URLs can be watched anywhere, any time. This means that public playback URLs can be embedded on other sites. For example, a user could "View Source," find the m3u8 URL, and publish it somewhere else (or post it to Reddit, or email it to a friend).

If this is not what you want, use a signed URL. Signed URLs are signed server-side by your application and are only playable for a short time.

Here's how to do this.

1. Create an Asset with a "Signed" Playback Policy

A Mux Video asset can have multiple playback IDs, each with a playback policy that is public or signed. The playback policy can be specified when creating a new asset, or it can be added after the fact via the playback policy API.

// POST https://api.mux.com/video/assets
{
  "input": "https://storage.googleapis.com/muxdemofiles/mux-video-intro.mp4",
  "playback_policy": "signed"
}

See Create a playback ID to learn how to add a new playback ID to an existing asset.

2. Create a signing key

Signing keys can be managed (created, deleted, listed) via the Mux Video API. When creating a new signing key, the API will generate a 2048-bit RSA key-pair and return the private key and a generated key-id; the public key will be stored at Mux to validate signed tokens.

Signing keys are created and deleted independently of assets. This enables key rotation at intervals of your choosing. Though you probably only need one signing key active at a time, there is no limit to the number of signing keys you can create. Store the private-key in a secure manner.

See Create a URL signing key for full documentation.

// POST https://api.mux.com/video/v1/signing-keys
{
  "data": {
    "private_key": (base64-encoded PEM file with private key),
    "id": "(unique signing-key identifier)",
    "created_at": "(UNIX Epoch seconds)”
  }
}

3. Generate a JSON Web Token

All signed requests will have a JWT with at least the following standard claims:

Claim Code
Description
Value

sub

Subject of the JWT

Mux Video Playback ID

aud

Audience (intended application of the token)

v (Video)
t (Thumbnail)
g (Gif)

exp

Expiration time

UNIX Epoch seconds when the token expires. This should always exceed the current-time plus the duration of the video, else portions of the video may be unplayable.

kid

Key Identifier

Key ID returned when signing key was created

The Thumbnail API accepts several options to control image selection and transformations.

For playback-id’s that use a public policy, the thumbnail options are supplied as query parameters on the request URL.

For playback-id’s that use a signed policy, the thumbnail options must be specified in the JWT claims when using signed URLs. This ensures that the thumbnail options are not altered, such as changing the timestamp or the dimensions of the thumbnail image. For example, if you uploaded a 4K video and wanted to restrict a thumbnail to a width of 600 pixels and a specific timestamp, then simply include the ‘width’ and ‘time’ keys in the JWT claims.

4. Signing the JWT

The steps can be summarized as:

  1. Load the private key used for signing
  2. Assemble the claims (sub, exp, kid, aud, etc) in a map
  3. Encode and sign the JWT using the claims map and private key and the RS256 algo.

There are dozens of software libraries for creating & reading JWTs. Whether you’re writing in Go, Elixir, Ruby, or a dozen other languages, don’t fret, there’s probably a JWT library that you can rely on.

// We've created some helper functions for Node to make your signing-life easier
const { JWT } = require('@mux/mux-node');

const playbackId = 'your-asset-playback-id';

// Set some base options we can use for a few different signing types
let baseOptions = {
  keyId: 'your-key-id',
  keySecret: 'your-key-secret',
};

const token = JWT.sign(playbackId, { ...baseOptions, type: 'video' });

// Now the signed playback url should look like this:
// `https://stream.mux.com/${playbackId}.m3u8?token=${token}`

// If you wanted to pass in params for something like a gif, use the
// `params` key in the options object
const gifToken = JWT.sign(playbackId, { 
  ...baseOptions, 
  type: 'gif',
  params: { time: 10 },
}
                          
// `https://image.mux.com/${playbackId}/animated.gif?token=${gifToken}`
require 'base64'
require 'jwt'

def sign_url(playback_id, audience, expires, signing_key_id, private_key, params = {})
	rsa_private = OpenSSL::PKey::RSA.new(Base64.decode64(private_key))
	payload = {sub: playback_id, exp: expires.to_i, kid: signing_key_id, aud: audience}
	payload.merge!(params)
	JWT.encode(payload, rsa_private, 'RS256')
end

sign_url('my_playback_id', 'v', Time.now + 3600, 'my_key_id', 'my_private_key')
import jwt
import base64
from datetime import datetime, timedelta

playback_id = '' # enter your playback id here
signing_key_id = '' # enter your signing key id here
private_key_base64 = '' # enter your base64 encoded private key here

private_key = base64.b64decode(private_key_base64)

token = {
    'sub': playback_id,
    'exp': (datetime.utcnow() + timedelta(minutes=60)).timestamp(),
    'aud': 'v'
}
headers = {
    'kid': signing_key_id
}

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

5. Include the JWT in the media URL

Supply the JWT in the resource URL using the ‘token’ query parameter. The Mux Video service will enforce the inspect and validate the JWT to make sure the request is allowed.

Video URL example:

https://stream.mux.com/{playback-id}.m3u8?token={JWT}
https://stream.mux.com/{playback-id}.m3u8

Thumbnail options are supplied as query parameters when using a public policy. When using a signed policy, the thumbnail options must be specified as claims in the JWT following the same naming conventions as with query parameters.

Thumbnail URL example:

https://image.mux.com/{playback-id}/thumbnail.{format}?token={JWT}
https://image.mux.com/{playback-id}/thumbnail.{format}?{thumbnail options}

Security: what's next

In the future, Signed URLs will be expanded to support additional security features, like geographic restriction.

Get in touch if you have other security needs.

Note on query parameters after signing

When you're signing a URL, you're signing the params for that URL as well. After the params are signed for a playback ID, the resulting signed URL should only contain the token param. This is important because leaving the parameters in the URL would both:

  • expose more information about the underlying asset than you may want
  • result in an incorrect signature since the extraneous params would alter the URL.

Example:

Let's say we're taking the following public example and making a signed URL:

  • https://image.mux.com/{public_playback_id}/thumbnail.jpg?time=25

Generate a signed url with {time: 25} in the claims body. Using the helper example we wrote above, this would look like:

  • sign(signedPlaybackId, { ...requiredTokenOptions, params: { time: 25 } })

Correct Signed URL:

  • https://image.mux.com/{signed_playback_id}/thumbnail.jpg?token={token}

Bad Signed URL:

  • https://image.mux.com/{signed_playback_id}/thumbnail.jpg?time=25&token={token}

Security: Signed URLs


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.