Signed Embed Codes

Note: Signed Embed Codes are only available with certain plans. Please contact support@sproutvideo.com with any questions.

Signed embed codes are an alternative to our Allowed Domains and Allowed IPs features for preventing unwanted sharing of your video embed codes. While those features are based upon verifying the referrer information sent by the web browser, signed embed codes are based upon verifying a signature appended to your embed code that is generated using a secret signing key. Signed embed codes allow you to set an expiration time for your embed code, so that a particular embed code is only valid for a limited time, ensuring that the embed code cannot be reused. Signed embed codes are meant to be used on sites that generate dynamic HTML content. Therefore, they require some programming knowledge in order to properly implement them.

Table of Contents

Enabling Signed Embed Codes

You can require a signed embed code for an individual video by going to that video, enabling the toggle for ‘Require Embed Codes to Be Signed,’ and clicking the ‘Save Changes’ button:

Require signed embed codes for videos hosted on SproutVideo

You can also enable signed embed codes for all videos in your account. Just navigate to Video Settings under Account Settings, and scroll down to the ‘Restrict Video Playback’ field. Enable the toggle for ‘Require Embed Codes to Be Signed’ and click the ‘Save Playback Settings’ button.

Account Wide Signed Embed Codes

Generating and Using Signed Embed Codes

We use a URL signing protocol very similar to the OAUTH1 signing protocol using a Base64-encoded hash generated using the HMAC-SHA1 algorithm.

Generate the Signing String

To start, we must generate the signing string that will be used to create the hash. The signing string is based on the src attribute of your embed code.

The signature base string is composed of the following elements:

  • Request method: The request method in this case will always be GET
  • Host name: The host name in this case will always be videos.sproutvideo.com
  • Request path: The request path is the part of the URL after the host up to the first ? If the URL we are signing is https://videos.sproutvideo.com/embed/e898d2b5111be3c860/546cd1548010aaeb?type=hd&autoplay=true, the request path is /embed/e898d2b5111be3c860/546cd1548010aaeb
  • Sorted query parameters: First, we need to add the expiration parameter to the list of parameters. Typically, you would pick a time a few minutes or so in the future (ideally, your system time should be synced using NTP or a similar protocol so your system thinks it is the same time that SproutVideo’s system does). The time you pick should be in UTC. The value should be represented as an integer number of seconds since the Epoch. For example, the time of May 02 2013, 10:20:43 PM (UTC) would be represented as 1367533243. These values need to be encoded into a single string which will be used later on. The process to build the string is very specific:
    1. Percent encode every key and value that will be signed.
    2. Sort the list of parameters alphabetically by encoded key.
    3. For each key/value pair:
      1. Append the ‘&’ character.
      2. Append the encoded key to the output string.
      3. Append the ‘=’ character to the output string.
      4. Append the encoded value to the output string.

Using the same URL above, the following string will be produced by repeating these steps:

&autoplay=true&expires=1367533243&type=hd

Now that we have all of our components, we need to combine them into a single string formatted on multiple lines as seen below:

Request method
Host name
Request path
Sorted query parameters

With our example URL above, the multi-line string would look like this:

GET
videos.sproutvideo.com
/embed/e898d2b5111be3c860/546cd1548010aaeb
&autoplay=true&expires=1367533243&type=hd

Now that we have our string, it’s time to sign it.

Calculating the Signature

First, you’ll need your API key to sign the string. You can find your API key here. Finally, the signature is calculated by passing the signature base string and signing key to the HMAC-SHA1 hashing algorithm.

The details of the algorithm are explained in depth here, but thankfully, there are implementations of HMAC-SHA1 available for every popular language. For instance, Ruby has the OpenSSL library and PHP has the hash_hmac function.

For example, given the example string (shown above), and a signing key of 9ab4b003d47003df394191234c54506d, the output is "\372\210@wo\356[\335\263\037\222h\230Fo\300\323,|\375". That value, when converted to base64, is the signature for this request: +ohAd2/uW92zH5JomEZvwNMsfP0=

Building the Final Request URL

  1. Take the original URL: https://videos.sproutvideo.com/embed/e898d2b5111be3c860/546cd1548010aaeb?type=hd&autoplay=true.
  2. Add the ‘expires’ parameter to the end: https://videos.sproutvideo.com/embed/e898d2b5111be3c860/546cd1548010aaeb?type=hd&autoplay=true&expires=1367533243
  3. URI encode the ‘signature’ parameter, and add that to the end of the URL: https://videos.sproutvideo.com/embed/e898d2b5111be3c860/546cd1548010aaeb?type=hd&autoplay=true&expires=1367533243&signature=%2BohAd2%2FuW92zH5JomEZvwNMsfP0%3D
  4. Add this as the src attribute of your iframe like this:
    <iframe src="https://videos.sproutvideo.com/embed/e898d2b5111be3c860/546cd1548010aaeb?type=hd&autoplay=true&expires=1367533243&signature=%2BohAd2%2FuW92zH5JomEZvwNMsfP0%3D"  />

    And your video will appear.

Code Samples

These code samples show how to generate the signed URL that you’d use as the src attribute of your iframe embed code in several popular languages.

  • let host = 'videos.sproutvideo.com';
    let videoID = 'e898d2b5111be3c860';
    let securityToken = '546cd1548010aaeb';
    let apiKey = '9ab4b003d47003df394191234c54506d';
    let expires = 1367533243;
    
    let params = { autoplay: true, type: 'hd', expires: expires };
    let sortedParams = Object.keys(params)
      .sort()
      .map((key) => {
        return `${key}=${encodeURIComponent(params[key])}`;
      })
      .join('&');
    let stringToSign = `GET\n${host}\n/embed/${videoID}/${securityToken}\n&${sortedParams}`;
    let signature = require('crypto')
      .createHmac('SHA1', apiKey)
      .update(stringToSign)
      .digest('base64');
    let queryString = `${sortedParams}&signature=${encodeURIComponent(signature)}`;
    console.log(`https://${host}/embed/${videoID}/${securityToken}?${queryString}`);
    
  • require 'openssl'
    require 'uri'
    
    host = 'videos.sproutvideo.com'
    video_id = 'e898d2b5111be3c860'
    security_token = '546cd1548010aaeb'
    api_key = '9ab4b003d47003df394191234c54506d'
    expires = 1367533243
    params = { autoplay: true, type: 'hd', expires: expires }
    
    string_to_sign = "GET\n#{host}\n/embed/#{video_id}/#{security_token}\n&#{URI.encode_www_form(params.sort)}"
    signature = [
      OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), api_key, string_to_sign)
    ].pack("m").strip
    query_string = URI.encode_www_form(params.merge(signature: signature))
    puts "https://#{host}/embed/#{video_id}/#{security_token}?#{query_string}"
    
  • import urllib.parse
    import base64
    import hmac
    from hashlib import sha1
    
    host = 'videos.sproutvideo.com'
    video_id = 'e898d2b5111be3c860'
    security_token = '546cd1548010aaeb'
    api_key = '9ab4b003d47003df394191234c54506d'
    expires = 1367533243
    params = dict(autoplay='true', type='hd', expires=expires)
    
    query_string = urllib.parse.urlencode(
      sorted(params.items(), key=lambda x: x[0])
    )
    string_to_sign = f'GET\n{host}\n/embed/{video_id}/{security_token}\n&{query_string}'
    signature = base64.b64encode(
      hmac.new(api_key.encode('ascii'), string_to_sign.encode('ascii'), sha1).digest()
    ).decode()
    params.update(signature=signature)
    query_string = urllib.parse.urlencode(params)
    print(f"https://{host}/embed/{video_id}/{security_token}?{query_string}")
    
  • $host = 'videos.sproutvideo.com';
    $video_id = 'e898d2b5111be3c860';
    $security_token = '546cd1548010aaeb';
    $api_key = '9ab4b003d47003df394191234c54506d';
    $expires = 1367533243;
    
    $params = array(
      'autoplay' => 'true',
      'type' => 'hd',
      'expires' => $expires
    );
    ksort($params);
    $string_to_sign = "GET\n{$host}\n/embed/{$video_id}/{$security_token}\n&" .
                      http_build_query($params);
    $params['signature'] = base64_encode(
      hash_hmac('sha1', $string_to_sign, $api_key, true)
    );
    $query_string = http_build_query($params);
    $embed_url = "http://{$host}/embed/{$video_id}/{$security_token}?{$query_string}";
    print_r($embed_url);
    
  • import java.net.URLEncoder;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;
    import java.util.*;
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    
    class Main {
      public static void main(String[] args) {
        String host = "videos.sproutvideo.com";
        String videoId = "e898d2b5111be3c860";
        String securityToken = "546cd1548010aaeb";
        String apiKey = "9ab4b003d47003df394191234c54506d";
        int expires = 1367533243;
    
        Map<String, String> params = new HashMap<>();
        params.put("autoplay", "true");
        params.put("type", "hd");
        params.put("expires", String.valueOf(expires));
    
        String[] sortedKeys = params.keySet().toArray(new String[params.size()]);
        Arrays.sort(sortedKeys);
    
        StringJoiner query = new StringJoiner("&");
        for (String key : sortedKeys) {
          String value = params.get(key);
          try {
            value = URLEncoder.encode(value, "UTF-8");
          } catch (Exception e) {
            e.printStackTrace();
          }
          query.add(key + "=" + value);
        }
        String path = "/embed/" + videoId + "/" + securityToken;
        String stringToSign = "GET\n" + host + "\n" + path + "\n&" + query.toString();
        String signature = null;
        try {
          signature = URLEncoder.encode(computeSignature(apiKey, stringToSign), "UTF-8");
        } catch (Exception e) {
          e.printStackTrace();
        }
    
        String embedUrl = "http://" + host + path + "?signature=" + signature + "&" + query.toString();
        System.out.println(embedUrl);
      }
    
      private static String computeSignature(String apiKey, String stringToSign)
          throws NoSuchAlgorithmException, InvalidKeyException {
        Mac mac = Mac.getInstance("HmacSHA1");
        SecretKeySpec secret = new SecretKeySpec(apiKey.getBytes(), "HmacSHA1");
        mac.init(secret);
        byte[] signatureBytes = mac.doFinal(stringToSign.getBytes());
        return Base64.getEncoder().encodeToString(signatureBytes);
      }
    }
    
  • using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Text;
    
    public class Program
    {
        public static void Main()
        {
            string host = "videos.sproutvideo.com";
            string video_id = "e898d2b5111be3c860";
            string security_token = "546cd1548010aaeb";
            string api_key = "9ab4b003d47003df394191234c54506d";
            int expires = 1367533243;
    
            var queryParams = new Dictionary<string, string>
            {
                { "autoplay", "true" },
                { "type", "hd" },
                { "expires", expires.ToString() }
            };
            var sortedParams = string.Join("&", queryParams
                .OrderBy(x => x.Key)
                .Select(x => $"{x.Key}={Uri.EscapeDataString(x.Value)}"));
            var stringToSign = $"GET\n{host}\n/embed/{video_id}/{security_token}\n&{sortedParams}";
            var signature = Uri.EscapeDataString(
                Convert.ToBase64String(
                    new HMACSHA1(Encoding.ASCII.GetBytes(api_key))
                    .ComputeHash(Encoding.ASCII.GetBytes(stringToSign))));
            var embedUrl = $"http://{host}/embed/{video_id}/{security_token}?{signature}&{sortedParams}";
            
            Console.WriteLine(embedUrl);
        }
    }
    
Was this article helpful?