A question that gets asked in the OutSystems forums on a regular basis is how to make a login page for an Agile Platform application. While the out-of-the-box application templates cover this functionality, there are times when you want your own handmade login system. This is a situation where "the devil is in the details," and since I've put together a well-functioning login system, I thought I'd share it with TechRepublic readers. One special property of my login system is that it allows the user to log in with an email address or a username, depending upon what they have. I do this because the email address is easier to remember, but some users get assigned usernames with no email address.
To get started, we want to make this be a Web Block, not a Screen, so it is easy to drop wherever we want it. All we need in the Preparation Action is a query to the User table matching the User.Id to Session.UserId (via condition or a parameter). We need the results of this query for our display of the logged in user's name later on.
In my login system, I start with an If Widget with the condition Session.UserId = NullIdentifier(). In the "True" section, I place the actual login components; in the "False" section, I display the user's name and a link to a "Logout" Action in the block. The "Logout" Action just calls the "Logout" Action built in to the system and then redirects the user to a default page.
The construction of the "True" block is more detailed. We will want a text field bound to a local variable (I call mine "EnteredUsername"), a password field bound to a local variable ("Pass"), and a login button. I also have a link for "Forget Password?" that only shows if the user has failed at least one login, and a "Create an account" link. I used the "Prompt" property of the username box to show what I want my users to enter ("Email Address"). For the password field, though, there is no "Prompt" property; instead, the user has to use the deprecated (in 5.1 and up) Input_SetPrompt Web block from the RichWidgets eSpace (I have created a Wisdom of the Crowds entry for this, though you need to be an OutSystems Agile Network member to access it). I have an If Widget that says "Your login failed, please try again" if the local Boolean variable "RetryLogin" is true (it defaults to false).
The real work begins with the contents of the local Login Action. The Login Action from the System eSpace needs a UserIdentifier parameter. To get it, we look up the entered username in the User entity. The desired functionality is to look for matches in the Username attribute first, and if none are found, to then look at the Email attribute. To do this, we make a query to User passing in the EnteredUsername variable (I call the parameter "Username"), with the conditions User.Is_Active = True and User.Username = Username. Next is an If Widget that checks for the Count property of the query to be equal to 1. If it is, it calls the LoginPassword Action (again, from the System eSpace) using the User.Id attribute of the found record. Otherwise, we move on to a nearly identical query, but this time we look for the Email attribute of the User entity. After this query, we go directly to LoginPassword (if nothing is found, we'll get the same Exception as if the password was bad). Both calls to LoginPassword lead to the desired screen after logging in. On both of the LoginPassword calls, I set the Persistent Login property to true, but you could tie this to a "Keep me logged in" checkbox if you want.
The final touch in the Login Action is to have an Exception Handler for the Invalid Login Exception. This handler sets RetryPassword to be true, and then calls "End." This way, the screen knows to display the Forgot Password link.
That's it! One thing you need to keep in mind is that, if you also create your own account creation system, when you create the User entity, do not set the Password attribute to what the user chose, you need to pass the user's input through the Encrypt Function first. Otherwise, the password will be stored in plain text, and the passwords will not work when you call LoginPassword.
Justin James is the Lead Architect for Conigent.