Have you used cloud functions yet? They come in many flavors: Amazon Lambda, Cloudflare Workers, Zeit Serverless Functions, and the one we’re using here: Netlify Functions.
Cloud functions are an essential component of the JAMstack. I had not heard the name JAMstack until recently. For the uninitiated (like me) it stands for Javascript, APIs and Markup. You may have seen JAMstack technologies like Gatsby, Next.js and tools of this nature that focus on performance, new developer tooling, and leveraging CDNs to serve pre-compiled HTML pages. I will be at JAMstack Conf 2019 in SF, if you will be there too, then come find me and say hi!
All the code in this post is open source here on GitHub in our examples repo: muxinc/examples/signed-playback-netlify.
Cloud functions, like Netlify Functions, might be a good option for you if you are using Mux’s Signed URLs feature.
When you create a video asset via Mux’s POST /video
API you can also create a Playback ID (Mux API docs) and specify the playback_policy
as either "public"
or "signed"
.
A “public” playback policy can be played back on any site, in any player and does not have an expiration. A “signed” playback policy requires that when the playback URL is requested from a player, it has to be accompanied by a “token” param that is generated and signed on your server.
This is how it looks:
public playback URL:https://stream.mux.com/${playbackId}.m3u8
signed playback URL:https://stream.mux.com/${playbackId}.m3u8?token=${token}
The token
param is what you need to create on your server in order for the playback URL to work.
MUX_TOKEN_ID
) and secret (MUX_TOKEN_SECRET
). You will need these to make two api calls.text
curl https://api.mux.com/video/v1/assets \
-X POST \
-H "Content-Type: application/json" \
-u ${MUX_TOKEN_ID}:${MUX_TOKEN_SECRET} \
-d '{ "input": "https://storage.googleapis.com/muxdemofiles/mux-video-intro.mp4", "playback_policy": "signed" }'
playback_id
from the response, you will need this later.text
curl https://api.mux.com/video/v1/signing-keys \
-X POST \
-H "Content-Type: application/json" \
-u ${MUX_TOKEN_ID}:${MUX_TOKEN_SECRET}
id
(MUX_TOKEN_ID
) and the private_key
(MUX_PRIVATE_KEY
) from the response, you will need these later. These are the keys you will need to create signed urls for playback.mkdir netlify-mux-signing && cd netlify-mux-signing
netlify init
to create a new project. You can choose to connect Netlify to a GitHub repository.yarn init
to create an empty package.json
and git init
to make this a git repository..netlify
which Netlify uses to handle deploys and Netlify commandsyarn add netlify-lambda
to install the netlify-lambda package into your project (it’s recommended to install this locally instead of globally).yarn add @mux/mux-node
to add the Mux node SDK to your projectCreate a src/
folder in your project and let’s create a small module called mux_signatures.js
. It will export one function called signPlaybackId which takes a playback id and returns a token that is generated with Mux.JWT.sign
:
Our lambda function is going to use this module in Step 2.
Create a Netlify Function entry point. This is the single function that will handle one request. The idomatic pattern for creating cloud functions is to do one file and one javascript function per route. We will create a directory called functions/
and add a file called sign_playback_id.js
.
Add a netlify.toml
file to the root directory and tell Netlify where your functions will live. This tells Netlify that before we deploy we are going to build our functions into the ./.netlify/functions
directory
text
[build]
functions = "./.netlify/functions"
In order to use Netlify Functions you will now need to commit your code and push it up to a git repository like GitHub. Do that next and in Netlify’s dashboard connect your git repository to the Netlify project that you created. After connecting you git repository then
In your Netlify project dashboard, naviate to "Settings" > "Deploys" > “Environment” to set your environment variables. Enter the MUX_SIGNING_KEY
and MUX_PRIVATE_KEY
from the Create a URL Signing Key step above.
netlify dev
this will start a local Netlify dev servernetlify-lambda serve ./functions
this will build your functions/, get them ready to handle requests and watch the filesystem for changes.<netlify-port>
and <playback-id>
with your values.text
curl -I 'http://localhost:<netlify-port>/.netlify/functions/sign_playback_id?playbackId=<playback-id>'
You should see a 302 (redirect) response with a location
header for the signed url.
When you make any changes to your source files, netlify-lambda serve
will pick up on the changes and recompile the functions into ./.netlify/functions
.
When you’re ready to deploy, you can deploy from the command line with netlify-lambda build ./functions && netlify deploy --prod
. This will build the functions and then push up the changes to Netlify.
Try making a POST request to your cloud function on Netlify:
text
curl -I 'https://<your-netlify-app>.netlify.com/.netlify/functions/sign_playback_id?playbackId=<playback-id>'
Just like in dev, you should get back a 302 response with a location
header that points to the signed playback URL.
text
https://stream.mux.com/${playbackId}.m3u8?token=${token}
This is what your response should look like:
text
HTTP/2 302
access-control-allow-origin: *
cache-control: no-cache
location: https://stream.mux.com/jqi1UtiO3gccQ019UcYjGJTLO9Ee00TLMY.m3u8?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkFsVFZncktBVTYzVldIdVplcDEwMVhZUk5mbHozeDIxRiJ9.eyJleHAiOjE1NzE3NjE0NzMsImF1ZCI6InYiLCJzdWIiOiJqcWkxVXRpTzNnY2NRMDE5VWNZakdKVExPOUVlMDBUTE1ZIn0.i7oANZ6inmwmGVQjon4WEv_gKcqQ2v8GuQA8xuCBdT0Reegkm6WyTdU-VloZvAt7duaRR3-T8dt147vUQjM1n70CLi0996pwMejYWIbRHUMqrDBtsENHG8T9jtz-EJcBGONSzgs7fBQIVQx8xJvPuX4YqpylDK_lNX0-RDqfhz5THAfuyxzePJod709msD8kbHAqnIke5lHzbQNHuO2ecNFVCb2ZozW7XkIEctyLxrDAK1ITtQV8iHek3whwO9S05kM-5bQzomJEliN3mXBqCwMBmyIp8l88YKl59tVXDdU-l-cZvZjt1GYKv0J7shO-oBYcr00NmVKkP7bie_w50w
date: Tue, 15 Oct 2019 16:24:33 GMT
age: 0
server: Netlify
x-nf-request-id: 80484951-e7ff-46f3-b78e-1349b8514bec-1426623
Now, in your player, you can use your netlify function as the URL src. Here's an example for a web player (note that in order to get HLS in a <video>
tag to work outside of Safari you will need to use another library like Video.js or HLS.js:
text
<video src="https://<your-netlify-project>.netlify.com/.netlify/functions/sign_playback_id?playbackId=<playback-id>"></video>
And here's an example on iOS with AVPlayer in Swift:
text
let url = URL(string: "https://<your-netlify-project>.netlify.com/.netlify/functions/sign_playback_id?playbackId=<playback-id>")
player = AVPlayer(url: url!)
player!.play()
The player will load the netlify URL, get the 302 redirect to the signed Mux URL and load the HLS manifest from stream.mux.com.
Now that your cloud function is working, you can add some security around it to make sure you only allow authorized users to generate signed urls.
For web players you will want to change this line in the sign_playback_id.js
function:
text
'Access-Control-Allow-Origin': '*',
You can use the Access-Control-Allow-Origin header to control the CORS rules for the resource.
I (along with others from Mux) will be at JAMstack Conf 2019 in SF. If you're using Netlify Cloud Functions (or other cool JAMStack stuff) get in touch or come talk to me at the conference!
No credit card to start. $20 in free credits when you're ready.
50 Beale Street, Floor 9
San Francisco, CA, 94105
34-37 Liverpool Street
Unit 4.06, 4th Floor
London, EC2M 7PP