Reverse engineering an easy way to check GNIB appointments
published: (updated: )
by Harshvardhan J. Pandit
is part of: GNIB Appointments
GNIB reverse-engineering visa
source: hosted on Github here
webapp: hosted on Heroku here shows available timings for GNIB and Visa appointments
As per the the Irish Law I must register myself with the local Police (or Garda). They issue a GNIB card, which is proof that I am registered with them and am permitted to remain in Ireland. For this, I need to take an appointment online through their website. Seems like a little extra effort but then that much was enough to make me stop working on what I was supposed to be doing and instead look into how I can save time on these appointment bookings.
The caveat with this approach is that now everyone books online, and somehow there almost never are any free appointments available. To just check whether there are any appointments available (in a future date), I need to full a form with 15 fields, which itself takes 5 minutes, only to see a "no appointments available" message at the end. Frustrating.
Through this post, I aim to document my efforts into reverse engineering GNIB appointment website and protocols to easily get available appointments. It resulted in a bit of javascript code that when pasted into the browser console, prints the available appointments. This is intended to be a first of many posts detailing my efforts to make this a side-project and a tool that can be helpful for others.
Digging through javascript - Form onClick
The first rule of automation is to familiar oneself with what is being automated.
So I set out to inspect what happens when I fill the form and hit the get
appointments button. This involved digging through the javascript the site
runs and following variables and AJAX requests around. Thankfully, Chrome makes
this really easy using right click -> inspect
.
The button (id btLook4App
) has an onclick
value of allowLook4App()
defined in
Appform.js.
The function calls another function called get4DateAvailability()
which
is where the appointments are retrieved using an AJAX request.
The url for this request is constructed dynamically using the statement
"/" + stPath + "/(getApps4DTAvailability)?openpage"
where
stPath
is "Website/AMSREG/AMSRegWeb.nsf"
. One thing to note here is
the addition of openpage
at the end of the URL - it is meant to be an
empty GET parameter. Looking at the other parameters of the AJAX request,
one can figure out that it only sends information about
- Category
Yup, that's all the information it sends from those 15 fields that were filled. The whole requirement of needing the entire form to be filled and validated before sending requests is a sham (and a shame).
GET request
The GET parameters can be summarised as:
{
"cat": "value of element #Category",
"sbcat": "All",
"typ: "Renewal",
"openpage": ""
}
The kicker here is that the URL handling this GET request has (sanely) been
configured to implement CORS
which means that I cannot just query it from any other website.
So I opened the browser console and tried to see if my AJAX request worked
by basically copying a minimal version of the original. It needs a variable
called dataThis
which is based on Category and a static string.
var dataThis = "&cat=Study&sbcat=All&typ=Renewal";
$.ajax( {
type : "GET",
url : "/" + stPath + "/(getApps4DTAvailability)?openpage",
data : dataThis,
success : function(data) {
console.log(data);
}
});
which yields the JSON response of Object {slots: "[]"}
which is NOTHING.
AJAX request and response
So I knew I was on a wrong track. Further diggging through requests gave the new
URL of "/" + stPath + "/(getAppsNear)?openpage"
in function getEarliestApps()
which is part of a hidden button that gets activated/shown only after the
form is complete and validated. Querying this new URL using the following function yielded in the given response.
var dataThis = "&cat=Study&sbcat=All&typ=Renewal";
$.ajax( {
type : "GET",
url : "/" + stPath + "/(getAppsNear)?openpage",
data : dataThis,
success : function(data) {
console.log(data);
}
});
{
"slots": [
{
"id": "0A03221D91CF90848025811400317D02",
"time": "4 July 2017 - 10:00"
},
{
"id": "3B7F286B73A976F08025812500318633",
"time": "21 July 2017 - 08:00"
}
]
}
On a successful query, the response sends over available times in a list
under the key slots
comprised of id
and time
, both of which are
strings. For my purpose, it was enough to extract the time
values as
available appointments. Studying getEarliestApps()
shows the conditions
and ways to detect other possible conditions.
- If there is a field called
data.error
then the request has an error - If there is a field called
data.empty
then there are no appointments currently available - Just to be sure, I also check length of
data.slots
as it can be an empty list
A quick way to get appointments
This gave me a way to quickly check available appointments by opening the website and executing the AJAX request with the success
function printing out the
available appointment times. Any error on the console means there was an
error with the request or there are no appointments available currently.
var dataThis = "&cat=Study&sbcat=All&typ=Renewal";
$.ajax( {
type : "GET",
url : "/" + stPath + "/(getAppsNear)?openpage",
data : dataThis,
success : function(data) {
for(appointment of data.slots) {
console.log(appointment.time);
}
}
});
Future plans
- Turn this into a script that I can run anywhere (terminal/server) so that it
can be automated (
cron
) - Create a chrome extension that does the AJAX request automatically periodically and then notifies using an icon the available appointments