Trust is a great force multiplier.
- Tom Ridge
When you build a web application you have two choices for the pages: they can either be public or protected. Mostly you will build applications using a combination of the two. Some pages will be publicly available, like the landing page of your application, but there will also be pages which are protected. Users need to login to the application to use these pages.
Authentication methods
APEX provides us with a couple of authentication schemes by default.
Some of which are:
- Application Express – Every user must exist as an APEX user
- Database Account – Every user must have a database account
- Open Door Credentials

The custom authentication is a good option so you can keep full control over how you want this to work. When you have created your application, or at least the start pages, you go to the shared components page where you go select the Authentication Schemes from the Security section.


Custom authentication scheme
When you want to create your own authentication scheme you must create a (packaged) function that must obey a few rules. The function must accept two parameters: the first is the username, and the second is the password. Both these parameters are varchar2 type. Remember, the username and password parameter are sent as clear text. If you want your application to be more secure, you may want to obfuscate the values before sending them to the authentication function. The simplest form of the authentication function is like this:
FUNCTION authenticate(username_in IN VARCHAR2 ,password_in IN VARCHAR2) RETURN BOOLEAN IS BEGIN RETURN TRUE; END authenticate;
This function is pretty much the same as the open door version, but it’s the start of a real authentication. You create a new authentication scheme by selecting the create button on the screen with the list of defined schemes for this application.
![]()
When you create a new scheme you can choose to create a scheme based on an existing scheme, but in this case we want to create a new scheme based on one of the pre-configured schemes.
In this screen you can create the code for the Authentication Function. You can write the code for the function in this screen, but you can also create the function as a stored (package) function so you can use your IDE to create the code.
To create a real authentication scheme you need to do more than just return true for whatever parameters are sent in. You can write code to check the usernames and their passwords, but that would mean you would have to alter the code every time you want to add or remove a user. You are better off using a table which stores the users for the application. The table can look like this:
Table USERS
| ID | NUMBER(15,0) | Primary key |
| USERNAME | VARCHAR2(50) | Unique constraint |
| PASSWORD | VARCHAR2(50) |
You can this table to save more information on the user, such as the email address but for this example that is not necessary.
The function could be updated to something like this:
FUNCTION authenticate(username_in IN VARCHAR2 ,password_in IN VARCHAR2) RETURN BOOLEAN IS l_value NUMBER; l_returnvalue BOOLEAN; BEGIN BEGIN SELECT 1 INTO l_value FROM users WHERE 1 = 1 AND upper(users.username) = upper(username_in) AND upper(users.password) = upper(password_in); EXCEPTION WHEN no_data_found OR too_many_rows THEN l_value := 0; WHEN OTHERS THEN l_value := 0; END; l_returnvalue := l_value = 1; RETURN l_returnvalue; END;
In this example the username and password are validated against the data in the table. The username and password are stored in plain text in the database, which is not a good idea. You may want to obfuscate the password before storing it.
This is where creating a package for the authentication functions comes in handy. You want to use the same function for obfuscating the password when you store the data as when you check the credentials. You can do this by creating a private function in the package that does the obfuscating and use the outcome of this function for both storing the data and checking the entered credentials. A package like this could look like this:
PACKAGE redgate_authentication IS PROCEDURE adduser(username_in IN VARCHAR2 ,password_in IN VARCHAR2); FUNCTION authenticate(username_in IN VARCHAR2 ,password_in IN VARCHAR2) RETURN BOOLEAN; END redgate_authentication;
PACKAGE BODY redgate_authentication IS -- private functions /******************************************************************************\ || function : obfuscate || parameters : text_in -=> text to be obfuscated || || return value: obfuscated value || || purpose : Hash the value of text_in || || author : PBA || (C) 2013 : Patrick Barel \******************************************************************************/ FUNCTION obfuscate(text_in IN VARCHAR2) RETURN RAW IS l_returnvalue RAW(16); BEGIN dbms_obfuscation_toolkit.md5(input => utl_raw.cast_to_raw(text_in), checksum => l_returnvalue); RETURN l_returnvalue; END obfuscate; -- public functions /******************************************************************************\ || procedure : adduser || parameters : username_in -=> Username of the user to be authenticated || password_in -=> Password of the user to be authenticated || || purpose : Add a user to the users table || || author : PBA || (C) 2013 : Patrick Barel \******************************************************************************/ PROCEDURE adduser(username_in IN VARCHAR2 ,password_in IN VARCHAR2) IS l_obfuscated_password users.password%TYPE; BEGIN l_obfuscated_password := obfuscate(text_in => password_in); INSERT INTO users (id ,username ,password) VALUES (users_seq.nextval ,username_in ,l_obfuscated_password); NULL; END adduser; /******************************************************************************\ || function : authenticate || parameters : username_in -=> Username of the user to be authenticated || password_in -=> Password of the user to be authenticated || || return value: TRUE -=> User is authenticated || FALSE -=> User is not authenticated || || purpose : Check if a user is authenticated based on the username and || password supplied || || author : PBA || (C) 2013 : Patrick Barel \******************************************************************************/ FUNCTION authenticate(username_in IN VARCHAR2 ,password_in IN VARCHAR2) RETURN BOOLEAN IS l_obfuscated_password users.password%TYPE; l_value NUMBER; l_returnvalue BOOLEAN; BEGIN l_obfuscated_password := obfuscate(text_in => password_in); BEGIN SELECT 1 INTO l_value FROM users WHERE 1 = 1 AND upper(users.username) = upper(username_in) AND users.password = l_obfuscated_password; EXCEPTION WHEN no_data_found OR too_many_rows THEN l_value := 0; WHEN OTHERS THEN l_value := 0; END; l_returnvalue := l_value = 1; RETURN l_returnvalue; END authenticate; END redgate_authentication;
Now all you have to do is tell your application that it needs to reference the custom authentication schema. When you created the new authentication schema your application was automatically told to use the new schema, but if you created an authentication scheme in one application and you created a new application where you copied the authentication scheme from an existing application you have to do this yourself. It can also happen that during development you want to use a different authentication scheme than you might in production.

To change the current authentication scheme you go to the Shared components – Authentication Schemes. Select the scheme you want to use and press the ‘Make Current Scheme’ button.
![]()
If the scheme you selected is already the current scheme, then this button will not be available.













19 Comments
Brian Spendolini
10/01/2013
Unless your running over HTTPS, your passing the password to the package unencrypted...all to easy to grab it...obfuscate it BEFORE you pass it to the package and salt your encryption otherwise I can hook a rainbow table up and guess it in minutes....
Patrick Barel
11/01/2013
I think that is exactly what I tried to say with these lines:
Remember, the username and password parameter are sent as clear text. If you want your application to be more secure, you may want to obfuscate the values before sending them to the authentication function.
For this article I obfuscated the password when already in the Oracle Database but again, this is just a simple example.
Brian
11/01/2013
while i understand that, when you put an example in code, people tend to copy and use that code. there should also be a warning in the code itself that this is simplified for this instance, or just make the changes and repost...might save someone some major trouble...
Anonymous
15/02/2013
Is is possible to show an example / modify this one, in such a way so that it IS secure?..
Because I don't know exactly what you guys mean with (or how to do that)
"obfuscate it BEFORE you pass it to the package and salt your encryption "
Learco Brizzi
10/01/2013
Hi Patrick,
Another interesting case is a page where you can log in, but don't have to.
Learco
Muhammad Uzair Awan
11/01/2013
Hi! Thanks for your informative post.
I want to know "how to take backup of all applications, built-in Oracle Application Express."
Regards
Muhammad Uzair Awan
Pakistn
Martin Giffy D'Souza
11/01/2013
Here's a post on how to do automatic backups, which can then be integrated with SVN etc: http://www.talkapex.com/2012/04/command-line-backups-for-apex.html
John Scott
11/01/2013
I wrote a blog post about the APEXExport command line utility here -
http://jes.blogs.shellprompt.net/2006/12/12/backing-up-your-applications/
Hope this helps,
John
Christian
08/02/2013
Hi
Is it possible to connect as a user created with "identified externally"?
Christian
Patrick Barel
15/02/2013
I don't think I understand what your question is exactly.
Christian Dethlefsen
17/05/2013
If this was possible in APEX (with os_authent_prefix = '' (blank)):
http://www.dba-oracle.com/t_windows_external_user_authentication.htm
it would be possible to connect as a database-defined user with all granted privileges.
Regards - Christian
davidglynn
15/02/2013
Why do you have no create or replace statement? this code doesn't run in it's present form.
Patrick Barel
15/02/2013
If you paste the code in an IDE, like PL/SQL Developer then it should run. If you want to create an SQL*Plus script for this, then indeed, you will have to add 'CREATE OR REPLACE' to the scipts.
davidglynn
15/02/2013
Cheers for replying Patrick, but if it doesn't run with the script editor in SQL Workshop, how am I to implement it in a demo on apex.oracle?
Droopy
27/02/2013
Remember, the username and password parameter are sent as clear text. If you want your application to be more secure, you may want to obfuscate the values before sending them to the authentication function.
Is is possible for you to add another piece of code that IS secure..
So what should I change so the the password is not sent as clear text.. ?
Jia
24/04/2013
Thanks a lot Patrick. The article is very helpful for customized user management in Apex. i have been trying to manage user in three category: view, edit and admin. I have created a users table according to your suggestion.
Now i want to assign view, edit and admin role to the users in table. I am trying to find out how, if you can help.
Regards
Jia
Bangladesh
tony miller
09/05/2013
What I would suggest in this case is looking at building an authentication plugin.. Since the APEX development team have included this functionality in the product...
STEVE
16/05/2013
Hi
Sorry to bother you. Wonder if I can bug you with a syntax question. I'm actually a mainframe developer, 20 years of COBOL, ASSEMBLER, CICS, dinosaur technology. A client asked me to rewrite a COBOL application in Apex ( 4.2) I have the application working. I added a link so that the client's administrator can add new users using a table and a custom table base authentication. All of this works fine
I want to set up all of the 80 end users with an initial default password which will force them to change their password the first time they log on. The idea is, there is a flag that defaults to yes. When they log on, if the flag is Yes , they are directed to a URL for an Apex page where they enter a new password. When they click submit , it directs them back to the log in page
I'm having trouble figuring out how to code this. The original log in function is sort of generic:
create or replace FUNCTION checklogin (
p_username IN VARCHAR2,
p_password IN VARCHAR2)
RETURN BOOLEAN IS
BEGIN
FOR c1 IN (SELECT 1
FROM myusers
WHERE upper(username) = upper(p_username)
AND upper(userpassword) = upper(p_password))
LOOP
RETURN TRUE;
END LOOP;
RETURN FALSE;
END;
It works fine
I've tried modifying it several times, but can't quite figure out how to code it
(See below example)
thanks for your time and toughts
create or replace FUNCTION checklogin (
p_username IN VARCHAR2,
p_password IN VARCHAR2)
P_resetpassword varchar2(1);
RETURN BOOLEAN IS
BEGIN
FOR c1 IN (SELECT 1
FROM myusers
WHERE upper(username) = upper(p_username)
AND upper(userpassword) = upper(p_password))
LOOP
RETURN TRUE;
END LOOP;
RETURN FALSE;
END;
IF RETURN = TRUE AND
if v_return = TRUE and resetpassword = 'Y'
THE URL for the page to change the password goes here owa_util.redirect_url('f?p=&APP_ID.:16:&SESSION.::NO:::');
end if;
tony miller
17/05/2013
take above code provided, add column to users table rest_password, varchar2(1) default value 'Y' FUNCTION authenticate(username_in IN VARCHAR2 ,password_in IN VARCHAR2) RETURN BOOLEAN IS l_obfuscated_password users.password%TYPE; l_value NUMBER; l_returnvalue BOOLEAN; l_reset_password varchar2(1); BEGIN l_obfuscated_password := obfuscate(text_in => password_in); BEGIN SELECT 1,reset_password INTO l_value,l_reset_password FROM users WHERE 1 = 1 AND upper(users.username) = upper(username_in) AND users.password = l_obfuscated_password; EXCEPTION WHEN no_data_found OR too_many_rows THEN l_value := 0; l_reset_password := 'N'; WHEN OTHERS THEN l_value := 0; l_reset_password := 'N'; END; if l_value = 1 and l_reset_password = 'Y' owa_util.redirect_url('f?p=&APP_ID.:16:&SESSION.::NO:::'); end if; l_returnvalue := l_value = 1; RETURN l_returnvalue; END authenticate; END redgate_authentication; Thus your user will reset their password on page 16, which will do the another redirect to your login page where they will use their NEW password to login with. Your page 16 will reset their password in the users table..No trackbacks yet.