Last Login and logout (Object Based)

Want to know the last time a user logged in to your Knack app?

This feature is usually implemented to keep track of the users who use the application, especially the period of time in which a session lasts.

Knack applications allow you to implement authentication forms simply and quickly, however when establishing a control or audit on the logins or log outs that are constantly running within the application, you must resort to the help of JavaScript for that end. Below we list a series of steps that will serve as a guide for the implementation of these logs:

 

  1. Create a new object in the database: Although the object can have any number of fields, it is recommended that it have at least three fields: Email, Date / Time and Action (Multiple Choice = Login or Logout).

![](upload://sWIFZh9zU8LnzSwU3mC6w1OojPX.png)

 

  1. Add the following lines of code within the API & Code section:

var LOGIN_PAGE = false;

var LOGIN = false;

 

// Set Knack Info for your app

var KNACK_HEADERS = {

    'X-Knack-Application-ID': 'XXXXXXXXXXXXXXXXXXXX',

    'X-Knack-REST-API-Key': 'YYYYYYYYYYYYYYYYYYYYY'

};

 

// Change object_XX for Login Log object ID

var objectId = 'object_XX';

 

// Login actions

$(document).on('knack-scene-render.any', function (event, scene) {

 

    LOGIN_PAGE = ($('.kn-login').length == 1) ? true : false;

 

    if (LOGIN_PAGE) {

        LOGIN = false;

 

        $('input[type="submit"]').click(function (e) {

 

            e.preventDefault();

            LOGIN = true;

 

            $('form').submit();

        });

    } else {

        if (LOGIN) {

            LOGIN = false;

 

            var user = Knack.session.user;

 

            // Create record log

            $.ajax({

                type: 'POST',

                headers: KNACK_HEADERS,

                url: Knack.api_url + '/v1/objects/' + objectId + '/records',

                data: {

                    field_XXX: user.email,  // Change field_XXX for Email field ID

                    field_YYY: 'Login'      // Change field_YYY for Action field ID 

                }

            });

        }

    }

});

 

// Logout actions

$(document).on('knack-scene-render.any', function (event, scene) {

 

    // Check is already authenticated

    if (!Knack.session.user) {

        return;

    }

 

    $('.kn-log-out').on('click', function () {

 

        var user = Knack.session.user;

 

        // Create record log

        $.ajax({

            type: 'POST',

            headers: KNACK_HEADERS,

            url:  Knack.api_url + '/v1/objects/' + objectId + '/records',

            data: {

                field_XXX: user.email,  // Change field_XXX for Email field ID

                field_YYY: 'Logout'     // Change field_YYY for Action field ID 

            }

        });

    });

});

 

  1. With the help of the implemented JavaScript code, the new object created in step 1 will have records like the following:

![](upload://gW1zrmF06KJKugQZkspA8ZrvO63.png)

And on this object you can use different native Knack components such as tables, reports or graphs.

 

We’re sure this will come in handy on all your projects. 

 

Did you like it?

 

Please let us know!

Thanks for the support! We will be releasing thousands of lines of code in the upcoming months. Much of the code is quite old, dating back to 2014 when we joined Knack. It is not perfect but feel free to make improvements. This is for your enjoyment!


No no 371444411831 I truly appreciate your posts on the Knack board and i have found them really helpful! Just adding my 2 cents to the conversation in what I have learned myself. Many users knack app are public facing and could be subject to hacking via an exposed API and they may not realize this.

Hey Guys, Don't Panic! 

Knack has created applications this way as well. The chances of your application being hacked are extremely low, especially if the use of it is internal. Now we have published how the Last Login & Logout (View Based) is done. We want to continue giving solutions instead of criticism.

https://support.knack.com/hc/en-us/community/posts/360063918172-Last-Login-and-Logout-View-Based-

No problem, 8947499407. Though I did start out in software development, I'm still completely self taught! It's awesome that you're teaching yourselves and are so open to learning.

Thanks 12127038288 for the tip on globals (and 20760862587 for asking!).  It's one of those coding things us amateurs miss from formal training I guess.

BTW this is a very good share from David: https://www.w3.org/wiki/JavaScript_best_practices

369481659772 Happy to help.

Avoiding globals is best practice because all Javascript on the page runs in a global scope. This means that if there was some other Javascript on the page that used LOGIN_PAGE or you wanted to use a different object_id somewhere else in your code, your original variable would be overwritten and your code could break. For this reason, it is also recommended to use const where possible, as const cannot be mutated or reassigned. W3C has an explanation on this, too, and some examples on how to avoid this here: https://www.w3.org/wiki/JavaScript_best_practices#Avoid_globals.

Using functions instead of variables for LOGIN AND LOGIN_PAGE also eliminates any possible race conditions or uncertainty of the value of LOGIN and LOGIN_PAGE. Say you wanted to use the same LOGIN_PAGE variable to check if it is a login page, but in a view-render handler. Since LOGIN_PAGE is only checked on scene-render, you could get an incorrect result because you rely on timing for LOGIN_PAGE to be set. Converting into a function, ensures that each time you want to check if you are on the login page, you are actually performing the check instead of checking a stored value that you hope is set correctly.

In writing this, I also noticed another thing that should be avoided in Javascript. $('.kn-login').length == 1 uses two equal signs. When comparing values in Javascript, you should always use three equal signs (===). This is because == converts the things you are comparing to the same type prior to performing the comparison. === will compare the values as is (thus it is known as identity or strict equality). This means that '1' == 1 will return true, whereas '1' === 1 will return false. This may seem insignificant, however, using == can often lead to even more confusing results. null == undefined, 0 == false will both return true, too. Also note that this applies to !=, as well. Instead, use !==. For more on that, see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators.

12127038288 thanks for all your valuable feedback in the forum lately. I have a question you mentioned above to avoid globals and to use functions to return true/false. Can you explain the reason behind this. I'm a medium level Javascript guy so details like this will help me.

That's just a warning, not an error. Doesn't actually affect anything. Are you seeing the white page immediately after logging out or after you try to login after logging out? I can also take a look at your app, if you'd like. Feel free to email me at david@hmnd.io.

![](upload://5dMdm7w9R0KwnGqKpcHedTJtaD8.png)

What does this mean?

394514022392 If you right click the page, select Inspect Element and open the Console tab, do you see any errors in there when you log out? Do you still only see white when you refresh the page?

I have successfully created a log status for login and logout. but i have a little problem. Why when I log out the page display does not return to the login view? only white. Please Help!

In addition to what Tony has mentioned, it's also best practice to avoid global variables when possible. LOGIN_PAGE and LOGIN should be turned into functions that return true/false. As well, it is best practice to declare all variables with var, or preferably (if you don't need to target old browsers such as IE), let/const.

I would modify the login API to use user token rather than your account level API login. I use webhooks to integromat to protect my accounts API key myself.

Do you want the document submit listener stuck inside the any scene render listener? (edit: actually never mind i see now that you are resetting the listener on each page)