====== CoovaChilli JSON Interface ======
===== Introduction =====
* If CoovaChilli is compiled with JSON support it exposes a JSON interface on the controller.
* This document will discuss the technical workings of the JSON interface.
* In order to have access to the JSON interface you have to be on the same network served by CoovaChilli, this means you have to be connected to the hotspot in plain english.
===== Accessing the JSON interface =====
* The JSON interface is served from the UAMLISTEN IP Address which is typically 10.1.0.1
* It has a standard interface (HTTP) on UAMPORT which is typicallly 3990
* If Coova is configured to also support HTTPS in the UAM, the HTTPS access will be on the UAMUIPORT which is typically port 4990.
* Each of the endpoints can then be reached by appending **/json/** and the end point name .
* For HTTP a typical URL will look like this
* **status** http://10.1.0.1:3990/json/status
* **logon** http://10.1.0.1:3990/json/logon
* **logoff** http://10.1.0.1:3990/json/logoff
* For HTTPS a typical URL will look like this
* **status** https://10.1.0.1:4990/json/status
* **logon** https://10.1.0.1:4990/json/logon
* **logoff** https://10.1.0.1:4990/json/logoff
==== Specifying a Callback ====
* In order to allow cross site functionality the JSON interface also allows for you to include a value for the **callback**
* Simply include **callback** and the required value for it in the query string of the endpoint call.
* http://10.1.0.1:3990/json/status?callback=jQuery33105641008201093548_1612410177983
* The reply will then be wrapped inside the value of **callback**
jQuery33105641008201093548_1612410177983({"versio......28-D2-44-20-04-8D"}})
===== The JSON interface methods =====
* The JSON interface has three endpoints exposed. They are
* **status** Report on the current status of the client from which you are making the call
* **logon** Used to log a user onto the captive portal. Needs a valid RADIUS username and encrypted password.
* **logoff** Used to disconnect a user that has an active session on the captive portal.
We will now unpack each of these endpoints in their own dedicated session
==== status ====
* The reply of a status call will indicate the current connection status of a client / user from where the call was made.
* Here is a typical reply when the user is not connected
{
"version":"1.0",
"clientState":0,
"nasid":"ZA-VM1",
"challenge":"0caeb2c0240fca8f430ea54e6423151e",
"location":{"name":"My HotSpot"},
"redir":{
"originalURL":"http://detectportal.firefox.com/success.txt",
"redirectionURL":"",
"logoutURL":"http://10.1.0.1:3990/logoff",
"ipAddress":"10.1.0.2",
"macAddress":"28-D2-44-70-04-00"
}
}
* Here is a typical reply when the user is connected
{
"version":"1.0",
"clientState":1,
"nasid":"ZA-VM1",
"redir": {
"originalURL":"http://detectportal.firefox.com/success.txt",
"redirectionURL":"",
"logoutURL":"http://10.1.0.1:3990/logoff",
"ipAddress":"10.1.0.2",
"macAddress":"28-D2-44-70-04-00"
},
"session":{
"sessionId":"161241022400000001",
"userName":"click_to_connect@demo1",
"startTime":1612410289,
"sessionTimeout":0,"terminateTime":0,
"idleTimeout":0,
"maxTotalOctets":249823541
},
"accounting":{
"sessionTime":15,
"idleTime":0,
"inputOctets":144606,
"outputOctets":85859,
"inputGigawords":0,
"outputGigawords":0,
"viewPoint":"client"
}
}
* The item that indicates if there is an active session is the value of **ClientState**
* **0** No active session
* **1** Active session
==== logon ====
* The logon process consists of three calls
* Doing a status call to get the latest challenge from the status reply
* Doing a call the the UAM encryption service on the RADIUSdesk back-end by calling the uam.php script.
* Using the returned hash value together with the username to to a call to the JSON login end-point.
=== Get the latest challenge ===
* Each time we do a call to the **status** end point we get a new challenge in the reply.
* CoovaChilli remembers the last challenge it generated for a client (when they are not logged in) and uses that during the login process.
* This is why its good practice to do a **status** call as step one of the login process to get a fresh challenge from CoovaChilli. (There is an expiry on the challenge which is why we go this route to ensure we have a current value that has not expired yet)
* The call to the status end point was already covered earlier in the document. From it you can see there is a **challenge** item.
=== Encrypt the Challenge and Password ===
* RADIUSdesk includes a UAM service that takes the challenge, the user's password along with a common uam secret to generate a encrypred value that it uses on the logon end point to authenticate the user.
* Here is a sample call that you can use as reference
* http://hotspot.radiusdesk.com/login/services/uam.php?callback=jQuery331012987580313312852_1612418453858&challenge=e04003303e72cdd7fd30ef11af977985&password=click_to_connect&_=1612418453861
* And here is the result
jQuery331012987580313312852_1612418453858(
{
'response':'7215ee77b34e83ced8f816ec3f2bf2c3'
}
)
* You will replace **hotspot.radiusdesk.com** with the FQDN or IP Address of your own server
=== Call the logon JSON endpoint ===
* We can now use the response as the value of **password** when we do the logon JSON call
* http://10.1.0.1:3990/json/logon?callback=jQuery331012987580313312852_1612418453858&username=click_to_connect%40demo1&password=7215ee77b34e83ced8f816ec3f2bf2c3&_=1612418453862
* Alternative format
{
"GET": {
"scheme": "http",
"host": "10.1.0.1:3990",
"filename": "/json/logon",
"query": {
"callback": "jQuery331036884032348716045_1612421095535",
"username": "click_to_connect@demo1",
"password": "7215ee77b34e83ced8f816ec3f2bf2c3",
"_": "1612421095539"
},
"remote": {
"Address": "10.1.0.1:3990"
}
}
}
* And here's the response of a successful logon
{
"version": "1.0",
"clientState": 1,
"nasid": "ZA-VM1",
"redir": {
"originalURL": "http://detectportal.firefox.com/success.txt",
"redirectionURL": "",
"logoutURL": "http://10.1.0.1:3990/logoff",
"ipAddress": "10.1.0.2",
"macAddress": "28-D2-44-20-04-8D"
},
"session": {
"sessionId": "161241843700000001",
"userName": "click_to_connect@demo1",
"startTime": 1612418460,
"sessionTimeout": 180,
"terminateTime": 0,
"idleTimeout": 0
},
"accounting": {
"sessionTime": 0,
"idleTime": 0,
"inputOctets": 0,
"outputOctets": 0,
"inputGigawords": 0,
"outputGigawords": 0,
"viewPoint": "client"
}
}
==== logoff ====
* The last JSON end point is **logoff**
* Here is the call and the results
* http://10.1.0.1:3990/json/logoff?callback=jQuery331012987580313312852_1612418453858&_=1612418453865
* Alternative
{
"GET": {
"scheme": "http",
"host": "10.1.0.1:3990",
"filename": "/json/logon",
"query": {
"callback": "jQuery331036884032348716045_1612421095535",
"username": "click_to_connect@demo1",
"password": "d24ae9fbf467e62642d1f4963016aca6",
"_": "1612421095539"
},
"remote": {
"Address": "10.1.0.1:3990"
}
}
}
* Repsonse
{
"version": "1.0",
"clientState": 0,
"nasid": "ZA-VM1",
"challenge": "efad131ebc031da777a4b7957eb58299",
"session": {
"sessionId": "161242107800000001",
"userName": "click_to_connect@demo1",
"startTime": 1612421102,
"sessionTimeout": 180,
"terminateTime": 0,
"idleTimeout": 0
},
"accounting": {
"sessionTime": 0,
"idleTime": 0,
"inputOctets": 0,
"outputOctets": 0,
"inputGigawords": 0,
"outputGigawords": 0,
"viewPoint": "client"
}
}
* As you can see the reply is pretty much the same than logon but the **clientstate** is now **0** meaning it logged off fine.