nginx RTMP Streaming With Slightly Improved Authentication

Thu, Jun 28, 2018

This is a followup to nginx RTMP Streaming With Simple Authentication.

Last time we covered a very basic setup with a hardcoded passkey. Multiple people have contacted me so far requesting an explanation on how to move towards a slightly more sophisticated authentication setup. Usually involving a php script to authenticate against. Maybe you want to use an existing mySQL or mariaDB database to set up users and channels? Fear not, this is not that complicated to start out with.

Server side configuration

Starting from the old example, we set up a basic rtmp section:

rtmp {
  server {
    listen 1935;
	ping 30s;
	notify_method get;
	  
	application stream {
	  live on;
	  on_publish http://yourdomain.com/rtmp_auth.php;
	  record off;
	}
  }
}

The on_publish command can point to any web address that you like. It could be supplied via the nginx server as well or you could use an apache2 instance for that. You can also use a completely different server if you wish. For now, we assume that there is a php script rtmp_auth.php which sits in the webroot of your webserver.

The above line will do the following:

  • as soon as someone tries to publish a stream to your domain, the nginx rtmp module will issue a HTTP POST request to the on_publish url.
  • nginx will supply the script with the get variable “name” and fill it with whatever comes directly after your initial stream url
  • it will also pass on any further GET style variables via the standard ?var1=value1&var2=value2 syntax.
  • it will wait for a HTTP return code which either tells it that everything is fine and streaming should commence (201) or that something went wront and it should drop the connection (404)

Suppose someone uses the following url to connect to your rtmp server:

rtmp://yourdomain.com/stream/john?psk=supersecret

nginx will then call your rtmp_auth.php script like this:

http://yourdomain.com/rtmp_auth.php?name=john&psk=supersecret

Inside of your php script you then have access to the $_POST array which holds your values and you can do whatever you want with them. In the following example we will use a php array $valid_users to hold a list of allowed users and passwords. Of course, you could instead connect to a database and query for the username and password. The interesting part is all in the if-statement which follows after that.

<?php
$username = $_POST["name"]; # in our current example, this will be 'john'
$password = $_POST["psk"]; # in our current example, this will be 'supersecret'

$valid_users = array("john" => "supersecret",
                     "winnie" => "thepooh",
					 "batman" => "nananananananana");

if ($valid_users[$username] == $password) {
  http_response_code(201); # return 201 "Created"
} else {
  http_response_code(404); # return 404 "Not Found"
}
?>

With this code, if the credentials check out, we return a 201 status code which tells nginx that whoever tries to connect is allowed to stream. If they do not, we issue a 404 and tell the client to get lost.

Client side configuration

Let us suppose your streaming client uses open broadcaster studio (OBS), which is a free and open source streaming utility which works with pretty much all major streaming sites and can also be configured to a custom site.

In OBS, you set the stream type to Custom Streaming Server and as the url you would use rtmp://yourdomain.com/stream/. As the stream key you would set john?psk=supersecret or any other username / password combination. If you want to supply more information you can supply more GET style variables via appending &var1=value1&var2=value2 and so on. Anything you write before the ? in the stream key field will end up in the variable $_POST["name"] inside your php script.

Sadly, SSL support for RTMP and also for the internal on_publishrequest is still somewhat lacking. So keep in mind that all of this stuff is plaintext authentication. You might not want to use a username / password combo directly but rather a streaming hash that you can allocate via a database and which has to be requested by the user beforehand. At least this way, if someone captures the data, they do not know the login credentials of your users.

You could for example generate a 64 character hash, put it into an SQL table and assign it to a user of your site. Then the user just enters this hash in OBS into the stream key field. Your php script will then be called and the variable $_POST["name"] will hold this hash. After that you can connect to your database, check out whether the hash exists or not and maybe even set up some notification on your website that user john is now streaming. Just be sure to finish with a 201 or 404 code in order to let nginx-rtmp know what it should do about the connection attempt.

In the end you can make things as complex as you want. You can use the same method for other nginx-rtmp directives such as: on_play, on_done, on_update and many more. Check them out at the nginx-rtmp wiki page.

Update: A kind reader has informed me that newer versions of the nginx rtmp plugin no longer use a GET but a POST request to call the URL you specify in the on_publish, on_play, etc directives. I have updated the code to reflect the changes.