Skip to content

Securing access to streams without backend

In this article we will show an example of how authorization can be implemented without writing your own backend.

The authorization system works as follows:

  • Your website generates a token by using a simple formula and hashes it with the secret key.
  • A client device opens a stream that has this token.
  • Flussonic generates a token string (using a stream name and (optionally) the client's IP address) and hashes it by using the same secret key.
  • If the hash sums match, then playback is allowed. Otherwise – no access provided to the stream.

The Flussonic supply has all the necessary logic for checking the generated tokens. Simply specify the securetoken option and password for authorization.

The auth directive can be configured for a certain stream or as a global setting:

stream example-stream {
  input fake://fake;
  on_play securetoken://SECRETKEY;
}

If you want to exclude IP address of client devices from checking, add no_check_ip=true option to the stream configuration:

stream example-stream {
  input fake://fake;
  on_play securetoken://SECRETKEY?no_check_ip=true;
}

Flussonic must know these values to generate a token:

  • (optional) The IP address of a client device
  • Stream name
  • Secret key
  • Current timestamp
  • (optional) user_id, unique user identifier to enable session limiter (see Limiting the Number of Sessions per User)

Code on a website should collect values to one string with the following order:

string = streamname + ip + starttime + endtime + secretkey + salt + user_id

The token is created as follows:

sha1(string) + salt + endtime + starttime

Where:

  • ip is an IP address of the client device.
  • streamname is a stream name.
  • starttime is a current time in UTC (Unix Timestamp).
  • endtime is a current time + a few hours. At the end of this time token will be invalid, so you will have to request another one.
  • secretkey is a key, stored in /etc/flussonic/flussonic.conf.
  • salt is a random string.
  • user_id is a string provided by your billing/middleware (optional).

If client devices are behind a proxy or their IPs can change frequently, you can exclude the client IP address when generating a token.

<?php
$flussonic = $_GET['host']; // This script gets Flussonic address from a query. String 'http://flussonic-ip'
$key = 'SECRETKEY'; // The key from flussonic.conf file. KEEP IT IN SECRET.
$lifetime = 3600 * 3; // The link will become invalid in 3 hours.

$stream = $_GET['stream']; // This script gets the stream name from a query. string (script.php?stream=bbc)

$ipaddr = $_SERVER['REMOTE_ADDR']; // (v20.07) Set $ipaddr = 'no_check_ip' if you want to exclude IP address of client devices from checking.
$desync = 300; // Allowed time desync between Flussonic and hosting servers in seconds.
$starttime = time() - $desync;
$endtime = $starttime + $lifetime;
$salt = bin2hex(openssl_random_pseudo_bytes(16));

$hashsrt = $stream.$ipaddr.$starttime.$endtime.$key.$salt;
$hash = sha1($hashsrt);

$token = $hash.'-'.$salt.'-'.$endtime.'-'.$starttime;
$link = $flussonic.'/'.$stream.'/embed.html?token='.$token.'&remote='.$ipaddr;
$embed = '<iframe allowfullscreen style="width:640px; height:480px;" src="'.$link.'"></iframe>';

echo $embed;
?>

config/routes.rb:

Rails.application.routes.draw do
 ...
  get '/securetoken/:id', to: 'securetoken#index'
end

app/controllers/securetoken_controller.rb:

class SecuretokenController < ApplicationController

  def index

    flussonic = 'http://flussonic-ip'
    secret = 'SECRETKEY'

    streamname = params[:id]
    lifetime = 3600 * 3
    starttime = Time.now.to_i - 300
    endtime = Time.now.to_i + lifetime
    salt = rand(8**8).to_s(8)

    hash = Digest::SHA1.hexdigest(streamname + request.remote_ip + starttime.to_s + endtime.to_s + secret + salt)
    token = hash + '-' + salt + '-' + endtime.to_s + '-' + starttime.to_s
    @url = flussonic + '/' + streamname + '/' + 'embed.html?token=' + token
  end
end

app/views/securetoken/index.html.erb:

<iframe allowfullscreen style="width:640px; height:480px;" src="<%= @url %>"></iframe>