APEX has built in logic to set the lifetime of a session. To configure this option go to Shared Components / Edit Security Attributes / Maximum Session Idle Time in Seconds and set the time in seconds.
This essentially terminates the user's session in the database and the next time they submit the page they'll be redirected to the login screen. The user will only know that they are logged out once they submit the page. If you have an Interactive Report (IR), or use Partial Page Refresh (PPR) the users won't know they're logged out. Instead it will look as though the report is still trying to load.
Another situation that may happen is that the user is filling out a long form on your page, their session timesout, then they click "submit". They'll be redirected to the login page and they'll lose all the information that they entered.
What if a user wants to extend their session? i.e. they haven't done anything to the page but would like a warning message before we automatically log them out? Or they are entering a log form and don't want to be logged out? I got this idea from the Air Canada web site when I was booking tickets. I really liked the fact that they let me know that they were to end my session, and gave me the option to extend my session.
The following solution will allow you to use APEX's session timeout and resolve the issues listed above. You can view the demo here: http://apex.oracle.com/pls/otn/f?p=20195:2600
Please note that since the demo page is set to public you can refresh after the session is supposed to have timedout and it will still work. If you set the Idle Session in APEX, this will work for pages that require authentication.
Here's a high level overview of what this solution does:
- Start a timer (pingApexSession) that will constantly "ping" (therefore refresh) your APEX session every X seconds.
- Start a timer (idleTimer) to detect movement on the page.
- If the idleTimer times out, give the user the option to extend session
- If the user does not extend their session, terminate their session
I haven't put this code into a production application yet. As I mention below, I plan to make a jQuery plugin for this, so if you please send me any feedback that would be useful fur the plugin.
This solution uses jQuery and the following plugins:
Create Application Process: AP_NULL
- Process Point: On Demand
- Name: AP_NULL
- Type: PL/SQL Anonymous Block
- Process Text: NULL;
Create Application Process: AP_LOGOUT
- Process Point: On Demand
- Name: AP_LOGOUT
- Type: PL/SQL Anonymous Block
- Process Text:
begin
apex_custom_auth.logout(
p_this_app => :app_id,
p_next_app_page_sess => :app_id || ':1');
end;
Create Region: "Extend Session" on Page 0
- Title: Extend Session
- Type: HTML Text
- Static ID: P0_REG_EXTEND_SESSION
- Region Attributes: style="display:none"
- Region Source:
Your session will timeout in: <span id="timeoutCountdownDisplay" style="font-weight:bold"></span>
You can put whatever message you want. Just make sure the span tags exist for the countdown timer.
Create Button: "Extend Session" on Page 0
- Button Name: EXTEND_SESSION
- Text Label: Extend Session
- Display in Region: Extend Session
- Target is a: URL
- URL Target: javascript:gTimeout.timers.killSession.liveFn();
Create Region: "Session Timedout" on Page 0
- Title: Session Ended
- Type: HTML Text
- Static ID:
P0_REG_SESSION_ENDED
- Region Attributes:
style="display:none"
- Region Source: Your session has ended. Please login.
Create Button: "Login" on Page 0
- Button Name: LOGIN
- Text Label: Login
- Display in Region: Session Ended
- Target is a: Page in this Application
- Page: 1
Create Region: "JavaScript - Session Timeout" on Page 0
- Title: JavaScript - Session Timeout
- Type: HTML Text
- Template: No Template
- Region Source:
<script src="#APP_IMAGES#jquery-1.3.2.min.js" type="text/javascript"></script>
<script src="#APP_IMAGES#jquery.simplemodal-1.3.min.js" type="text/javascript"></script>
<script src="#APP_IMAGES#idle-timer-0.7.080609.js" type="text/javascript"></script>
<script src="#APP_IMAGES#jquery.jApex.0.9.2.js" type="text/javascript"></script>
<script src="#APP_IMAGES#jquery.countdown-1.5.3.js" type="text/javascript"></script>
<script type="text/javascript">
</script>
You'll need to upload the JS files beforehand. Please see the list above to obtain the files.
Here's the script to put into the region above. I separated them for display purposes.
I probably should have created this as a jQuery plugin. I may convert it later on.
var gTimeout = {
debug: false,
debugFn: function(pMsg) {
if (gTimeout.debug){
console.log(pMsg);
}
},
modalRegions: {
extendSession: {
id: 'P0_REG_EXTEND_SESSION',
backgroundColor: '#CCC',
opacity: 70,
openFn: function() {
gTimeout.debugFn('gTimeout.modalRegions.extendSession.openFn');
$('#timeoutCountdownDisplay').countdown('destroy');
$('#timeoutCountdownDisplay').countdown({
until: '+' + (gTimeout.timers.killSession.time / 1000),
compact: true,
format: 'M:S'
});
$('#' + gTimeout.modalRegions.extendSession.id).modal({
overlayCss: {backgroundColor: this.backgroundColor},
opacity: this.opacity
});
return;
},
closeFn: function(){
gTimeout.debugFn('gTimeout.modalRegions.extendSession.closeFn');
$.modal.close();
return;
}
},
sessionEnded: {
id: 'P0_REG_SESSION_ENDED',
backgroundColor: 'black',
opacity: 70,
openFn: function() {
gTimeout.debugFn('gTimeout.modalRegions.sessionEnded.openFn');
gTimeout.modalRegions.extendSession.closeFn();
$('#' + gTimeout.modalRegions.sessionEnded.id).modal({
overlayCss: {backgroundColor: this.backgroundColor},
opacity: this.opacity
});
return;
}
}
},
timers: {
pingApexSession: {
id: -1,
time: 5000,
loadFn: function(){
gTimeout.debugFn('gTimeout.timers.pingApexSession.loadFn:');
this.id = setTimeout('gTimeout.timers.pingApexSession.fn();', this.time);
return;
},
unloadFn: function(){
gTimeout.debugFn('gTimeout.timers.pingApexSession.unloadFn:');
clearTimeout(this.id);
this.id = -1;
return;
},
fn: function(){
gTimeout.debugFn('gTimeout.timers.pingApexSession.fn: Extending APEX Session');
jQuery.jApex.ajax({
appProcess: 'AP_NULL',
success: function(){},
async: true
});
gTimeout.timers.pingApexSession.loadFn();
return;
}
},
killSession: {
id: -1,
time: 5000,
loadFn: function(){
gTimeout.debugFn('gTimeout.timers.killSession.loadFn:');
this.id = setTimeout('gTimeout.timers.killSession.killFn();', this.time);
return;
},
unloadFn: function(){
gTimeout.debugFn('gTimeout.timers.killSession.unloadFn: ');
clearTimeout(this.id);
this.id = -1;
gTimeout.modalRegions.extendSession.closeFn();
return;
},
killFn: function(){
gTimeout.debugFn('gTimeout.timers.killSession.killFn: Killing APEX Session');
gTimeout.modalRegions.sessionEnded.openFn();
gTimeout.timers.pingApexSession.unloadFn();
jQuery.jApex.ajax({
appProcess: 'AP_LOGOUT',
success: function(){},
async: true
});
return;
},
liveFn: function(){
gTimeout.debugFn('gTimeout.timers.killSession.liveFn: ');
if (this.id == -1){
alert('Session is not marked to be killed');
return;
}
this.unloadFn();
return;
}
},
idle:{
time: 5000,
loadFn: function(){
gTimeout.debugFn('gTimeout.timers.idle.loadFn:');
$.idleTimer(this.time);
$(document).bind("idle.idleTimer", function(){gTimeout.timers.idle.idleFn();});
return;
},
idleFn: function(){
gTimeout.debugFn('gTimeout.timers.idle.idleFn:');
gTimeout.modalRegions.extendSession.openFn();
if (gTimeout.timers.killSession.id == -1){
gTimeout.timers.killSession.loadFn();
}
return;
}
}
},
loadFn: function() {
gTimeout.timers.pingApexSession.loadFn();
gTimeout.timers.idle.loadFn();
return;
}
};
$(document).ready(function(){
gTimeout.timers.pingApexSession.time = 5 * 1000;
gTimeout.timers.idle.time = 10 * 1000;
gTimeout.timers.killSession.time = 10 * 1000;
gTimeout.modalRegions.extendSession.backgroundColor = '#CCC';
gTimeout.loadFn();
});