Setting up external hotspot with Ubiquiti (ubnt) and debugging

Robert AndresenProgramming, Tutorials 2 Comments

My setup:

  • Webserver: Ubuntu 16.04 with default Apache2, PHP7 and MySQL.
    The webserver also has a local issued certificate with HTTPS.
  • UniFi Controller: 4.8.18 (Running on a Windows 10 client)
  • Laptop with wireless, running Windows 10, for testing.

This article will focus more on debugging than on the setup itself, as there are multiple of howto-guides everywhere on the Internet.

My guest control settings



My first step was to download the «Simple PHP External» from this thread

As the thread was pretty much up to date, i thought the portal and files (first posted in 2012) also has been updated along the way – but NO! Ubnt has changed the payload from POST to JSON. As the Simple-External-Portal linked to above had JSON payload for the API call, I thought that problem was taken care of – but after 5-6 hours of googling, I finally found out it wasn’t. The login also need to be sent as a JSON-payload.

The out-of-the-box issue, was that I just was redirected back to the portal webpage after trying to connect. Indicating a curl/auth issue. You can also log in to the controller and click on clients, to see if the client has been authorized or not.



My working code was copied from: This code has JSON payload for the login.

I still needed to do some minor changes to the code.

  1. The $unifi parameters was not sent to the function. So I removed $unifi as a parameter in the function, and added global $unifi in the function instead.
  2. For some reason none of my webservers looks like there available to create the cookie file, so I altered the path and created the cookie-file manually. Probably some permission issues, but didn’t troubleshoot that part any more.

My final sendAuthorization function looks like this:

function sendAuthorization($id, $minutes) {
	global $unifi;

	// Start Curl for login
	$ch = curl_init();
	// We are posting data
	curl_setopt($ch, CURLOPT_POST, TRUE);

	// Set up cookies
	//$cookie_file = "/tmp/unifi_cookie";
	$cookie_file = "/var/www/krokenweb.local/guest/unifi_cookie.txt";
	curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
	curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);

	// Allow Self Signed Certs
	curl_setopt($ch, CURLOPT_SSLVERSION, 1);
	// Login to the UniFi controller
	curl_setopt($ch, CURLOPT_URL, $unifi['unifiServer']."/api/login");
	$data = json_encode(array("username" => $unifi['unifiUser'],"password" => $unifi['unifiPass']));
	curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
	curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
	$login = curl_exec($ch);

	// Send user to authorize and the time allowed
	$data = json_encode(array(
	// Make the API Call
	curl_setopt($ch, CURLOPT_URL, $unifi['unifiServer'].'/api/s/default/cmd/stamgr');
	curl_setopt($ch, CURLOPT_POSTFIELDS, 'json='.$data);
	curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
	$api = curl_exec ($ch);

	// Logout of the connection
	curl_setopt($ch, CURLOPT_URL, $unifi['unifiServer']."/logout");
	curl_exec ($ch);
	$logout = curl_close ($ch);

	sleep(6); // Small sleep to allow controller time to authorize


Some troubleshooting and debugging along the way:

Most of the troubleshooting was from testing the Simple-External-Portal, but added them as they could be useful if something doesn’t work as expected.

SSL Version

The «curl_setopt($ch, CURLOPT_SSLVERSION, 3);» didn’t give me any output from Curl at all. The apache error-log was also blank.

So I changed to «curl_setopt($ch, CURLOPT_SSLVERSION, 1);», and outputted the requests in the page. I finally got some feedback from the code.

$login = curl_exec($ch);
echo "<pre>";
echo "</pre>";
$api = curl_exec ($ch);
echo "<pre>";
echo "</pre>";

This gave me some feedback like «api.err.LoginRequired». So the curl is working, but not the authorization.


I also removed ob_start() from Simple-External-Portal, as this can prevent output. Looks like this isn’t in the github-code. You can also just try to move it on the top of your code, under session_start().

Force some error messages

I also added:

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_VERBOSE, 1);

…in the curl.

This gave me some output in the apache error-log, like this:

< HTTP/1.1 302 Found
< Server: Apache-Coyote/1.1
< Cache-Control: private
< Expires: Thu, 01 Jan 1970 00:00:00 UTC
< Location:
< Content-Length: 0
< Date: Fri, 20 May 2016 02:09:27 GMT
* Connection #0 to host left intact
* Issue another request to this URL: ''
* Found bundle for host 0x5644312560e0 [can pipeline]
* Re-using existing connection! (#0) with host
* Connected to ( port 8443 (#0)
> GET /login?redirect=%2Fmanage HTTP/1.1
Accept: */*

< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Accept-Ranges: bytes
< ETag: W/"5796-1462440926000"
< Last-Modified: Thu, 05 May 2016 09:35:26 GMT
< Content-Type: text/html
< Content-Length: 5796
< Vary: Accept-Encoding
< Date: Fri, 20 May 2016 02:09:27 GMT

From this, it looked like it was trying to redirect me to /manage, which is the portal. We don’t actually want to go there, but this tells me that the authorization probably is working.


As the authorization is working, the «api.err.LoginRequired»-problem was that I didn’t have a auth when the code tried to make the API-call after the login, so it’s probably a cookie issue.


if (! file_exists($cookie_file))
	echo "Cookie, $cookie_file, file missing.";

if (! is_writable($cookie_file))
	echo "Cookie, $cookie_file, file is not writable.";

And I got an error that the cookie didn’t exist. I created it manually, but then it wasn’t writable. So I just changed the path and created the cookie with full permissions (0777) to be sure, «$cookie_file = «/var/www/krokenweb.local/guest/unifi_cookie.txt»;».

Testing connection

You should also test if the webserver is allowed to communicate with the controller on the port. You can do a simple telnet test, telnet <controller_ip> 8443.

root@krokenweb:~# telnet 8443
Connected to
Escape character is '^]'.


The result