# Last Login and logout (Object Based)

**URL:** <https://forums.knack.com/t/last-login-and-logout-object-based/8452>\
**Category:** Ask Developers\
**Created:** [May 4, 2020, 9:20pm UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452 "2020-05-04T21:20:41Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![Soluntech](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/soluntech/32/780_2.png) [@Soluntech](https://forums.knack.com/u/Soluntech)\
**Post date:** [May 4, 2020, 9:20pm UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/1 "2020-05-04T21:20:41Z")

</div>

**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:

&nbsp;

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)

&nbsp;

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

var LOGIN\_PAGE = false;

var LOGIN = false;

&nbsp;

// Set Knack Info for your app

var KNACK\_HEADERS = {

&nbsp;&nbsp;&nbsp;&nbsp;'X-Knack-Application-ID': 'XXXXXXXXXXXXXXXXXXXX',

&nbsp;&nbsp;&nbsp;&nbsp;'X-Knack-REST-API-Key': 'YYYYYYYYYYYYYYYYYYYYY'

};

&nbsp;

// Change object\_XX for Login Log object ID

var objectId = 'object\_XX';

&nbsp;

// Login actions

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

&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;LOGIN\_PAGE = ($('.kn-login').length == 1) ? true : false;

&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;if (LOGIN\_PAGE) {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LOGIN = false;

&nbsp;

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

&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.preventDefault();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LOGIN = true;

&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$('form').submit();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});

&nbsp;&nbsp;&nbsp;&nbsp;} else {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (LOGIN) {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LOGIN = false;

&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var user = Knack.session.user;

&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Create record log

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$.ajax({

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type: 'POST',

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;headers: KNACK\_HEADERS,

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url: Knack.api\_url + '/v1/objects/' + objectId + '/records',

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data: {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;field\_XXX: user.email,&nbsp; // Change field\_XXX for Email field ID

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;field\_YYY: 'Login'&nbsp; &nbsp; &nbsp; // Change field\_YYY for Action field ID&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;}

});

&nbsp;

// Logout actions

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

&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;// Check is already authenticated

&nbsp;&nbsp;&nbsp;&nbsp;if (!Knack.session.user) {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;

&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;

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

&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var user = Knack.session.user;

&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Create record log

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$.ajax({

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type: 'POST',

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;headers: KNACK\_HEADERS,

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url:&nbsp; Knack.api\_url + '/v1/objects/' + objectId + '/records',

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data: {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;field\_XXX: user.email,&nbsp; // Change field\_XXX for Email field ID

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;field\_YYY: 'Logout' &nbsp; &nbsp; // Change field\_YYY for Action field ID&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});

&nbsp;&nbsp;&nbsp;&nbsp;});

});

&nbsp;

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.

**&nbsp;**

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

&nbsp;

Did you like it?

**&nbsp;**

Please let us know!

---

<div class="post-metadata">

**Author:** ![Soluntech](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/soluntech/32/780_2.png) [@Soluntech](https://forums.knack.com/u/Soluntech)\
**Post date:** [May 19, 2020, 2:39pm UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/2 "2020-05-19T14:39:26Z")

</div>

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!

---

<div class="post-metadata">

**Author:** ![TonyLewis75327](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/tonylewis75327/32/261_2.png) [@TonyLewis75327](https://forums.knack.com/u/TonyLewis75327)\
**Post date:** [May 16, 2020, 2:15am UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/3 "2020-05-16T02:15:47Z")

</div>

No no 371444411831&nbsp;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.

---

<div class="post-metadata">

**Author:** ![Soluntech](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/soluntech/32/780_2.png) [@Soluntech](https://forums.knack.com/u/Soluntech)\
**Post date:** [May 15, 2020, 8:57pm UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/4 "2020-05-15T20:57:04Z")

</div>

Hey Guys, Don't Panic!&nbsp;

Knack has created applications this way as well. The chances of your application being hacked are extremely low, especially if the&nbsp;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-](https://support.knack.com/hc/en-us/community/posts/360063918172-Last-Login-and-Logout-View-Based-)

---

<div class="post-metadata">

**Author:** ![hmnd](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/hmnd/32/190_2.png) [@hmnd](https://forums.knack.com/u/hmnd)\
**Post date:** [May 14, 2020, 2:56am UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/5 "2020-05-14T02:56:39Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![BradStevens](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/bradstevens/32/140_2.png) [@BradStevens](https://forums.knack.com/u/BradStevens)\
**Post date:** [May 14, 2020, 2:43am UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/6 "2020-05-14T02:43:47Z")

</div>

Thanks 12127038288&nbsp;for the tip on globals (and 20760862587&nbsp;for asking!).&nbsp; 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](https://www.w3.org/wiki/JavaScript_best_practices#Avoid_globals)

---

<div class="post-metadata">

**Author:** ![hmnd](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/hmnd/32/190_2.png) [@hmnd](https://forums.knack.com/u/hmnd)\
**Post date:** [May 14, 2020, 2:36am UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/7 "2020-05-14T02:36:11Z")

</div>

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](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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators).

---

<div class="post-metadata">

**Author:** ![TonyLewis75327](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/tonylewis75327/32/261_2.png) [@TonyLewis75327](https://forums.knack.com/u/TonyLewis75327)\
**Post date:** [May 10, 2020, 2:34am UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/8 "2020-05-10T02:34:56Z")

</div>

12127038288&nbsp;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.

---

<div class="post-metadata">

**Author:** ![hmnd](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/hmnd/32/190_2.png) [@hmnd](https://forums.knack.com/u/hmnd)\
**Post date:** [May 10, 2020, 1:50am UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/9 "2020-05-10T01:50:19Z")

</div>

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](mailto:david@hmnd.io).

---

<div class="post-metadata">

**Author:** ![CiptoDariu91492](https://avatars.discourse-cdn.com/v4/letter/c/2bfe46/32.png) [@CiptoDariu91492](https://forums.knack.com/u/CiptoDariu91492)\
**Post date:** [May 10, 2020, 1:38am UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/10 "2020-05-10T01:38:56Z")

</div>

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

What does this mean?

---

<div class="post-metadata">

**Author:** ![hmnd](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/hmnd/32/190_2.png) [@hmnd](https://forums.knack.com/u/hmnd)\
**Post date:** [May 10, 2020, 1:18am UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/11 "2020-05-10T01:18:27Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![CiptoDariu91492](https://avatars.discourse-cdn.com/v4/letter/c/2bfe46/32.png) [@CiptoDariu91492](https://forums.knack.com/u/CiptoDariu91492)\
**Post date:** [May 10, 2020, 1:14am UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/12 "2020-05-10T01:14:58Z")

</div>

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!

---

<div class="post-metadata">

**Author:** ![hmnd](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/hmnd/32/190_2.png) [@hmnd](https://forums.knack.com/u/hmnd)\
**Post date:** [May 6, 2020, 2:52am UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/13 "2020-05-06T02:52:29Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![TonyLewis75327](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/tonylewis75327/32/261_2.png) [@TonyLewis75327](https://forums.knack.com/u/TonyLewis75327)\
**Post date:** [May 5, 2020, 1:06am UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/14 "2020-05-05T01:06:22Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![TonyLewis75327](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.knack.com/tonylewis75327/32/261_2.png) [@TonyLewis75327](https://forums.knack.com/u/TonyLewis75327)\
**Post date:** [May 4, 2020, 10:30pm UTC](https://forums.knack.com/t/last-login-and-logout-object-based/8452/15 "2020-05-04T22:30:14Z")

</div>

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)
