Introduction
Before APEX 23.1, there was only one way to set the Authentication Scheme at run-time: via a URL and the request APEX_AUTHENTICATION
. In APEX 23.1, you have another option: to identify a Configuration Procedure that APEX uses to set the Authentication Scheme dynamically.
In this post, I will review the existing URL and Request method and provide details on the new Configuration Procedure approach. I will also discuss the benefits of each approach as I go along.
Pre-Requisites
Both approaches require you to set the Switch in Session
flag to 'Yes' for any Authentication scheme you want to switch to.
Use Case
I will use the following Authentication Schemes to demonstrate the two approaches. Each Authentication Scheme has a different Scheme Type, which becomes important for the Configuration Procedure approach.
URL Request Approach
With this approach, you must call an APEX page with a request of APEX_AUTHENTICATION
and assign the name of the Authentication Scheme you want to switch to.
Using this approach, there are two ways to set the request. One is to Construct a URL, and the other is to use a Button.
Constructing a URL
If you need to access your Application directly from a URL (and set the Authentication Scheme), then you can construct the URL as follows.
-- Traditional URL
https://example.com/ords/f?p=201:1::APEX_AUTHENTICATION=OFFICE365
-- Freindly URL
https://example.com/ords/r/demo/blog-demo/home?request=APEX_AUTHENTICATION=OFFICE365
These URLs point to the home page (Page ID: 1, Page Alias: home). They are both setting the Authentication scheme to OFFICE365
. For this particular authentication scheme, the user will be automatically re-directed to log in to Office 365 (if not already logged in) and then, once logged in, re-directed to the home page. It will work the same way for the example Okta Authentication Scheme. For the APEX Accounts scheme, the user would be directed to the login page to enter their APEX credentials.
When to Use This Approach
If you have a portal or static website from which your users access your App, and you know the Authentication Scheme you want to use in advance.
You are accessing your APEX Application from another application. For example, if you are accessing your APEX App from a link in a SaaS application.
From a Button
Another approach is to set the request from a button on your login page.
The 'Sign in with Microsoft' button is configured as follows. When the user clicks the button, APEX sets the Authentication scheme to 'OFFCIE365', then attempts to re-direct to page 1. After this, the remaining authentication steps are the same as for the URL method above.
When to Use This Approach?
- If you have an APEX login page and you want to give your users a choice in how they want to log in to your Application using buttons (like the above screenshot).
Configuration Procedure Approach
Now, to the APEX 23.1 Configuration Procedure approach. The short version is that you can create a PL/SQL procedure (the Configuration Procedure) that will be called when APEX creates a new session. Within this procedure, you can tell APEX which Authentication scheme you want it to use (and some other things that I will get to shortly).
Example Scenario
To help explain how this works, I will walk through an example I have encountered several times. Let's take an APEX SaaS application where we want to separate the data for each Tenant (or customer) that uses the Application. I touched on this subject previously in my blog post Building Multi-Tenant APEX Apps.
Let's say we have three tenants using our APEX SaaS application.
Tenant A wants users to sign in using their Office 365 credentials and their Office 365 environment. i.e., The Tenant's IT department creates an App Registration in Active Directory and generates their own Client ID and Client Secret, which you include in an APEX Web Credential with a static ID of
OFFICE365
.Tenant B wants users to sign in using their own Okta instance. The Tenant's IT department creates its own Client ID and Client Secret, which you include in an APEX Web Credential with a static ID of
OKTA_TENANTA
.Tenant C wants their users to register with your Okta tenant, and you generate a Client ID and Secret from your Okta instance, which you include in a Web Credential with a static ID of
OKTA_CLOUD_NUEVA
.
Each Tenant accesses our Application with a different sub-domain, as follows:
Tenant | URL Prefix |
A | https://tenanta.example.com/ords/saasapp |
B | https://tenantb.example.com/ords/saasapp |
C | https://tenantc.example.com/ords/saasapp |
Before this new Configuration Procedure approach came along, you would have had to create an Authentication Scheme for each of the three tenants (as well as an APEX Web Credential for each Authentication Scheme).
APEX Setups
Web Credentials
For our example, we need three web credentials:
Authentication Schemes
For our example, we need just two Authentication Schemes. One for Office 365 and one for Okta. How can this be, you say, the Credential Store and Discovery URLs will be different for each Tenant? Don't worry; we will get into that.
Office 365 Authentication Scheme
Okta Authentication Scheme
Create a PL/SQL Procedure
The next step is to create a PL/SQL Procedure to dynamically set the Authentication Scheme, APEX Web Credential, and Discovery URL. I have hard-coded the logic in the example procedure below to make things easier to understand. You should store these values in a table.
CREATE OR REPLACE PROCEDURE cn_set_auth_scheme
(p_conf in out nocopy apex_authentication.t_configuration) AUTHID DEFINER IS
l_log_prfx VARCHAR2(500) := 'cn.cn_set_auth_scheme';
l_tenant_host VARCHAR2(1000) := sys.owa_util.get_cgi_env('HTTP_HOST');
BEGIN
-- l_tenant_host captures the host name of the URL used to
-- access the application e.g. tenanta.example.com
apex_debug.info('%s - In Select Auth [%s]', l_log_prfx, l_tenant_host);
-- This logic is for illustration purposes only, these values
-- should really be stored in a table.
CASE
WHEN l_tenant_host LIKE '%tenanta.%' THEN
-- We only have one Credential for Office 365.
p_conf.authentication_name := 'OFFICE365';
-- Even though we do not need to dynamically set the
-- CREDENTIAL_STATIC_ID, we must still provide it, or
-- we will get an error.
p_conf.substitutions := apex_t_varchar2
('CREDENTIAL_STATIC_ID', 'OFFICE365');
-- We can also set the Tenant ID for the current session.
-- This is the same as calling apex_session.set_tenant_id.
-- In this case tenanta has a tenant_id of 1.
p_conf.tenant_id := 1;
WHEN l_tenant_host LIKE 'tenantb.%' THEN
-- Both Tenant B & C can use the OKTA Authentication Scheme.
p_conf.authentication_name := 'OKTA';
-- For Tenant B, we need to dynamically set the Web Credential to
-- 'OKTA_TENANTA'. We also need to change the discovery URL
-- which is specific to each Okta instance.
p_conf.substitutions := apex_t_varchar2
('CREDENTIAL_STATIC_ID', 'OKTA_TENANTA',
'DISCOVERY_URL', 'https://dev-1111111.okta.com/.well-known/openid-configuration');
p_conf.tenant_id := 2;
WHEN l_tenant_host LIKE 'tenantc.%' THEN
-- Both Tenant B & C can use the OKTA Authentication Scheme.
p_conf.authentication_name := 'OKTA';
-- For Tenant C, we need to dynamically set the Web Credential to
-- 'OKTA_CLOUD_NUEVA'. We also need to change the discovery URL
-- which is specific to each Okta instance.
p_conf.substitutions := apex_t_varchar2
('CREDENTIAL_STATIC_ID', 'OKTA_CLOUD_NUEVA',
'DISCOVERY_URL', 'https://dev-2222222.okta.com/.well-known/openid-configuration');
p_conf.tenant_id := 3;
END CASE;
EXCEPTION WHEN OTHERS THEN
apex_debug.error('%s - Unhandled Error [%s]', l_log_prfx, SQLERRM);
RAISE;
END cn_set_auth_scheme;
CREDENTIAL_STATIC_ID
from the one in the Authentication Scheme, you must still set it in the configuration; otherwise, you will get an error when the procedure runs.Setting the Tenant ID
Notice that I also set the tenant_id in the above procedure. This is the equivalent of calling the PL/SQL API apex_session.set_tenant_id
. Setting this value is optional, but if you need to set it, it makes perfect sense to set it here.
Substitutions
Thank you to Patrick Wolf for clarifying in this forum post.
APEX also allows you to add your own substitutions. Let's take this Authentication Scheme as an example.
I have defined my own substitution variable #DISCOVERY_URL#
. I can set this value in the Configuration Procedure as follows:
p_conf.substitutions := apex_t_varchar2
('DISCOVERY_URL', 'https://dev-2222222.okta.com/.well-known/openid-configuration');
When APEX runs the Configuration Procedure, assuming I set select this Authentication Scheme using p_conf.authentication_name := 'OFFICE365';
APEX will replace my substitution variable #DISCOVERY_URL#
with https://dev-2222222.okta.com/.well-known/openid-configuration
and then execute the Authentication Scheme using this value for the Discovery URL.
When is the Procedure Called?
The documentation mentions that the APEX engine may call this procedure at other times, not just during authentication.
In my testing, during authentication, this procedure was called four times before the Post Authentication procedure was called and once afterward. It was also called once for every page refresh and once during log out. Because it is called so frequently, you need to make sure any code you have in this procedure is highly tuned.
p_conf
every time it is called for a given session. I tried changing the return values after a session was established and received the error 'The tenant id already exists for the current session.'Set the Configure Procedure
Now, we can configure the Application using the PL/SQL procedure defined above. Navigate to Application> Shared Components > Security Settings > (Security Tab), and add your procedure name. In my case, it is cn_set_auth_scheme
. In reality, this procedure should be in a PL/SQL package, not defined as a stand-alone procedure.
What Happens When You Run the App?
In my example, when you run the Application with the URL https://tenanta.example.com/ords/saasapp
, APEX will automatically determine the Authentication Scheme applicable for Tenant A (OFFICE365) and then direct you to the Office 365 login process.
If you use the URL https://tenantb.example.com/ords/saasapp
, APEX will automatically determine the Authentication Scheme applicable for Tenant B (OKTA_TENANTA) and then direct you to Okta to perform the login process.
Other Uses for this Approach
Even if you don't have a Multi-Tenant use case, you could also use this approach to automatically switch the APEX Web credential used for logging in to your various instances, e.g., DEV, TEST, PROD. This way, you can have different Office 365, Okta, etc. App Registrations for each of your instances.
Conclusion
Although there are still use cases where the URL Request method makes sense, the Configuration PL/SQL Procedure approach represents another important step to making APEX work seamlessly with Multi-Tenant APEX applications. As a bonus, it also reduces the number of Authentication Schemes we need to create for our Multi-tenant applications.
🔗 Read More
✍️ APEX Posts