Hi,
We are currently on Oracle 12.1.0.2.0 and Apex 5.1.4.
Set-up:
There are three applications.
1. SSO Application - This has homescreen with hyperlink to actual applications(Applications A & B).
2. Application A
3. Application B
Issue:
After the user logs out from any of the pages of above 3 applications, clicking on back button of browser, user is taken back to the exact same screen before logout.
Expected Behavior:
Once logged out, irrespective of what the user does, he should be directed to SSO login page, unless valid login credentials are provided.
Following is the code in our system,
Authentication Scheme:
SSO Application:
Name: SSO Login Authentication Scheme
Sentry Function_Name: sentry
Authentication Function Name: check_credentials
Session Not Valid:
URL: f?p=&APP_ID.:101:&SESSION.
Post Logout URL:
logout?p_app_alias=&APP_ALIAS.
Classic Navigation Bar Entry: Logout
Target -> URL -> logout?p_app_alias=&APP_ALIAS.
Application A & B:
Name: Auxiliary Authentication Scheme
Scheme Type: Custom
Sentry Function_Name: sentry
Session Not Valid:
login_page
Post Logout URL:
login_page
Classic Navigation Bar Entry: Logout
Target -> URL -> logout?p_app_alias=&APP_ALIAS.
CODE
Our code is very close to below article.
https://blogs.oracle.com/oraclemagazine/creating-custom-authentication
Sentry:
FUNCTION sentry
RETURN BOOLEAN
AS
l_current_sid NUMBER;
l_username VARCHAR2 (240) := NULL;
l_cookie OWA_COOKIE.cookie := OWA_COOKIE.get (c_cookie_name);
BEGIN
BEGIN
-- See if there is a leftover user cookie
l_username :=
UPPER (
wwv_flow_utilities.string_to_table2 (l_cookie.vals (1),
sep => '^') (1));
EXCEPTION
WHEN OTHERS
THEN
RETURN FALSE;
END;
l_current_sid := APEX_CUSTOM_AUTH.get_session_id_from_cookie;
-- If the apex session from the cookie is valid, re-instantiate the session
IF APEX_CUSTOM_AUTH.is_session_valid
THEN
wwv_flow.g_instance := l_current_sid;
IF l_username = APEX_CUSTOM_AUTH.get_username
THEN
APEX_CUSTOM_AUTH.define_user_session (
p_user => l_username,
p_session_id => l_current_sid);
RETURN TRUE;
ELSE
-- Username mismatch. Unset the session cookie and redirect back here to take other branch
APEX_CUSTOM_AUTH.LOGOUT (
p_this_app => v ('APP_ID'),
p_next_app_page_sess => v ('APP_ID')
|| ':'
|| NVL (v ('APP_PAGE_ID'), 0)
|| ':'
|| l_current_sid);
-- tell apex engine to stop processing and return false
wwv_flow.g_unrecoverable_error := TRUE;
RETURN FALSE;
END IF;
ELSE
-- application session cookie not valid; we need a new apex session
APEX_CUSTOM_AUTH.define_user_session (
p_user => l_username,
p_session_id => wwv_flow_custom_auth.get_next_session_id);
wwv_flow.g_unrecoverable_error := TRUE; -- tell flows engine to quit
IF OWA_UTIL.get_cgi_env ('REQUEST_METHOD') = 'GET'
THEN
wwv_flow_custom_auth.remember_deep_link (
p_url => 'f?'
|| wwv_flow_utilities.get_cgi_query_string_decoded);
ELSE
wwv_flow_custom_auth.remember_deep_link (
p_url => 'f?p='
|| TO_CHAR (wwv_flow.g_flow_id)
|| ':'
|| TO_CHAR (NVL (wwv_flow.g_flow_step_id, 0))
|| ':'
|| TO_CHAR (wwv_flow.g_instance));
END IF;
-- register session in sessions$,set cookie,redirect back
APEX_CUSTOM_AUTH.post_login (
p_uname => l_username,
p_app_page => wwv_flow.g_flow_id
|| ':'
|| NVL (wwv_flow.g_flow_step_id, 0));
RETURN FALSE;
END IF;
EXCEPTION
WHEN OTHERS
THEN
logger.error (DBMS_UTILITY.format_error_backtrace);
logger.error (DBMS_UTILITY.format_call_stack);
RAISE;
END sentry;
Check Credentails:
C_Cookie_name value is "VALID_SESSION";
FUNCTION check_credentials (p_username IN VARCHAR2,
p_password IN VARCHAR2)
RETURN BOOLEAN
AS
l_result BOOLEAN;
BEGIN
HTP.init;
OWA_UTIL.mime_header ('text/html', FALSE);
set_sso_cookie;
EXCEPTION
WHEN OTHERS
THEN
logger.error (DBMS_UTILITY.format_error_backtrace);
logger.error (DBMS_UTILITY.format_call_stack);
RAISE;
END check_credentials;
PROCEDURE set_sso_cookie
IS
--sets suite cookie, called from check_credentials only
l_t_id NUMBER := NULL;
BEGIN
OWA_COOKIE.send (
name => c_cookie_name,
VALUE => UPPER (v ('P101_USERNAME')) || '^' || v ('APP_SESSION'),
expires => NULL,
PATH => '/');
EXCEPTION
WHEN OTHERS
THEN
logger.error (DBMS_UTILITY.format_error_backtrace);
logger.error (DBMS_UTILITY.format_call_stack);
RAISE;
END set_sso_cookie;
Login Page
PROCEDURE login_page
AS
BEGIN
OWA_UTIL.redirect_url (logout_url);
EXCEPTION
WHEN OTHERS
THEN
logger.error (DBMS_UTILITY.format_error_backtrace);
logger.error (DBMS_UTILITY.format_call_stack);
RAISE;
END;
FUNCTION logout_url
RETURN VARCHAR2
AS
l_return VARCHAR2 (225);
l_host_port VARCHAR2 (25);
l_service_name VARCHAR2 (15);
c_sso_app_alias CONSTANT VARCHAR2 (10) := 'SSO';
BEGIN
l_host_port := OWA_UTIL.get_cgi_env ('HTTP_HOST');
SELECT ords_serv_name
INTO l_service_name
FROM apex_usr.apex_applications
WHERE application_id = c_sso_app_alias;
l_return :=
'http://'
|| l_host_port
|| '/'
|| l_service_name
|| '/f?p='
|| c_sso_app_alias
|| ':LOGIN';
RETURN l_return;
EXCEPTION
WHEN OTHERS
THEN
logger.error (DBMS_UTILITY.format_error_backtrace);
logger.error (DBMS_UTILITY.format_call_stack);
RAISE;
END logout_url;
Logout
PROCEDURE LOGOUT (p_app_alias IN VARCHAR2 DEFAULT NULL)
IS
l_sqlerrm VARCHAR2 (4000) DEFAULT NULL;
l_target_dad VARCHAR2 (400) DEFAULT NULL;
l_target_instance VARCHAR2 (400) DEFAULT NULL;
l_target_url VARCHAR2 (4000) DEFAULT NULL;
l_target_app_alias VARCHAR2 (4000) DEFAULT NULL;
l_target_app_id NUMBER DEFAULT NULL;
l_target_logout_page_id NUMBER DEFAULT NULL;
l_last_instance VARCHAR2 (400) DEFAULT NULL;
l_last_url VARCHAR2 (4000) DEFAULT NULL;
l_last_app_id NUMBER DEFAULT NULL;
l_calling_app_id NUMBER DEFAULT v ('APP_ID');
l_calling_app_alias VARCHAR2 (400)
DEFAULT NVL (p_app_alias, v ('APP_ALIAS'));
l_calling_instance VARCHAR2 (400) DEFAULT NULL;
l_script VARCHAR2 (400) DEFAULT '/apex';
l_host VARCHAR2 (400)
DEFAULT OWA_UTIL.get_cgi_env ('HTTP_HOST');
l_app_cnt NUMBER DEFAULT NULL;
l_logout_cnt NUMBER := 0;
l_app_found BOOLEAN := FALSE;
l_is_sso2 NUMBER := 0;
l_service_name VARCHAR2 (15);
BEGIN
-- get instance info from apps table based on this iteration's invoking application alias
FOR appinfo IN (SELECT instance, id AS app_id
FROM apex_usr.apex_applications
WHERE application_id = l_calling_app_alias)
LOOP
l_calling_instance := appinfo.instance;
l_calling_app_id := appinfo.app_id;
END LOOP;
-- set through cursor of active apps to find the next one needing deauthentication
FOR i IN ( SELECT id AS app_id,
instance,
host_port,
logout_page_id,
application_id AS app_alias
FROM apex_usr.apex_applications
WHERE active_flag = c_yes_value
ORDER BY instance, id)
LOOP
--deauthenticating all the apps
APEX_CUSTOM_AUTH.LOGOUT (p_this_app => i.app_id);
IF NOT l_app_found
THEN
l_last_instance := l_target_instance;
l_last_app_id := l_target_app_id;
l_target_instance := i.instance;
l_target_app_alias := i.app_alias;
l_target_app_id := i.app_id;
IF ( l_last_instance = l_calling_instance
AND NVL (l_last_app_id, -1) = NVL (l_calling_app_id, -1))
OR p_app_alias IS NULL
THEN
l_app_found := TRUE;
END IF;
END IF;
END LOOP;
-- expire the suite cookie if all apps have been deauthenticated
IF p_app_alias IS NULL
THEN
LOGOUT (p_app_alias => l_target_app_alias);
RETURN;
ELSE
expire_cookie;
APEX_CUSTOM_AUTH.logout_then_go_to_url (
p_args => l_target_app_id || ':' || fhlb_logout_url);
END IF;
wwv_flow.g_unrecoverable_error := TRUE;
EXCEPTION
WHEN OTHERS
THEN
l_sqlerrm := SQLERRM;
logger.error (DBMS_UTILITY.format_error_backtrace);
logger.error (DBMS_UTILITY.format_call_stack);
RAISE;
END LOGOUT;
PROCEDURE expire_cookie
IS
l_t_id NUMBER := NULL;
BEGIN
HTP.init;
OWA_UTIL.mime_header ('text/html', FALSE);
OWA_COOKIE.send (name => c_cookie_name,
VALUE => 'user_logged_out',
expires => SYSDATE - 100,
PATH => '/');
EXCEPTION
WHEN OTHERS
THEN
logger.error (DBMS_UTILITY.format_error_backtrace);
logger.error (DBMS_UTILITY.format_call_stack);
RAISE;
END expire_cookie;
Let us know what we are missing, because of which the session is not expiring. Any inputs are highly appreciated.
Thanks,
Shoaib