Generar tokens

En esta guía se explica cómo generar un token y los campos obligatorios y opcionales de los tokens.

Para crear un token, debes componer una cadena que firmar, a la que llamaremos valor firmado en esta guía. El valor firmado incluye parámetros que describen el contenido que estás protegiendo, la hora de vencimiento del valor firmado, etc.

El valor firmado se usa al crear una cadena de token. Para crear una cadena de token, debes componer los parámetros del token, como un código de autenticación de mensajes basado en hash (HMAC) de clave simétrica del valor firmado.

Media CDN usa el token compuesto final para proteger tu contenido.

Crear un token

  1. Crea un valor firmado concatenando una cadena que contenga los campos de token obligatorios y los campos de token opcionales que quieras. Separe cada campo y los parámetros con el carácter de tilde ~.

  2. Firma el valor firmado con una firma Ed25519 o un HMAC de clave simétrica.

  3. Crea el token concatenando una cadena que contenga los campos obligatorios y los opcionales. Separe cada campo y los parámetros con el carácter de tilde ~.

    Al componer el token, los valores de cada uno de los parámetros son los mismos entre el valor firmado y la cadena de token, con las siguientes excepciones:

    • FullPath
    • Headers

En el siguiente código de ejemplo se muestra cómo crear un token mediante programación:

Python

Para autenticarte en Media CDN, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta el artículo Configurar la autenticación en un entorno de desarrollo local.

import base64
import datetime
import hashlib
import hmac

import cryptography.hazmat.primitives.asymmetric.ed25519 as ed25519


def base64_encoder(value: bytes) -> str:
    """
    Returns a base64-encoded string compatible with Media CDN.

    Media CDN uses URL-safe base64 encoding and strips off the padding at the
    end.
    """
    encoded_bytes = base64.urlsafe_b64encode(value)
    encoded_str = encoded_bytes.decode("utf-8")
    return encoded_str.rstrip("=")


def sign_token(
    base64_key: bytes,
    signature_algorithm: str,
    start_time: datetime.datetime = None,
    expiration_time: datetime.datetime = None,
    url_prefix: str = None,
    full_path: str = None,
    path_globs: str = None,
    session_id: str = None,
    data: str = None,
    headers: str = None,
    ip_ranges: str = None,
) -> str:
    """Gets the Signed URL Suffix string for the Media CDN' Short token URL requests.
    One of (`url_prefix`, `full_path`, `path_globs`) must be included in each input.
    Args:
        base64_key: Secret key as a base64 encoded string.
        signature_algorithm: Algorithm can be either `SHA1` or `SHA256` or `Ed25519`.
        start_time: Start time as a UTC datetime object.
        expiration_time: Expiration time as a UTC datetime object. If None, an expiration time 1 hour from now will be used.
        url_prefix: the URL prefix to sign, including protocol.
                    For example: http://example.com/path/ for URLs under /path or http://example.com/path?param=1
        full_path:  A full path to sign, starting with the first '/'.
                    For example: /path/to/content.mp4
        path_globs: a set of ','- or '!'-delimited path glob strings.
                    For example: /tv/*!/film/* to sign paths starting with /tv/ or /film/ in any URL.
        session_id: a unique identifier for the session
        data: data payload to include in the token
        headers: header name and value to include in the signed token in name=value format.  May be specified more than once.
                    For example: [{'name': 'foo', 'value': 'bar'}, {'name': 'baz', 'value': 'qux'}]
        ip_ranges: A list of comma separated ip ranges. Both IPv4 and IPv6 ranges are acceptable.
                    For example: "203.0.113.0/24,2001:db8:4a7f:a732/64"

    Returns:
        The Signed URL appended with the query parameters based on the
        specified URL prefix and configuration.
    """

    decoded_key = base64.urlsafe_b64decode(base64_key)
    algo = signature_algorithm.lower()

    # For most fields, the value we put in the token and the value we must sign
    # are the same.  The FullPath and Headers use a different string for the
    # value to be signed compared to the token.  To illustrate this difference,
    # we'll keep the token and the value to be signed separate.
    tokens = []
    to_sign = []

    # check for `full_path` or `path_globs` or `url_prefix`
    if full_path:
        tokens.append("FullPath")
        to_sign.append(f"FullPath={full_path}")
    elif path_globs:
        path_globs = path_globs.strip()
        field = f"PathGlobs={path_globs}"
        tokens.append(field)
        to_sign.append(field)
    elif url_prefix:
        field = "URLPrefix=" + base64_encoder(url_prefix.encode("utf-8"))
        tokens.append(field)
        to_sign.append(field)
    else:
        raise ValueError(
            "User Input Missing: One of `url_prefix`, `full_path` or `path_globs` must be specified"
        )

    # check & parse optional params
    if start_time:
        epoch_duration = start_time.astimezone(
            tz=datetime.timezone.utc
        ) - datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc)
        field = f"Starts={int(epoch_duration.total_seconds())}"
        tokens.append(field)
        to_sign.append(field)

    if not expiration_time:
        expiration_time = datetime.datetime.now() + datetime.timedelta(hours=1)
        epoch_duration = expiration_time.astimezone(
            tz=datetime.timezone.utc
        ) - datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc)
    else:
        epoch_duration = expiration_time.astimezone(
            tz=datetime.timezone.utc
        ) - datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc)
    field = f"Expires={int(epoch_duration.total_seconds())}"
    tokens.append(field)
    to_sign.append(field)

    if session_id:
        field = f"SessionID={session_id}"
        tokens.append(field)
        to_sign.append(field)

    if data:
        field = f"Data={data}"
        tokens.append(field)
        to_sign.append(field)

    if headers:
        header_names = []
        header_pairs = []
        for each in headers:
            header_names.append(each["name"])
            header_pairs.append("%s=%s" % (each["name"], each["value"]))
        tokens.append(f"Headers={','.join(header_names)}")
        to_sign.append(f"Headers={','.join(header_pairs)}")

    if ip_ranges:
        field = f"IPRanges={base64_encoder(ip_ranges.encode('ascii'))}"
        tokens.append(field)
        to_sign.append(field)

    # generating token
    to_sign = "~".join(to_sign)
    to_sign_bytes = to_sign.encode("utf-8")
    if algo == "ed25519":
        digest = ed25519.Ed25519PrivateKey.from_private_bytes(decoded_key).sign(
            to_sign_bytes
        )
        tokens.append("Signature=" + base64_encoder(digest))
    elif algo == "sha256":
        signature = hmac.new(
            decoded_key, to_sign_bytes, digestmod=hashlib.sha256
        ).hexdigest()
        tokens.append("hmac=" + signature)
    elif algo == "sha1":
        signature = hmac.new(
            decoded_key, to_sign_bytes, digestmod=hashlib.sha1
        ).hexdigest()
        tokens.append("hmac=" + signature)
    else:
        raise ValueError(
            "Input Missing Error: `signature_algorithm` can only be one of `sha1`, `sha256` or `ed25519`"
        )
    return "~".join(tokens)

Java

Para autenticarte en Media CDN, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta el artículo Configurar la autenticación en un entorno de desarrollo local.


import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.signers.Ed25519Signer;
import org.bouncycastle.util.encoders.Hex;

public class DualToken {

  public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
    // TODO(developer): Replace these variables before running the sample.
    // Secret key as a base64 encoded string.
    byte[] base64Key = new byte[]{};
    // Algorithm can be one of these: SHA1, SHA256, or Ed25519.
    String signatureAlgorithm = "ed25519";
    // (Optional) Start time as a UTC datetime object.
    DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
    Optional<Instant> startTime = Optional.empty();
    // Expiration time as a UTC datetime object.
    // If None, an expiration time that's an hour after the current time is used.
    Instant expiresTime = Instant.from(formatter.parse("2022-09-13T12:00:00Z"));

    // ONE OF (`urlPrefix`, `fullPath`, `pathGlobs`) must be included in each input.
    // The URL prefix and protocol to sign.
    // For example: http://example.com/path/ for URLs under /path or http://example.com/path?param=1
    Optional<String> urlPrefix = Optional.empty();
    // A full path to sign, starting with the first '/'.
    // For example: /path/to/content.mp4
    Optional<String> fullPath = Optional.of("http://10.20.30.40/");
    // A set of path glob strings delimited by ',' or '!'.
    // For example: /tv/*!/film/* to sign paths starting with /tv/ or /film/ in any URL.
    Optional<String> pathGlobs = Optional.empty();

    // (Optional) A unique identifier for the session.
    Optional<String> sessionId = Optional.empty();
    // (Optional) Data payload to include in the token.
    Optional<String> data = Optional.empty();
    // (Optional) Header name and value to include in the signed token in name=value format.
    // May be specified more than once.
    // For example: [{'name': 'foo', 'value': 'bar'}, {'name': 'baz', 'value': 'qux'}]
    Optional<List<Header>> headers = Optional.empty();
    // (Optional) A list of comma-separated IP ranges. Both IPv4 and IPv6 ranges are acceptable.
    // For example: "203.0.113.0/24,2001:db8:4a7f:a732/64"
    Optional<String> ipRanges = Optional.empty();

    DualToken.signToken(
        base64Key,
        signatureAlgorithm,
        startTime,
        expiresTime,
        urlPrefix,
        fullPath,
        pathGlobs,
        sessionId,
        data,
        headers,
        ipRanges);
  }

  // Gets the signed URL suffix string for the Media CDN short token URL requests.
  // Result:
  //     The signed URL appended with the query parameters based on the
  // specified URL prefix and configuration.
  public static void signToken(
      byte[] base64Key, String signatureAlgorithm, Optional<Instant> startTime,
      Instant expirationTime, Optional<String> urlPrefix, Optional<String> fullPath,
      Optional<String> pathGlobs, Optional<String> sessionId, Optional<String> data,
      Optional<List<Header>> headers, Optional<String> ipRanges)
      throws NoSuchAlgorithmException, InvalidKeyException {

    String field = "";
    byte[] decodedKey = Base64.getUrlDecoder().decode(base64Key);

    // For most fields, the value in the token and the value to sign
    // are the same. Compared to the token, the FullPath and Headers
    // use a different string for the value to sign. To illustrate this difference,
    // we'll keep the token and the value to be signed separate.
    List<String> tokens = new ArrayList<>();
    List<String> toSign = new ArrayList<>();

    // Check for `fullPath` or `pathGlobs` or `urlPrefix`.
    if (fullPath.isPresent()) {
      tokens.add("FullPath");
      toSign.add(String.format("FullPath=%s", fullPath.get()));
    } else if (pathGlobs.isPresent()) {
      field = String.format("PathGlobs=%s", pathGlobs.get().trim());
      tokens.add(field);
      toSign.add(field);
    } else if (urlPrefix.isPresent()) {
      field = String.format("URLPrefix=%s",
          base64Encoder(urlPrefix.get().getBytes(StandardCharsets.UTF_8)));
      tokens.add(field);
      toSign.add(field);
    } else {
      throw new IllegalArgumentException(
          "User Input Missing: One of `urlPrefix`, `fullPath` or `pathGlobs` must be specified");
    }

    // Check & parse optional params.
    long epochDuration;
    if (startTime.isPresent()) {
      epochDuration = ChronoUnit.SECONDS.between(Instant.EPOCH, startTime.get());
      field = String.format("Starts=%s", epochDuration);
      tokens.add(field);
      toSign.add(field);
    }

    if (expirationTime == null) {
      expirationTime = Instant.now().plus(1, ChronoUnit.HOURS);
    }
    epochDuration = ChronoUnit.SECONDS.between(Instant.EPOCH, expirationTime);
    field = String.format("Expires=%s", epochDuration);
    tokens.add(field);
    toSign.add(field);

    if (sessionId.isPresent()) {
      field = String.format("SessionID=%s", sessionId.get());
      tokens.add(field);
      toSign.add(field);
    }

    if (data.isPresent()) {
      field = String.format("Data=%s", data.get());
      tokens.add(field);
      toSign.add(field);
    }

    if (headers.isPresent()) {
      List<String> headerNames = new ArrayList<>();
      List<String> headerPairs = new ArrayList<>();

      for (Header entry : headers.get()) {
        headerNames.add(entry.getName());
        headerPairs.add(String.format("%s=%s", entry.getName(), entry.getValue()));
      }
      tokens.add(String.format("Headers=%s", String.join(",", headerNames)));
      toSign.add(String.format("Headers=%s", String.join(",", headerPairs)));
    }

    if (ipRanges.isPresent()) {
      field = String.format("IPRanges=%s",
          base64Encoder(ipRanges.get().getBytes(StandardCharsets.US_ASCII)));
      tokens.add(field);
      toSign.add(field);
    }

    // Generate token.
    String toSignJoined = String.join("~", toSign);
    byte[] toSignBytes = toSignJoined.getBytes(StandardCharsets.UTF_8);
    String algorithm = signatureAlgorithm.toLowerCase();

    if (algorithm.equalsIgnoreCase("ed25519")) {
      Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(decodedKey, 0);
      Ed25519Signer signer = new Ed25519Signer();
      signer.init(true, privateKey);
      signer.update(toSignBytes, 0, toSignBytes.length);
      byte[] signature = signer.generateSignature();
      tokens.add(String.format("Signature=%s", base64Encoder(signature)));
    } else if (algorithm.equalsIgnoreCase("sha256")) {
      String sha256 = "HmacSHA256";
      Mac mac = Mac.getInstance(sha256);
      SecretKeySpec secretKeySpec = new SecretKeySpec(decodedKey, sha256);
      mac.init(secretKeySpec);
      byte[] signature = mac.doFinal(toSignBytes);
      tokens.add(String.format("hmac=%s", Hex.toHexString(signature)));
    } else if (algorithm.equalsIgnoreCase("sha1")) {
      String sha1 = "HmacSHA1";
      Mac mac = Mac.getInstance(sha1);
      SecretKeySpec secretKeySpec = new SecretKeySpec(decodedKey, sha1);
      mac.init(secretKeySpec);
      byte[] signature = mac.doFinal(toSignBytes);
      tokens.add(String.format("hmac=%s", Hex.toHexString(signature)));
    } else {
      throw new Error(
          "Input Missing Error: `signatureAlgorithm` can only be one of `sha1`, `sha256` or "
              + "`ed25519`");
    }
    // The signed URL appended with the query parameters based on the
    // specified URL prefix and configuration.
    System.out.println(String.join("~", tokens));
  }

  // Returns a base64-encoded string compatible with Media CDN.
  // Media CDN uses URL-safe base64 encoding and strips off the padding at the
  // end.
  public static String base64Encoder(byte[] value) {
    byte[] encodedBytes = Base64.getUrlEncoder().withoutPadding().encode(value);
    return new String(encodedBytes, StandardCharsets.UTF_8);
  }

  public static class Header {

    private String name;
    private String value;

    public Header(String name, String value) {
      this.name = name;
      this.value = value;
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    public String getValue() {
      return value;
    }

    public void setValue(String value) {
      this.value = value;
    }

    @Override
    public String toString() {
      return "Header{"
          + "name='" + name + '\''
          + ", value='" + value + '\''
          + '}';
    }
  }

}

En las siguientes secciones se describen los campos que usan los tokens.

Campos de token obligatorios

Los siguientes campos son obligatorios para cada token:

  • Expires
  • Una de las siguientes:
    • PathGlobs
    • URLPrefix
    • FullPath
  • Una de las siguientes:
    • Signature
    • hmac

A menos que se indique lo contrario, los nombres de los parámetros y sus valores distinguen entre mayúsculas y minúsculas.

En la siguiente tabla se explica cada parámetro:

Nombre del campo o alias Parámetros de token Valor firmado

Expires

exp

Número entero de segundos transcurridos desde el inicio del registro de tiempo de Unix (1970-01-01T00:00:00Z). Expires=EXPIRATION_TIME, tras lo cual el token dejará de ser válido.

PathGlobs

paths, acl

Lista de hasta cinco segmentos de ruta a los que se va a conceder acceso. Los segmentos se pueden delimitar con comas (,) o signos de exclamación (!), pero no con ambos.

PathGlobs admite comodines en las rutas mediante asteriscos (*) y signos de interrogación (?). Un solo asterisco (*) abarca cualquier número de segmentos de ruta, a diferencia de la sintaxis de coincidencia de patrones de pathMatchTemplate.

No se permiten los parámetros de ruta, que se indican con puntos y comas (;), porque crean ambigüedad al buscar coincidencias.

Por estos motivos, asegúrese de que su URL no contenga los siguientes caracteres especiales: ,!*?;

PathGlobs=PATHS
URLPrefix

Una URL codificada en base64 segura para la Web que incluya el protocolo http:// o https:// hasta el punto que elijas.

Por ejemplo, algunos valores de URLPrefix válidos para `https://example.com/foo/bar.ts` son `https://example.com`, `https://example.com/foo` y `https://example.com/foo/bar`.

URLPrefix=BASE_64_URL_PREFIX
FullPath Ninguno Cuando especifique FullPath en un token, no duplique la ruta que haya especificado en el valor firmado. En un token, incluye el nombre del campo sin =. FullPath=FULL_PATH_TO_OBJECT
Signature Versión de la firma codificada en base64 segura para la Web. No aplicable
hmac Una versión codificada en base64 segura para la Web del valor HMAC. No aplicable

Sintaxis de comodín PathGlobs

En la siguiente tabla se explica la sintaxis del carácter comodín PathGlobs.

Operador Coincide con Ejemplos
* (asterisco) Coincide con cero o más caracteres de la ruta de la URL, incluidas las barras inclinadas (/).
  • /videos/* coincide con cualquier ruta que empiece por /videos/.
  • /videos/s*/4k/* coincide con /videos/s/4k/ y /videos/s01/4k/main.m3u8.
  • /manifests/*/4k/* coincide con /manifests/s01/4k/main.m3u8 y /manifests/s01/e01/4k/main.m3u8. No coincide con /manifests/4k/main.m3u8.
? (signo de interrogación) Coincide con un solo carácter de la ruta de la URL, sin incluir el carácter de barra diagonal (/). /videos/s?main.m3u8 coincide con /videos/s1main.m3u8. No coincide con ninguna de las dos: /videos/s01main.m3u8 o /videos/s/main.m3u8.

Los globs deben empezar por un asterisco (*) o una barra inclinada (/) en las rutas de URLs.

Como * y /* coinciden con todas las rutas de URLs, no recomendamos usar ninguna de ellas en tus tokens firmados. Para disfrutar de la máxima protección, asegúrese de que sus globs coincidan con el contenido al que quiere conceder acceso.

Campos de token opcionales

A menos que se indique lo contrario, los nombres de los parámetros y sus valores distinguen entre mayúsculas y minúsculas.

En la siguiente tabla se explican los nombres de los parámetros, los alias y los detalles de los parámetros opcionales:

Nombre del campo o alias Parámetros Valor firmado

Starts

st

Segundos enteros desde el inicio del registro de tiempo de Unix (1970-01-01T00:00:00Z). Starts=START_TIME
IPRanges

Lista de hasta cinco direcciones IPv4 e IPv6 en formato CIDR para las que esta URL es válida en formato base64 seguro para web. Por ejemplo, para especificar los intervalos de IP "192.6.13.13/32,193.5.64.135/32", debe especificar IPRanges=MTkyLjYuMTMuMTMvMzIsMTkzLjUuNjQuMTM1LzMy.

Puede que no sea útil incluir IPRanges en los tokens cuando los clientes corran el riesgo de sufrir migraciones de WAN o en los casos en los que la ruta de red al frontend de tu aplicación sea diferente de la ruta de entrega. Media CDN rechaza a los clientes con un código HTTP 403 cuando se conectan con una dirección IP que no forma parte de la solicitud firmada.

A continuación, se indican algunos casos que pueden provocar que Media CDN rechace a los clientes con un código HTTP 403:

  • Entornos de doble pila (IPv4 e IPv6)
  • Migración de la conexión (de Wi-Fi a datos móviles y de datos móviles a Wi-Fi)
  • Redes móviles que usan NAT de pasarela de operador (CGNAT o CGN)
  • TCP de varias rutas (MPTCP)

Todos estos factores pueden contribuir a que un cliente determinado tenga una dirección IP no determinista durante una sesión de reproducción de vídeo. Si la dirección IP del cliente cambia después de que hayas concedido acceso y el cliente intenta descargar un segmento de vídeo en su búfer de reproducción, recibirá un HTTP 403 de Media CDN.

IPRanges=BASE_64_IP_RANGES

SessionID

id

Una cadena arbitraria, útil para el análisis de registros o la reproducción de seguimientos.

Para evitar que se cree un token no válido, utilice cadenas codificadas en base64 seguras para la Web o codificadas con %. No se deben usar los siguientes caracteres en SessionID, ya que invalidan el token: "~", "&" o " " (espacio).

SessionID=SESSION_ID_VALUE

Data

data, payload

Cadena arbitraria, útil para analizar registros.

Para evitar que se cree un token no válido, utilice cadenas codificadas en base64 seguras para la Web o codificadas con %. No se deben usar los siguientes caracteres en Data, ya que invalidan el token: "~", "&" o " " (espacio).

data=DATA_VALUE
Headers Lista delimitada por comas de nombres de campos de encabezado. En los nombres de los encabezados no se distingue entre mayúsculas y minúsculas en las búsquedas de la solicitud. En los nombres de los encabezados de los valores firmados se distingue entre mayúsculas y minúsculas. Si falta un encabezado, el valor es la cadena vacía. Si hay varias copias de un encabezado, se concatenan con comas. Headers=HEADER_1_NAME=HEADER_1_EXPECTED_VALUE, HEADER_2_NAME=HEADER_2_EXPECTED_VALUE

Ejemplos

En las siguientes secciones se muestran ejemplos de cómo generar tokens.

Ejemplo con FullPath

Veamos un ejemplo con el campo FullPath:

  • Artículo solicitado: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Fecha de caducidad: 160000000

El valor firmado es:

Expires=160000000~FullPath=/tv/my-show/s01/e01/playlist.m3u8

Para crear un token, firma el valor firmado con una firma Ed25519 o un HMAC de clave simétrica.

Estos son algunos ejemplos de tokens creados a partir de un valor firmado:

Firma Ed25519

Expires=160000000~FullPath~Signature=SIGNATURE_OF_SIGNED_VALUE

Donde SIGNATURE_OF_SIGNED_VALUE es la firma ED25519 del valor firmado creado anteriormente.

HMAC de clave simétrica

Expires=160000000~FullPath~hmac=HMAC_OF_SIGNED_VALUE

Donde HMAC_OF_SIGNED_VALUE es el HMAC de clave simétrica del valor firmado creado anteriormente.

En los ejemplos anteriores, FullPath se proporciona en el token, pero el valor no se repite en la ruta especificada en el valor firmado. De esta forma, puedes firmar la ruta completa de la solicitud sin duplicarla en el token.

Ejemplo con URLPrefix

Veamos un ejemplo con el campo URLPrefix:

  • Artículo solicitado: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Fecha de caducidad: 160000000

El valor firmado es:

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4

En el ejemplo anterior, hemos sustituido la ruta del elemento solicitado, http://example.com/tv/my-show/s01/e01/playlist.m3u8, por la ruta del elemento en formato Base64 seguro para la Web, aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4.

Para crear un token, firma el valor firmado con una firma Ed25519 o un HMAC de clave simétrica.

Estos son algunos ejemplos de tokens creados a partir de un valor firmado:

Firma Ed25519

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4~Signature=SIGNATURE_OF_SIGNED_VALUE

Donde SIGNATURE_OF_SIGNED_VALUE es la firma ED25519 del valor firmado creado anteriormente.

HMAC de clave simétrica

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4~hmac=HMAC_OF_SIGNED_VALUE

Donde HMAC_OF_SIGNED_VALUE es el HMAC de clave simétrica del valor firmado creado anteriormente.

Ejemplo con Headers

Veamos un ejemplo con el campo Headers:

  • Artículo solicitado: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Fecha de caducidad: 160000000
  • Valor de PathGlobs: *
  • Encabezados de solicitud esperados:
    • user-agent: browser
    • accept: text/html

El valor firmado es:

Expires=160000000~PathGlobs=*~Headers=user-agent=browser,accept=text/html

Para crear un token, firma el valor firmado con una firma Ed25519 o un HMAC de clave simétrica.

Estos son algunos ejemplos de tokens creados a partir de un valor firmado:

Firma Ed25519

Expires=160000000~PathGlobs=*~Headers=user-agent,accept~Signature=SIGNATURE_OF_SIGNED_VALUE

Donde SIGNATURE_OF_SIGNED_VALUE es la firma ED25519 del valor firmado creado anteriormente.

HMAC de clave simétrica

Expires=160000000~PathGlobs=*~Headers=user-agent,accept~hmac=HMAC_OF_SIGNED_VALUE

Donde HMAC_OF_SIGNED_VALUE es el HMAC de clave simétrica del valor firmado creado anteriormente.

En los ejemplos anteriores, Headers=user-agent,accept se proporciona en el token, pero los valores de encabezado esperados no se repiten del valor firmado. De esta forma, puede firmar pares clave-valor de encabezado de solicitud específicos sin duplicar los valores en el token.