Authenticated Playback for Live Streaming with Signed JWT Tokens
Introduction
Often times you need granular control over how live streaming content us secured across a large viewer base with different levels of access. To provide authenticated access for live streaming content using Qencode, you can use signed tokens for enhanced control. Signed tokens are JSON Web Tokens (often abbreviated to JWT). They are used to authenticate requests made to the Qencode Content Delivery service for robust access control when playing the live stream to your viewers. Utilizing signed tokens enables many options for managing authenticated playback and helps ensure content delivery is both secure and efficient.
Authenicating Signed Token Requests
1Generate Playback URL
To authenticate requests, a signed token must be appended to the stream's Playback URL using the 'auth' query string parameter. For instance, to authenticate a Live stream playback URL request, format the URL as follows:
https://play-$PLAYBACK_ID.qencode.com/qhls/qlive/playlist.m3u8?auth=$TOKENIn this URL, $PLAYBACK_ID represents your live stream's playback ID.
Authentication for Custom Domains
For live streams utilizing a custom domain for playback, structure the URL like this:
https://$CUSTOM_DOMAIN/$PLAYBACK_ID.m3u8?auth=$TOKENHere, $CUSTOM_DOMAIN denotes your designated custom playback domain name.
2Create a Signing Key
The signing key is used in the signing process to generate the signed URL. The key signs claims (such as the file path and expiration time) to create a unique, secure, and time-limited access link. This process ensures that users with permission can access the content while the link is active.
You can create a signing key using the following API method:
To create a signing key for a stream, you should specify the stream id in the request payload:
You can create a signing key and use live stream playback authentication.
curl -X POST 'https://api-cdn.qencode.com/v1/signing-keys'
-H 'Authorization: Bearer $ACCESS_TOKEN'
-H 'Content-Type: application/json'
--data-raw '{"stream_id": "1f1ab6a3-d867-4787-b882-7f2fb1203634"}'{
"signing_key": {
"stream_id": "1f1ab6a3-d867-4787-b882-7f2fb1203634",
"private_key": "-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEA4DAKSi4F8yQTt1L4N9s9Wt+3hBoHt5KMs14jy37YNnqxaIzJ
...
1c4v1P/wPr2sqepof19dbacmGcxplhHJ4d3BXAdInxYuGrgerShJXy5icFWf8A==
-----END RSA PRIVATE KEY-----
",
"created": "2023-06-12 15:00:40",
"id": "ec25a6a6-b31e-4ac9-af01-711460421c6b",
"name": null
}
}You can get a list your existing signing keys using the GET /v1/signing-keys API method. You also can rename an existing signing key using the PUT /v1/signing-keys/<signing_key_id> API method.
- Navigate to the Signing Keys page in the Content Delivery section.
- Click on Create signing key and select Live Stream from the drop down.
- Select your stream, and click Create to confirm your selection.
3Generate Signed Token
Signed Token Structure
A signed token comprises a series of headers and payload values. Below is a list of supported headers and payload values. Additionally, you have the option to include custom payload values within a JWT for further customization.
Supported Parameters for Signed JWT Tokens
| typ | Signed token type. Should be set to 'JWT' |
| alg | Token signing method. Should be set to 'RS256' |
| kid | Signing key ID, e.g. 12345678-90ab-cdef-1234-567890abcdef |
| uri | The URI that this signed token is expected to be used with. For Live Streaming, URI should be set to the stream playback ID, e.g. 12345678-90ab-cdef-1234-567890abcdef. |
| exp | The token expiration timestamp. If not specified, token does not expire. The value should be in Unix timestamp format, e.g. 1710199121. |
| nbf | The token 'not before' timestamp. A timestamp that indicates the time before which the token must not be accepted. The value should be in Unix timestamp format, e.g. 1710199121. |
Code examples
Below you can find some code examples in various programming languages which can be used to generate a signed tokens:
Generating a signed token
import jwt
import time
def generate_signed_token(signing_key_id, private_key, uri, exp=3600, nbf=None):
payload = {
'uri': uri
}
if exp > 0:
payload['exp'] = int(time.time()) + exp
if nbf is not None:
payload['nbf'] = nbf
headers = {
'alg': 'RS256',
'typ': 'JWT',
'kid': signing_key_id
}
token = jwt.encode(payload, private_key, algorithm="RS256", headers=headers)
return token
# you can get signing key data from the response of https://api-cdn.qencode.com/v1/signing_keys endpoint
signing_key = {
'id' = 'your_signing_key_id',
'private_key' = 'your_signing_key_private_key'
}
# generate token for Live stream playback
stream_playback_id = 'your_stream_playback_id'
token = generate_signed_token(signing_key['id'], signing_key['private_key'], playback_id)<?php
/*
Please note that in order to use JWT in PHP, you need to install the "firebase/php-jwt" library via Composer.
You can do that with the following command:
composer require firebase/php-jwt
*/
require_once('vendor/autoload.php');
use FirebaseJWTJWT;
function generateSignedToken($signingKeyId, $privateKey, $uri, $exp = 3600, $nbf = null)
{
$payload = [
'uri' => $uri
];
if ($exp > 0) {
$payload['exp'] = time() + $exp;
}
if ($nbf !== null) {
$payload['nbf'] = $nbf;
}
$headers = [
'alg' => 'RS256',
'typ' => 'JWT',
'kid' => $signingKeyId
];
$token = JWT::encode($payload, $privateKey, 'RS256', null, $headers);
return $token;
}
// you can get signing key data from the response of https://api-cdn.qencode.com/v1/signing_keys endpoint
$signingKey = [
'id' => 'your_signing_key_id',
'private_key' => 'your_signing_key_private_key'
];
// generate token for Live stream playback
$streamPlaybackId = 'your_stream_playback_id';
$token = generateSignedToken($signingKey['id'], $signingKey['private_key'], $streamPlaybackId);
echo $token;
?>const jwt = require('jsonwebtoken');
function generateSignedToken(signingKeyId, privateKey, uri, exp = 3600, nbf = null) {
let payload = {
uri: uri,
};
if (exp > 0) {
payload.exp = Math.floor(Date.now() / 1000) + exp;
}
if (nbf !== null) {
payload.nbf = nbf;
}
let headers = {
alg: 'RS256',
typ: 'JWT',
kid: signingKeyId,
};
let token = jwt.sign(payload, privateKey, { algorithm: 'RS256', header: headers });
return token;
}
// you can get signing key data from the response of https://api-cdn.qencode.com/v1/signing_keys endpoint
let signingKey = {
id: 'your_signing_key_id',
privateKey: 'your_signing_key_private_key',
};
// generate token for Live stream playback
let streamPlaybackId = 'your_stream_playback_id';
let token = generateSignedToken(signingKey.id, signingKey.privateKey, streamPlaybackId);
console.log(token);
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
class Program
{
static void Main()
{
string signingKeyId = "your_signing_key_id";
string privateKey = "your_signing_key_private_key"; // You might need to convert this to a SecurityKey
string uri = "your_stream_playback_id";
int exp = 3600;
int? nbf = null;
var token = GenerateSignedToken(signingKeyId, privateKey, uri, exp, nbf);
Console.WriteLine(token);
}
static string GenerateSignedToken(string signingKeyId, string privateKey, string uri, int exp, int? nbf)
{
var claims = new List<Claim>
{
new Claim("uri", uri)
};
if (exp > 0)
{
var expTime = DateTime.UtcNow.AddSeconds(exp);
claims.Add(new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(expTime).ToUnixTimeSeconds().ToString()));
}
if (nbf != null)
{
claims.Add(new Claim(JwtRegisteredClaimNames.Nbf, nbf.ToString()));
}
var securityKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(privateKey)); // might need to be adjusted based on your key
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
var header = new JwtHeader(credentials);
header.Add("kid", signingKeyId);
var payload = new JwtPayload(claims);
var secToken = new JwtSecurityToken(header, payload);
var handler = new JwtSecurityTokenHandler();
return handler.WriteToken(secToken);
}
}
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.util.Date;
public class Main {
public static void main(String[] args) {
String signingKeyId = "your_signing_key_id";
String privateKeyStr = "your_signing_key_private_key"; // You might need to convert this to a Key
String uri = "your_stream_playback_id";
long exp = 3600;
Long nbf = null;
String token = generateSignedToken(signingKeyId, privateKeyStr, uri, exp, nbf);
System.out.println(token);
}
private static String generateSignedToken(String signingKeyId, String privateKeyStr, String uri, long exp, Long nbf) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
byte[] privateKeyBytes = privateKeyStr.getBytes(StandardCharsets.UTF_8);
Key signingKey = Keys.hmacShaKeyFor(privateKeyBytes);
JwtBuilder builder = Jwts.builder()
.setId(signingKeyId)
.claim("uri", uri)
.signWith(signingKey, SignatureAlgorithm.HS256);
if (exp > 0) {
long expMillis = nowMillis + exp * 1000;
Date expDate = new Date(expMillis);
builder.setExpiration(expDate);
}
if (nbf != null) {
long nbfMillis = nbf * 1000;
Date nbfDate = new Date(nbfMillis);
builder.setNotBefore(nbfDate);
}
return builder.compact();
}
}
require 'jwt'
require 'time'
def generate_signed_token(signing_key_id, private_key, uri, exp=3600, nbf=nil)
payload = {
'uri' => uri
}
if exp > 0
payload['exp'] = Time.now.to_i + exp
end
if !nbf.nil?
payload['nbf'] = nbf
end
headers = {
'alg' => 'RS256',
'typ' => 'JWT',
'kid' => signing_key_id
}
token = JWT.encode payload, private_key, 'RS256', headers
return token
end
# you can get signing key data from the response of https://api-cdn.qencode.com/v1/signing_keys endpoint
signing_key = {
'id' => 'your_signing_key_id',
'private_key' => 'your_signing_key_private_key'
}
# generate token for Live stream playback
stream_playback_id = 'your_stream_playback_id'
token = generate_signed_token(signing_key['id'], signing_key['private_key'], stream_playback_id)
puts token
4Deleting a signing key
You may choose to revoke access by deleting the Signing Key. In order to do this, please follow these steps:
To delete a Signed Key, pass the signing key ID via the delete signing key API method. The method and some examples can be found below:
Deletes a signing key.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X DELETE https://api-cdn.qencode.com/v1/signing-keys/$SIGNING_KEY_ID{
"status": "ok"
}-
Navigate to the Signing Keys page in the Content Delivery section.
-
Select the key you wish to delete.
-
Click Delete icon.
Expected Outcome: The key is invalidated, and URLs generated with it will no longer work.
Need More Help?
For additional information, tutorials, or support, visit the Qencode Documentation page or contact Qencode Support at support@qencode.com.