Wednesday, 24 July 2019

Creating a mobile app with APEX - Part 8: Implementing autologin

In the mobile app I usually enter only a few records of data like an expense or a few activities.
It is pretty annoying when I have to enter username and password each time I use the app for a few seconds. 

In most native apps the authentication is asked once and then stored safely to be reused at further use. The security lies in the presumption that the authorization has been done by unlocking the phone. For most applications this will be sufficient. Only apps with a high stake or high risk like banking apps will need additional authentication. 

So I want this scenario also for my mobile APEX apps: 
  • enter username and password the first time
  • checking the 'Stay logged in' checkbox
  • next time when starting the app the login process is done automatically and I will be led to the starting page
We can accomplish this by using cookies to store client side data. This idea has been described by Christian Rokita in this blogpost

I have created a bit different implementation without the need of an extra page:
  • a custom authentication scheme is created based on a tables with users and passwords
  • a sessions table is created to store tokens and user names
  • a package will accommodate the code needed to read and write the cookies and perform the autologin
  • on the login page the autologin procedure is called to read the cookie. If the cookie points to a valid user a session is created for this user and the session is redirected to the starting page of the application
  • on the login page a Stay logged in checkbox is added
  • an Before Header application process writes the token to the Stay logged in cookie and creates an entry for the token and the user name of the current user

Creating the database objects

You can download the file to create the tables here
Execute the script in your favorite SQL console. 
In your schema you should see:
  • the table aut_users
  • the table aut_sessions
  • the package aut_pck
The table aut_users  contains one record for a user user with a password secret. You can use this data to login to the application. Add your own users in this table.
The implementation of the authentication is very basic and just for demonstration purposes. For serious use at least the passwords should be stored encrypted!

Creating a new authentication scheme

To obtain autologin functionality we need to create a custom authentication scheme:

  • go to the Shared Components > Security > Authentication Schemes
    • press Create
    • chose Based on a pre-configured scheme from the gallery
    • press Next
      • Name: Custom
      • Scheme Type: Custom
      • Authentication Function Name: aut_pck.authenticate
    • hit Create Authentication Scheme
After creating the scheme it is automatically the current scheme.

Changing the login page

Now we will adapt the login page:
  • open the login page 9999
  • add a new process:
    • Name: autologin
    • PL/SQL code:
begin
  aut_pck.autologin
       ( p_app_id    =>  :APP_ID
       , p_page_id   =>  10
       );
end; 
    • Executing Options > Point: Before Header
    • Server-side Condition:
      • Type: Request != Value
      • Value: LOGOUT
  • add a another process:
    • Name: autologout
    • PL/SQL code
begin
  aut_pck.autologout;
end; 
    • Executing Options > 
      • Sequence: 0
      • Point: Before Header
    • Server-side Condition:
      • Type: Request = Value
      • Value: LOGOUT
  • select the Login Region
    • select the item P9999_REMEMBER
    • Change Label to Stay logged in
The last step in processing is clearing the page's session state. We need to limit that to the username and password items in order to have the value of the Stay logged in checkbox available on subsequent pages. 
  • Go to the processing tab
    • Open the Clear Page(s) Cache
    • In the attributes change Settings:
      • Type: Clear Items
      • Item(s): P9999_USERNAME,P9999_PASSWORD  This is done to keep the value of the P9999_REMEMBER in session state
  • Save the page

Adding the Application Process

We will create an application process that will fire on each page before the header on condition that the user is authenticated and P9999_REMEMBER = 'Y'. In this process the Stay logged in cookie will be written, unless there is a valid cookie.
Go create the application process:

  • go to the Shared Components > Application Processes
  • click the button Create
  • Enter 
    • Name: Write autologin cookie
    • Point: On Load: Before Header
    • Press Next
  • Enter the PL/SQL code:
begin
  -- set autologin cookie
  aut_pck.set_username_in_cookie
         ( p_username      =>  :APP_USER
         , p_remember      =>  :P9999_REMEMBER
         );
  :P9999_REMEMBER := 'N';
end;
    • Press Next
  • Enter the condition:
    • Condition Type: PL/SQL Expression
    • Expression 1: 
:APP_USER != 'nobody' and
:P9999_REMEMBER = 'Y'
  • Press Create Process
After writing the cookie the value of P9999_REMEMBER is set to 'N'. This ensures that the process only fires once after login. 

Enabling logging out

As all is set up now you will be automatically logged in until the cookie or the aut_session record expires. To give the user the possiblity to end the autologin we will adapt the logout URL. 

  • go to Shared Components > Navigation > Navigation Bar List
  • select Desktop Navigation Bar
  • click on Sign Out 
  • change Target:
    • Target Type: Page in this Application
    • Page: 9999
    • Request: LOGOUT
  • press Apply Changes
Now chosing Sign Out will result in navigating to the login page with the request LOGOUT.
Previously we have created a logout process on page 9999 which is triggered by the request LOGOUT. This process erases the cookie and removes the corresponding record from aut_sessions thus disabling the autologin. 

Testing the autologin functionality

Now you can test the functionality by logging in with the Stay logged in item checked. 
You can test the autologin by changing the session ID in the URL. Normally the session would be recognized and you would be returned to the login page. Now you just stay logged in. 

Behind the screen you can check on the existence of the cookie STAY_LOGGED_IN_xxx, where xxx is the application number using developer tools like the Chrome Inspector. Likewise you can inspect to content of the table aut_sessions, where a record should exist with the token stored in the cookie and the name of the user. 

Test the Sign out functionality. You should be returned to the login page. 

8 comments:

Goran said...

Hi Dick.
I've noticed that application process code is never executed because the of condition.
:P9999_REMEMBER is not in session state and it's value is NULL so the parameter passed into aut_pck.set_username_in_cookie is null.


Did i missed anything?

Goran

Dick Dral said...

Hi Goran,

Yes, you missed the change of the Page Reset process in which P9999_REMEMBER is excluded from the reset.
Good luck,
Dick

goran said...

Hi Dick,

Thanks for your reply.

Unfortunately, this wasn't a problem. Item :P9999_REMEMBER is still without session value.
Something else is a problem. I can't figure it out what.

Br,
Goran

Dick Dral said...

Hi Goran,

After the Page Load Dynamic Action the item :P9999_REMEMBER is reset to prevent setting the cookie for each page.
So when you login and examine session state on the first page :P9999_REMEMBER will indeed be empty.
You can look at the debug listing. That will provide you with more info.
You need to change the Home URL in the Desktop Interface to force the debugging.

Good luck,
Dick

goran said...

Hi Dick,

I think the problem was in P9999_REMEMBER Maintain Session State setting.
By default it's set to Per request. When I change it to Per session autologin worked.

Carsten Cerny said...

Hi Dick,

I've noticed that the P9999_REMEMBER label didn't changed. I'm wondering why.

I can see the changed label text in the page item settings but after refreshing the page in the browser (different browser tested) it's still showing "Remeber username".

Any idea?

Best,
Carsten

Dick Dral said...

Carsten,

The label is defined in the LOV definition.
You can see that because the label is behind the checkbox.

Kind regards,
Dick

Carsten Cerny said...

Hello Dick,

Thanks, great. Thats' the solution.

And after changing the "Maintain Session State" of this item (like in the comment from goran) now it works.

Best,
Carsten