Guides: How to use signed URLs to secure video playback
this video that you're watching right now is meant to be public anybody at any time should be able
to load it up in their browser and click the play button and it should just work but not every video
is meant for a public audience maybe there's a requirement that the viewer should be a paying
member or maybe they need to have a specific user permission to access the next video in
a course whatever the case is it's possible that you want to limit who can and cannot watch your
video content and in this video we're going to talk about how you can Implement secure playback
using muks and our signed playback policies let's Jump Right In we have a guide available that
makes it easy to go through all of the different permutations of what it takes to implement signed
playback policy but this is a complicated topic so in this video we're just going to walk through
an actual code sample of how you can Implement signed playback in a production application I'm
going to use code sandbox to write and store all of this code and we'll be using nextjs but you
don't need to have any sort of nextjs experience to understand this video you should be able to
apply those same principles to whatever code base you're working in I'm here in a very B basic page
that will have some sort of interaction with the server when the browser makes a request and then
it will render something to the browser that can be viewed so right now on this homepage this in
nextjs is our code that runs on the server and then here we're rendering a player uh and if I
go over to my player file we'll see that this is a client component that is rendering a MX Player
we're using a playback ID from a specific MX Video asset that's just open to the public so by passing
this string over to our MX player component we see that the video is showing up here on
the right hand side looks like it's working that's good now what we're going to do here is switch to
a playback ID that's signed that's protected and take take a look at what actually happens in that
case so first of all I'm going to go over to my M dashboard and we're going to have to create a new
asset one that is protected by a signed playback policy so I'm going to go to assets create new
asset and we can just leave all the defaults here for this case other than this playback
policy value right now it's set to public but we don't want this particular video to be public we
want only certain folks to be able to watch it so I'm going to change the playback policy value
from public to signed if I click run request we'll see that the asset is now preparing and
we should be able to go to the view asset page to see some more about what we've just done so take a
look here even the player in the M dashboard is not showing the video because there's no public
playback ID by default this video is not going to load for anybody unless you have a signed token so
now we have to do that work we have to understand what it takes to sign a token and what instances
it makes sense to be able to issue sign tokens for those users that were authorizing to view
the video so I'm scrolling down here just a bit and we can see that this particular playback ID
is signed so I'm going to copy this playback ID and I'm just going to bring it back over to our
nextjs application remove the existing playback ID and replace it with our signed playback ID so
if I give this a Reload look at this uh we have an invalid playback URL because it's basically
acting like this video does not exist we haven't set any sort of uh permissions to be able to tell
the player we do have authorization to view this video so let's go ahead and do that now the first
thing we'll have to do is create a signing key within the MX dashboard this key is used almost
as uh like a key to a door that tells you that you have the permissions or authority to be able to
access what is behind that door let's go ahead and create that signing key right now back over in the
MX dashboard I'm going going to go to the settings area and click on signing Keys now we'll want
to make sure that we have the same environment selected that we've uploaded that signed video
to right now I have a production environment selected but I actually uploaded that video to
an environment called delete me so I'm going to make sure that I'm generating that signing key in
the same environment that my protected video content has been uploaded it says I haven't
created any signning Keys just yet I'm going to go ahead and click generate new key and click
the green button to actually generate the key so we have two values here we have the signing key
ID and then this private key that's listed below we can use both of these in different
ways the way that I'm going to load it into the nextjs environment is by setting these values as
environment variables within nextjs so let's start by copying the signing key ID heading over to our
nextjs code sandbox and pasting that value and I'm also going to add a MX signing key secret and that
one is going to be set to that second value which is a really long string I'll go ahead and save and
we won't see anything change just yet we're still have a little bit of work left to do I'm heading
back to the homepage now and within that serers side code is exactly where we'll want to generate
our signed playback tokens this should not be done in the browser environment we don't want
to expose your signing key ID or secret to the browser or the client because then anybody can
go around signing playback tokens and pretending like they're the ones that have authorization to
determine who is able to watch those videos we don't want that so we're going to make sure that
this signing occurs in a serers side protected environment I'm over here in the secure server
code for our nextjs codebase and I'm going to use a helper for signing JWT Json web tokens
uh and this particular package in JavaScript is called Json web token so I'm going to add
this to my project depending ending on which language you're using you probably have your
own implementation for handling signing requests within that language codebase we just do a quick
search for a Json web token signing Library so now we have Json web token installed with our project
and I'm going to take a look at the docs here for how we actually use this particular Library
there's really one method in particular we're interested in for this video and it's this signing
method right here so we can see that we're going to call something JWT doign we're going to pass a
payload and then also pass the secret or private key as the second argument so here's an example
implementation of how you can actually sign a payload using this Library I'm going to select
it and copy it since this is a typescript project I'm also going to install the types for Json web
token okay we should be in a good spot to be able to import this library and start putting it to
use so I'll import the library and then in our server side code I'm going to call the
JWT sign method now this is documented within this particular library's documentation uh depending on
the library that you're using you might have a different name for this method you can see that
this signing method accepts a few different arguments the first is the payload that we're
going to sign and then there's another one right after that that is looking for the secret key so
let's take a look at what we should pass for that first payload argument back over on the
MX documentation we'll be able to look at what we should be including within this payload that
needs to be signed in order to give us access to be able to view that video there's a few different
claims here that are going to be required that we should pass values in for and in return we'll
generate a signed token for playback the first one I'm looking at here is called sub and that's
going to be the MX video playback ID that we are looking to sign so I'm going to go back to this
code base here and I want to pass that sub value as the same value that we have for the playback
ID okay next let's go back over to the m docks and we have odd for audience or essentially what
are we signing here in this case we are just signing the playback for this video so I'm
going to pass the value of V to this odd claim next up here is the EXP claim and this is when
this signing token should be expired now these tokens are only shortlived they will only last
for as long as you set them to be good for um this allows us to have playback URLs that work
for a little while but then no longer work once they have passed the expiration time that you
define so I'm going to set a value of one day from now um for this particular expiration time
and then finally here we're looking at the kid claim and this is going to be the key ID that
we're using that we got when the signing key was created now remember we put that value within our
environment variables here and it's going to be this top one this Muk signing key I could
probably actually put anore ID on this just so it's a little more clear which value we're
using all right all right now we have those four values in place and we're seeing an
error here that says that we're looking to have a value for the secret or private
key and that's the second argument for this signing method so I'm going to pass that right
here if I go back to this file I can see what the key name
is and now the error has gone away but let's make sure that we are getting the token as
expected all right so I'm seeing a value generated here which is good news but we're still not able
to access that playback well this value needs to be used in correlation with the MX Player library
or whatever video player that you're using to be able to tell uh the browser that it can access
the particular playback ID uh and here's proof here's the token that we just generated and just
to note if you're using a player other than M player you'll need to pass the entire playback
URL to the player and include the token by adding a token query parameter to the end of the URL so
I'm going to take this token value and hand it off to the player in react that might look something
like this and all this is doing is defining that the player should the player component should now
accept a token value and in MX Player the way that we pass tokens for use is through another
property called The Tokens property so I am going to specify Within in this tokens value that the
token for playback is going to be the token that I've generated I'll hit save and reload the page
all right and we're still not quite there this brings up another part of what is going to be
common really with signing tokens is sometimes there's a little bit of debugging that you have
to do in order to figure out what's going on here why isn't this this video playback working
as expected so I want to walk through a couple of different things that we should think about when
debugging an issue like this and how we can get to the bottom of what's going on so we already logged
out that this MX Player is getting the value that it's expecting but the value is just not
quite right I'm going to go back to the top level page and double check on what our signing method
is looking like I'm back over in the server code of our homepage and I'm taking a look at
this signing method here um so let's double check everything we have the sub as the signed playback
ID here that looks correct to me we have the odd value as V for video which is what we're looking
to sign right now we have the EXP set to a date um of now which creates the unix's time for this
moment in time plus 86400 which is the number of seconds in a day uh so this will expire one
day from when the token is generated and we have the kid which is the key ID value for the signing
key this is looking correct to me um so I would double check that these values are getting picked
up appropriately and uh looks like for me they are if I just log them out and so what I did was just
a quick look again at the mug dos and I noticed something here that we want to actually be signing
using a different algorithm than what the default algorithm is with this Json web token package you
can see that the default algorithm is signed with this hmac setup but uh to use a different
RSA approach we need to specify ify that as the third argument during the signing process so I'm
going to go ahead and copy this rs256 config value go back over to my signing method and add that
as the third argument we're not quite out of the woods just yet when we change the algorithm it's
now telling us that our secret key is expected to be in a different format from what we're seeing uh
as the string we've provided up here so I'm back over in this libraries documentation I
can see that when they are reading the private key they are actually loading that from a local
file instead of the environment variable and if we were to download this file from this PM file
from MX and just crack this thing open um what I'm kind of interested in is what does it look
like uh what will we see here that might look a little bit different from what we've copied
and pasted into our environment over here this ls0 value so I've downloaded the file and I'm going to
create uh I want to paste its contents right here just so we can look at it so it does look pretty
different to me uh when I've opened this file and pasted it here below so what is the difference
here what is going on well I'm back over here in the MX dashboard and notice this private key is
base 64 encoded so in order to use it within our sign signing package we need to decode it from
Bas 64 format there's all kinds of gotas like this when it comes to security this is why I
picked making videos as a career instead so let's clean up this environment uh file to look like it
used to look and I'm going back to our server code and I need to base 64 decode this signing
key secret that's going to look in something like this within a node JavaScript [Music]
environment so I am decoding uh using this approach right here I now have the secret
key value and I'm going to replace this second argument with the new decoded secret key value
I'll hit save and look we still get an error I probably should have tested some of this before I
went in cold okay so I've done a little bit of off camera debugging uh because you sometimes have to
Dive Right into these kinds of things so a couple things that I've uncovered here first of all this
particular decoding method needed a little bit of work to get the decoded base 64 value from
our signing key secret but you can see down here this is looking as expected now when I log out
the secret key and the other thing that I noticed is uh this expiration value isn't exactly in the
correct format so there's some uh different math that we need to do to get the expiration value set
up correctly and I have that uh preconfigured here so we're doing some uh Division and just to kind
of break this out this is now an hour this token will expire an hour from now so that's 60 seconds
time 60 minutes is that right 60 seconds time 60 will lead to 1 hour and then uh um as we pass this
new expiration we've got this base 64 decoding method in progress in place now we should be
able to save and click reload to get that signed playback loading we did it uh this stuff uh does
get a little complicated but I assure you it does work when you have it all configured appropriately
all right great so we have a signed token that's working we did it but here's the thing we don't
want to give this token out to just anybody that loads this page right we only want to generate the
token if that particular user has permission to watch that video so it's probably not the
right call to just put this token generation right within every single page load that sort of defeats
the purpose we might as well use a public playback ID uh in that case so let's instead move this to a
different area that is going to determine whether or not we should issue the playback token so for
demonstration purposes let's move this code to an API endpoint just so we can encapsulate
and visualize everything that's happening on the server and potentially call it from a client side
application in the future so I have a API endpoint that I've set up just as an example here going to
paste that right in um and let's make sure that we also bring over the JWT Library which by the
way I found out some people pronounce that jot uh I don't know this is like the new GIF vers
GIF argument I'm going to go with JWT um okay so we have that imported here we have our secret key
and everything's loaded up appropriately so all I'm going to do here is change this response to
include the token that we've generated so I can remove the console log and now anytime a
get request goes to this particular endpoint it's going to execute the code that we wrote sign the
token and then return it in the Json payload so I'll click save there head back over to the
homepage and we can see that in the server still I'm making that fetch request and this is the
endpoint to the API route that we've just created created we receive the response save it in this
Dynamic data variable and then we convert that uh response body to a Json object so I'm doing a
console log here of that response and uh we should assume just to get rid of this error here for a
moment uh we should assume that the uh response token is going to exist within the token key in
that Json payload but let's hit save and reload and see what we see here so perfect so here is
that payload that we're seeing as a result of that API call there's a token property and the value of
the token is right there so that's working and the video is loading over here as well that's great
now here's the thing over on that uh API route we're still just issuing this token regardless
of any circumstance right we probably don't want to do that so here um is some example code that
just basically is something that you might want to implement on your side and it depends on what
your business use case is perhaps you would look at the current logged in user and if they have a
specific permission then you would go ahead and issue that token um or check the [ __ ]
database maybe in this example and determine whether or not the user is on the plan the paid
plan that has access to this particular piece of content that sort of business logic is up to you
to determine what's the right fit for your use case just for demonstration purposes what we'll
do here is we will let's let's come up with a reason why this user might not have access to
this token so instead of returning the correct token here what I'm going to do is return a an
error so right here maybe let's just just get the um error response set up so that that's
returning correctly so instead of a successful uh token we're going to return an actual error we'll
call it not allowed I'm going to hit save reload yeah and look we are actually getting a different
message now is that right so so I'm returning an error response now I'm going to go back to
the page page and before we attempt to convert it to Json let's take a look at this Dynamic
data I'm scrolling through and I'm seeing that this particular response object has
that status code of 401 which we would send if the user is unauthorized to view
the content so let's come up with one way that we might handle this error we'll put a
uh variable for the token here and we're going to continue with this fetching just as is but
instead of immediately trying to convert to Json I'm going to wrap this in a tri catch
block and then I'm only going to render the player if we have a valid
token so let's hit reload here and we are seeing that we're getting
an error in response to attempting to make our
call I should probably move this guy right in here as
well so here we're seeing that the token is not being issued it's not allowed but if I go back
to this route and I change the requirements I'm going to remove this and again you would check
to see if your requirements are met if they're not met you can return an error like this if they are
met then you can go ahead and issue that token in which case that token will make it over to
our player and allow for a successful playback so we finally got it set up so that we have
signed playback working within our application but there's still a little bit more to know about um
what we should consider here is that every asset that's included on MX has different elements that
will allude to the contents of that particular asset let's think about that when you generate
uh thumbnails for a video you might have some previews of what uh the contents of that video
are within the thumbnail same for a text track or some captions and all of those also need to
be protected but there's some use cases where it could be helpful to provide the user with access
to a thumbnail of a video but then protect the actual video playback so this is where it can
get extra tricky right we set this up in a way so that each element will require its own signed
playback token let me say that again you won't use the same playback token to uh authorize access to
a video that you would use to authorize access to a thumbnail that's going to be a different token
entirely so throughout that process what you would end up changing when you were trying to generate a
token for let's say a thumbnail or a storyboard instead of just the video is this AUD claim the
audience claim so here again we passed a V for video if we also wanted to generate a token for
being able to show thumbnails for this particular video we would have to do that separately so let's
look again at the MX documentation we are going to look at the claims and we can see different values
here for thumbnail for the GIF and for story boards so let's go ahead and sign let's create
another signed token for access ing the thumbnail of this video most of the hard work is done here
I'm going to go back and create a another token so I'm copying this exact method that we've used
above and pasting it here and the only thing I want to change is this odd value from a v to a t
I'm also going to clarify that this token is now the playback token and this one is the thumbnail
token let's change the payload that we return in a successful response
playback token and thumbnail token great and now over here on our homepage I'm
going to update this as well to say playback token let thumbnail
token and then the [Music]
setter and I'm going to go over to the player as well and specify
that this component now accepts two [Music]
[Music] tokens and look at that now we're actually getting a different preview and that little broken
image icon has gone away that's because this this token that we're passing to the player tells the
player we do have the appropriate permissions to access the thumbnails that player will then make
the request uh to get that thumbnail with that token attached to it and as long as it succeeds
it will show the thumbnail right here on the screen as expected so we covered a lot in this
tutorial but we talked through a successful setup about signing tokens and why you might
want to use sign playback to begin with we went through the different approaches to do that within
a nextjs application and a node environment and we talked through different ways that you
might approach debugging some situations when things might go arai hopefully this video has
equipped you with the tools that you need to make a successful signed playback integration
in your app and if you have any questions at all please hit us up and we will be more than
happy to help make your integration a success thanks for watching we'll see you in the next
video