B4J Question WebApp - Force the user to re-login

Christos Dorotheou

Member
Licensed User
Longtime User
Hi,

I am writing a WebApp using websockets, based on Erel's WebApp Overview ServerExample
using login and the members filtering.

I need a way to force the user to re-login into the WebApp if he navigates beyond the scope
of my Application (Browser's Home pressed or Browser's Back pressed etc).

The way the WebApp functions now is, if the User navigated away from my Application's URLs
lets say to www.google.com and later decides to use the browsers back functionality he will
eventually be able to display the WebApps last page used and continue as it were.

I would like to avoid that by somehow detecting that he navigated off the WebApps scope
and reset either the session's attribute 'registered' to false or by disconnecting the session
all together.

Regards

Chris
 

Fabrice La

Active Member
Licensed User
Longtime User
Already in the example you have :
B4X:
'check that no more than 30 minutes passed since last activity
If req.GetSession.LastAccessedTime +  DateTime.TicksPerMinute * 30 > DateTime.Now Then
      Return True 'allow request to continue
End If

Can be used with by checking if the user is still in the right web site ....
 
Upvote 0

Christos Dorotheou

Member
Licensed User
Longtime User
Already in the example you have :
B4X:
'check that no more than 30 minutes passed since last activity
If req.GetSession.LastAccessedTime +  DateTime.TicksPerMinute * 30 > DateTime.Now Then
      Return True 'allow request to continue
End If

Can be used with by checking if the user is still in the right web site ....

Thanks for the reply,

I am not concerned with session time-out, as it has been taken care of with some functionality I have added to my WebApp modules
as follows: (Besides being able to interrogate the timer is a proof by itself that the current URL is within the WebApp's scope)

B4X:
Private Sub myTimer_Tick

    If CheckIfSessionTimedOut(WS) = True Then
        Log("Session Timed Out")
        myTimer.Initialize("myTimer", 0)
        myTimer.Enabled = False
        If InvalidateSession(WS, True) = True Then
          WS.Eval("window.location = arguments[0]", Array As Object("/index.html"))
        End If
    End If

End Sub

Public Sub CheckIfSessionTimedOut(mWS As WebSocket) As Boolean

    Try
        If mWS.Session.IsInitialized = True Then
            If mWS.Session.GetAttribute2("registered", "") = True Then
                'check if Session has expired
                If mWS.Session.LastAccessedTime + DateTime.TicksPerMinute * Main.dblSessionTimeOut > DateTime.Now Then
                      Return False
                Else
                   Return True
                End If
            Else
              Return True
            End If
        Else
            Log("Can not Interrogate session, because is not initialized")
            Return False
        End If
    Catch
       Log(LastException.Message)
        Log("Excemption Error in CheckIfSessionTimeOut")
        Return False
    End Try

End Sub

Public Sub InvalidateSession(mWS As WebSocket, booInvalidate As Boolean) As Boolean
Dim req As ServletRequest = mWS.UpgradeRequest
Dim sesIDBuffer As String = mWS.Session.Id
Dim result As Boolean = True

If mWS.Session.IsInitialized = True Then
    Try
        req.GetSession.SetAttribute("registered", False)
        If booInvalidate = True Then
            req.GetSession.Invalidate
            Log("Session has been invalidated")
        End If
        RemoveConnection(sesIDBuffer)
        Log("User has been disconnected")
    
        Return result
    Catch
        Log(LastException.Message)
        mWS.Alert(LastException.Message)
        Log("Error occured during disconnection")
        WebUtils.RedirectTo(mWS, Main.mainURL)
        result = False
        Return result
    End Try
Else
    Log("Can not Invalidate session, because is not initialized")
    Return False
End If

End Sub

Public Sub GetBrowserHistory(mWS As WebSocket) As String
    Dim resHistory As Future
    Dim result As String

    If mWS.Session.IsInitialized = True Then
        Try
            resHistory = mWS.EvalWithResult("return document.referrer", Null)
            result = resHistory.Value
        Catch
            Log(LastException.Message)
            result = ""
        End Try
    Else
        Log("Session is not initialized")
        result = ""
    End If

    Return result

End Sub

I have tried different approaches, to detect if the User has navigated away of my WebApp but so far with no success.

I have tried to read the Browsers history in order to detect the last page visited, which would have been a suitable "evidence"
but to my disappointment I've found out that there is no way to be able to read the browsers history or what a user is written
on the browser's URL bar as these are serious security issues as said throughout Google.

I have tried to read the browsers last visited page using WS.EvalWithResult("return window.history.previous.href", Null)

But I get an error.

I have even created Unload and onBeforeUnload Events in the WebSocket_Connected Subroutine as follows in hope
that I would have been able to address my issue:

B4X:
        WS.Eval( _
        "$(window).unload(function(e) {" & _
        "b4j_ws.send(JSON.stringify({type: 'event', event: 'client_unload', params: { lasturl: document.referrer } })); " & _
        "});", Null)
    
        WS.Eval( _
        "$(window).on('beforeunload', function(){" & _
        "b4j_ws.send(JSON.stringify({type: 'event', event: 'client_beforeunload', params: { docready: document.URL } })); " & _
        "});", Null)

Thanks again

Chris
 
Last edited:
Upvote 0

Christos Dorotheou

Member
Licensed User
Longtime User
The WebSocket will be disconnected when the user navigates to another page. You can invalidate the session when that happens.

Hi Erel,

For some reason which I do not understand the WebSocket does not disconnect when I navigate to www.google.com by pressing the Home Button
of Chrome while in my WebApp.

The way I know that is that I've added in the WebSocket_Connected Subroutine 2 logs as follows:

B4X:
Private Sub WebSocket_Connected (WebSocket1 As WebSocket)
  
    WS = WebSocket1
    WS.Flush
    Log("Session Is New: " & WS.Session.IsNew)
    Log("Session ID: " & WS.Session.Id)
  
    If WS.Session.GetAttribute("registered") = True Then
       ........
    Else
       .......
    End If

I always get the same Session.ID whenever I come back to my WebApp's URL using the browsers Back Button after I have Navigated away
with the Home Button.

In your reply, your saying "You can invalidate the session when that happens.", that is exactly my question, how do I detect when this happens
if the WebSocket does not disconnect?

Regards

Chris
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
Not sure if this is implementable in websocket, but works for webpages to detect if user has left a page.
B4X:
// if sessionStorage.getItem(1) == "out" then user has left the page.
if (sessionStorage.getItem(1)==null) sessionStorage.setItem(1,"in");
window.onbeforeunload = function(){
    return "You're about to end your session, are you sure?";
}
window.onunload = function() {
    sessionStorage.setItem(1,"out");
}
 
Upvote 0

rwblinn

Well-Known Member
Licensed User
Longtime User
Hi,

got load and unload working in a websocket using example below.

B4X:
<script type='text/javascript'>
       
        // Handle page unload event
        var myEvent = window.attachEvent || window.addEventListener;
        var chkevent = window.attachEvent ? 'onbeforeunload' : 'beforeunload'; /// make IE7, IE8 compitable

            myEvent(chkevent, function(e) { // For >=IE7, Chrome, Firefox
                var confirmationMessage = 'Are you sure to leave the page?';  // a space
                (e || window.event).returnValue = confirmationMessage;
                return confirmationMessage;
            });

        // Handle page load event
      // executes when complete page is fully loaded, including all frames, objects and images
        $(window).load(function()
        {
          alert("The window is loaded!");
        }); 
           
        // Set the ready functions
        $( document ).ready(function()
        {
            // Connect to the B4J server
            b4j_connect("/ws");
        });
           
    </script>
 
Upvote 0

Christos Dorotheou

Member
Licensed User
Longtime User
I have partially achieved to monitor if a user has left the WebApp's URL by adding into all of the WebApp's Index.html files, a hidden div element in the body section with a custom id and textcontent as such:

B4X:
<div style="display: none;" id="familymember">familymember</div>

I then use a custom subroutine to read the textcontent of the hidden div element and if I get the "expected" string ('familymember' in my case), I know that the displayed page of the browser belongs to my WebApp, otherwise the user has navigated beyond the App's URLs and as of that I can proceed with the session invalidate procedure,
to force user re-login.

Monitoring Routine:

B4X:
Public Sub CheckIfIsFamily(mWS As WebSocket) As String
    Dim resURL As Future
    Dim result As String

    Try
        resURL = mWS.EvalWithResult("return document.getElementById('familymember').textContent", Null)
        result = resURL.Value
    Catch
        result = "NotFamily"
    End Try

    Return result
End Sub

I call the CheckIfIsFamily routine from within a Timer_Tick Event or a Client_Unload Event as follows:

B4X:
    If CheckIfIsFamily(WS).ToLowerCase.Trim <> "familymember" Then
        Try
            If InvalidateSession(WS, False) = True Then
                Log("Good-Bye")
                myTimer.Initialize("myTimer", 0)
                myTimer.Enabled = False
            End If         
        Catch
            Log(LastException.Message)
        End Try
    Else
        Log("Family Member")
    End If

So far while the application is in the ws main Index.html this approach works fine if the user
navigates away by the browsers HOME or Back Buttons, but if he navigates further
down into the WebApps module (other Index.html) I get in the log, a flip-flop effect
as such:

"NotFamily" and on the next Timer tick "Family Member" and so on.

I was wondering if this problem has to do with the WebSocket been passed as parameter
to the next module and not interpreted correctly.

By the way I would like to thank everyone for their help.

Regards

Chris
 
Upvote 0
Top