Secure Oracle E-Business Suite with Oracle IAM Asserter: A Complete Technical Guide
You will configure OAM as the Identity Provider, deploy the EBS Asserter plugin, implement token-based SSO, set up user provisioning via OIM, and write the supporting PL/SQL and Java code required at every layer.
1. What Is the Oracle EBS Asserter?
The Oracle EBS Asserter (also called the Oracle E-Business Suite Asserter) is a J2EE web application that acts as a bridge between a modern identity provider — specifically Oracle Access Manager (OAM) — and Oracle EBS. It converts an OAM-issued token (OAM_ID cookie or OAuth 2.0 bearer token) into a valid ICX_SESSION inside EBS, giving the end user a seamless Single Sign-On experience without re-entering credentials.
The Asserter replaces the older mod_osso / Oracle SSO (OSSO) approach and is the recommended path for all EBS R12.2 deployments running OAM 12c PS3+. It supports:
- SSO into EBS via OAM (SAML, OAuth 2.0, OpenID Connect)
- Automatic EBS session creation tied to OAM authenticated identity
- EBS responsibility and menu-level authorization
- Deep link / bookmark support through the EBS Login URL
- Logout propagation back to OAM (Single Logout — SLO)
2. Architecture Overview
The following diagram shows the complete authentication flow when a user accesses EBS through OAM using the EBS Asserter.
Authentication Flow Explanation
The numbered steps in the diagram correspond to:
- ① User accesses EBS URL — request hits Oracle HTTP Server (OHS) with WebGate
- ② WebGate intercepts the unauthenticated request and redirects to OAM login page
- ③ OAM authenticates the user (LDAP, Kerberos, or MFA) and issues OAM_ID cookie
- ④ OHS forwards the request with the token to the EBS Asserter application
- ⑤ Asserter calls back to OAM to validate the token and extract identity attributes
- ⑥ Asserter queries OID/LDAP to map the OAM identity to an EBS username
- ⑦ Asserter calls EBS APIs to create an ICX session and redirects the user into EBS
- ⑧ EBS reads/writes user session data from Oracle Database (FND_USER, ICX_SESSIONS)
- ⑨ Oracle Identity Manager (OIM) handles user provisioning to EBS in the background
3. Prerequisites & Component Versions
| Component | Min Version | Recommended | Notes |
|---|---|---|---|
| Oracle EBS | R12.2.6 | R12.2.12 | AD/TXK delta patches applied |
| Oracle OAM | 12.2.1.3 | 12.2.1.4 BP05 | Oracle Access Manager 12c |
| Oracle OIM | 12.2.1.3 | 12.2.1.4 | Oracle Identity Manager 12c |
| OID / ODSEE | 11.1.1.9 | 12.2.1.4 | Or Active Directory via AD-OID sync |
| WebLogic Server | 12.2.1.3 | 12.2.1.4 | For Asserter deployment |
| Oracle HTTP Server | 12.1.3 | 12.2.1.4 | Hosts WebGate for OAM |
| WebGate | 12.2.1.3 | 12.2.1.4 | Must match OAM version |
| JDK | JDK 8u181 | JDK 11.0.x | WLS and Asserter runtime |
| EBS Asserter Patch | 25764498 | Latest via MOS | MOS Doc ID 2462012.1 |
SELECT patch_name, patch_type, last_update_date FROM ad_applied_patches WHERE patch_name IN ('25764498','14060571');4. Step 1 — Configure Oracle Internet Directory (OID) for EBS
OID acts as the LDAP directory that stores EBS user identities. OAM authenticates against OID, and the Asserter looks up the EBS username from the OID orclEBSUser attribute.
4.1 EBS–OID User Reconciliation Schema
# ebs_oid_extension.ldif
# Extend OID schema to store EBS-specific attributes
dn: cn=subschemasubentry
changetype: modify
add: attributetypes
attributetypes: ( 2.16.840.1.113894.7.100.10
NAME 'orclEBSUserName'
DESC 'Oracle EBS FND_USER username'
EQUALITY caseIgnoreMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
attributetypes: ( 2.16.840.1.113894.7.100.11
NAME 'orclEBSPersonID'
DESC 'EBS HR Person ID (PER_ALL_PEOPLE_F.PERSON_ID)'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
# Example user entry with EBS attributes
dn: cn=JDOE,cn=Users,dc=example,dc=com
changetype: modify
add: orclEBSUserName
orclEBSUserName: JDOE
-
add: orclEBSPersonID
orclEBSPersonID: 10048
-
add: mail
mail: jdoe@example.com# Apply schema extension to OID
ldapmodify \
-h oid-server.example.com \
-p 389 \
-D "cn=orcladmin" \
-w "OID_Admin_Password" \
-f ebs_oid_extension.ldif
# Verify extension applied
ldapsearch \
-h oid-server.example.com -p 389 \
-D "cn=orcladmin" -w "OID_Admin_Password" \
-b "cn=Users,dc=example,dc=com" \
-s sub "(orclEBSUserName=JDOE)" \
orclEBSUserName orclEBSPersonID mail
# Bulk-load existing EBS users into OID using EBS-OID Provisioning Tool
java -jar ebsOIDSync.jar \
--ebs-jdbc "jdbc:oracle:thin:@ebsdb:1521/EBSPROD" \
--ebs-user APPS \
--ebs-pass "APPS_PASSWORD" \
--oid-host oid-server.example.com \
--oid-port 389 \
--oid-dn "cn=orcladmin" \
--oid-pass "OID_Admin_Password" \
--base-dn "cn=Users,dc=example,dc=com" \
--mode FULL_SYNC4.2 EBS-OID Reconciliation PL/SQL Package
CREATE OR REPLACE PACKAGE APPS.xx_iam_oid_sync_pkg AS
-- Sync a single EBS user to OID via DBMS_LDAP
PROCEDURE sync_user_to_oid(
p_user_name IN VARCHAR2,
p_action IN VARCHAR2 DEFAULT 'UPSERT' -- UPSERT | DELETE
);
-- Full reconciliation of all active EBS users to OID
PROCEDURE full_reconcile(
p_log_id OUT NUMBER
);
-- Get OID DN for a given EBS username
FUNCTION get_oid_dn(
p_ebs_user IN VARCHAR2
) RETURN VARCHAR2;
END xx_iam_oid_sync_pkg;
/
CREATE OR REPLACE PACKAGE BODY APPS.xx_iam_oid_sync_pkg AS
-- OID connection constants
gc_oid_host CONSTANT VARCHAR2(100) := 'oid-server.example.com';
gc_oid_port CONSTANT PLS_INTEGER := 389;
gc_bind_dn CONSTANT VARCHAR2(200) := 'cn=orcladmin';
gc_bind_pwd CONSTANT VARCHAR2(100) := 'OID_Admin_Password';
gc_base_dn CONSTANT VARCHAR2(200) := 'cn=Users,dc=example,dc=com';
FUNCTION get_oid_dn(p_ebs_user IN VARCHAR2) RETURN VARCHAR2 IS
BEGIN
RETURN 'cn=' || UPPER(p_ebs_user) || ',' || gc_base_dn;
END;
PROCEDURE sync_user_to_oid(
p_user_name IN VARCHAR2,
p_action IN VARCHAR2 DEFAULT 'UPSERT'
) IS
l_session DBMS_LDAP.session;
l_message DBMS_LDAP.message;
l_retval PLS_INTEGER;
l_dn VARCHAR2(500);
l_attrs DBMS_LDAP.mod_array;
l_fnd_rec FND_USER%ROWTYPE;
l_email VARCHAR2(240);
l_person_id NUMBER;
BEGIN
-- Fetch EBS user data
SELECT * INTO l_fnd_rec
FROM FND_USER
WHERE USER_NAME = UPPER(p_user_name);
l_dn := get_oid_dn(p_user_name);
-- Connect to OID via DBMS_LDAP
DBMS_LDAP.use_exception := TRUE;
l_session := DBMS_LDAP.init(gc_oid_host, gc_oid_port);
l_retval := DBMS_LDAP.simple_bind_s(l_session, gc_bind_dn, gc_bind_pwd);
IF p_action = 'DELETE' THEN
l_retval := DBMS_LDAP.delete_s(l_session, l_dn);
ELSE -- UPSERT
-- Get employee email and person_id from HR
BEGIN
SELECT email_address, employee_id
INTO l_email, l_person_id
FROM FND_USER
WHERE USER_NAME = UPPER(p_user_name);
EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END;
-- Build LDAP attribute modification array
l_attrs := DBMS_LDAP.create_mod_array(5);
DBMS_LDAP.populate_mod_array(
l_attrs, DBMS_LDAP.MOD_REPLACE, 'orclEBSUserName',
DBMS_LDAP.string_collection(p_user_name));
DBMS_LDAP.populate_mod_array(
l_attrs, DBMS_LDAP.MOD_REPLACE, 'mail',
DBMS_LDAP.string_collection(NVL(l_email, p_user_name || '@example.com')));
IF l_person_id IS NOT NULL THEN
DBMS_LDAP.populate_mod_array(
l_attrs, DBMS_LDAP.MOD_REPLACE, 'orclEBSPersonID',
DBMS_LDAP.string_collection(TO_CHAR(l_person_id)));
END IF;
DBMS_LDAP.populate_mod_array(
l_attrs, DBMS_LDAP.MOD_REPLACE, 'cn',
DBMS_LDAP.string_collection(UPPER(p_user_name)));
DBMS_LDAP.populate_mod_array(
l_attrs, DBMS_LDAP.MOD_REPLACE, 'objectClass',
DBMS_LDAP.string_collection('top', 'person', 'inetOrgPerson', 'orclUser'));
-- Try modify first, then add if entry doesn't exist
BEGIN
l_retval := DBMS_LDAP.modify_s(l_session, l_dn, l_attrs);
EXCEPTION
WHEN OTHERS THEN
l_retval := DBMS_LDAP.add_s(l_session, l_dn, l_attrs);
END;
DBMS_LDAP.free_mod_array(l_attrs);
END IF;
-- Unbind LDAP session
l_retval := DBMS_LDAP.unbind_s(l_session);
EXCEPTION
WHEN OTHERS THEN
BEGIN
l_retval := DBMS_LDAP.unbind_s(l_session);
EXCEPTION WHEN OTHERS THEN NULL; END;
RAISE_APPLICATION_ERROR(-20001,
'OID Sync failed for [' || p_user_name || '] - ' || SQLERRM);
END sync_user_to_oid;
PROCEDURE full_reconcile(p_log_id OUT NUMBER) IS
l_success NUMBER := 0;
l_error NUMBER := 0;
BEGIN
FOR r IN (
SELECT user_name
FROM fnd_user
WHERE NVL(end_date, SYSDATE + 1) > SYSDATE
) LOOP
BEGIN
sync_user_to_oid(r.user_name, 'UPSERT');
l_success := l_success + 1;
EXCEPTION WHEN OTHERS THEN
l_error := l_error + 1;
END;
END LOOP;
-- Return log summary
p_log_id := l_success * 1000 + l_error; -- encode in return
COMMIT;
END full_reconcile;
END xx_iam_oid_sync_pkg;
/5. Step 2 — Install & Configure OAM WebGate on OHS
WebGate is an OHS plugin that intercepts every HTTP request and enforces OAM authentication policies. It replaces the older mod_osso module.
# Set environment
export ORACLE_HOME=/u01/oracle/ohs12c
export WG_INSTALL=/u01/oracle/webgate_install
# Run WebGate installer in silent mode
java -jar $WG_INSTALL/webgate_12.2.1.4.0_linux64.jar \
-silent \
-responseFile $WG_INSTALL/webgate_response.rsp
# webgate_response.rsp content:
# [ENGINE]
# Response File Version=1.0.0.0.0
# [VARIABLES]
# ORACLE_HOME=/u01/oracle/ohs12c
# COMPONENT_PATHS=WebGate
# OHS_ORACLE_HOME=/u01/oracle/ohs12c
# OHS_INSTANCE=/u01/oracle/ohs12c/instances/ohs1
# Register WebGate with OAM using WLST
$ORACLE_HOME/oracle_common/common/bin/wlst.sh register_webgate.py
# register_webgate.py script:
# connect('weblogic','WLS_Password','t3://oam-server:7001')
# registerNewAgent(agentName='EBS_WebGate_Agent',
# agentType='WebGate',
# hostIdentifier='ebsweb01.example.com:443',
# autoCreatePolicy=True,
# protectedResources=['/OA_HTML/...', '/forms/...'])
# Copy generated artifacts to OHS WebGate directory
cp /tmp/wg_artifacts/ObAccessClient.xml \
$ORACLE_HOME/webgate/ohs/config/
cp /tmp/wg_artifacts/cwallet.sso \
$ORACLE_HOME/webgate/ohs/config/
# Restart OHS
$ORACLE_HOME/bin/opmnctl restartproc ias-component=ohs15.1 OHS WebGate Configuration for EBS
# /u01/oracle/ohs12c/instances/ohs1/config/OHS/ohs1/mod_webgate.conf
<IfModule webgate_module>
# WebGate configuration file location
WebGateConfigFileLocation /u01/oracle/ohs12c/webgate/ohs/config
# EBS Application protected resource policies
<Location /OA_HTML>
require valid-user
AuthType Oblix
OblixAuth on
OblixHeaderVars OBUSERDN,OBOAMSESSIONCOOKIENAME
</Location>
<Location /forms>
require valid-user
AuthType Oblix
OblixAuth on
</Location>
# EBS Asserter endpoint — must be unprotected (handles token validation)
<Location /ebsasserter>
Satisfy Any
Allow from all
OblixAuth off
</Location>
# Exclude static EBS resources from OAM protection
<Location /OA_MEDIA>
Satisfy Any
Allow from all
OblixAuth off
</Location>
<Location /OA_HTML/AppsLocalLogin.jsp>
Satisfy Any
Allow from all
OblixAuth off
</Location>
</IfModule>6. Step 3 — Deploy the EBS Asserter on WebLogic
The EBS Asserter is a WAR file that runs inside a WebLogic managed server (or standalone WLS instance). It handles token validation and EBS ICX session creation.
# Extract EBS Asserter patch artifacts
unzip p25764498_R12.2.0_Generic.zip -d /u01/oracle/ebs_asserter
cd /u01/oracle/ebs_asserter
# Edit the Asserter configuration BEFORE deployment
vi config/asserter.properties6.1 EBS Asserter Properties File
##=============================================================
## Oracle EBS Asserter — asserter.properties
## Location: WEB-INF/classes/asserter.properties inside WAR
##=============================================================
## EBS Database Connection (for ICX session creation)
ebs.jdbc.url=jdbc:oracle:thin:@ebsdb01.example.com:1521/EBSPROD
ebs.jdbc.user=APPS
ebs.jdbc.password={AES}encrypted_apps_password_here
ebs.jdbc.pool.minSize=5
ebs.jdbc.pool.maxSize=20
## OAM Token Service Configuration
oam.server.host=oam-server.example.com
oam.server.port=14100
oam.token.validate.url=http://oam-server.example.com:14100/oam/services/rest/auth/api/v1/session/jwt/validate
oam.webgate.agent.name=EBS_WebGate_Agent
oam.webgate.password={AES}encrypted_webgate_password
## OID/LDAP for username resolution
ldap.host=oid-server.example.com
ldap.port=389
ldap.bind.dn=cn=orcladmin
ldap.bind.password={AES}encrypted_oid_password
ldap.user.base.dn=cn=Users,dc=example,dc=com
ldap.user.search.filter=(uid={USERNAME})
ldap.ebs.username.attr=orclEBSUserName
## EBS URL Configuration
ebs.base.url=https://ebsweb01.example.com
ebs.apps.servlet.agent=https://ebsweb01.example.com/OA_HTML/RF.jsp
ebs.login.url=https://ebsweb01.example.com/OA_HTML/OA.jsp?OAFunc=OAHOMEPAGE
ebs.logout.url=https://ebsweb01.example.com/OA_HTML/AppsLogout.jsp
## OAM Logout (Single Logout) URL
oam.logout.url=https://oam-server.example.com/oam/server/logout
## Asserter Cookie Settings
asserter.cookie.name=EBS_IAM_SSO_TOKEN
asserter.cookie.domain=.example.com
asserter.cookie.path=/
asserter.cookie.secure=true
asserter.cookie.httpOnly=true
asserter.session.timeout.mins=480
## EBS Responsibility Mapping (OAM Role → EBS Responsibility)
role.mapping.enabled=true
role.map.CN=EBS_SYSADMIN,OU=Groups=SYSTEM_ADMINISTRATOR|FND
role.map.CN=EBS_GL_USERS,OU=Groups=GENERAL_LEDGER_USER|SQLGL
role.map.CN=EBS_PO_BUYERS,OU=Groups=PURCHASING_BUYER|PO
role.map.CN=EBS_HR_MANAGERS,OU=Groups=HR_MANAGER|PER
## Logging
asserter.log.level=INFO
asserter.log.file=/u01/oracle/ebs_asserter/logs/asserter.log
asserter.audit.enabled=true6.2 Deploy Asserter WAR via WLST
# deploy_asserter.py — WLST deployment script
connect('weblogic', 'WLS_Password', 't3://wls-server.example.com:7001')
deploy(
appName = 'EBSAsserter',
path = '/u01/oracle/ebs_asserter/ebsAsserter.war',
targets = 'EBSAsserterServer',
stageMode = 'stage',
planPath = '/u01/oracle/ebs_asserter/plan.xml'
)
startApplication('EBSAsserter')
# Verify deployment
print(get('/AppDeployments/EBSAsserter/State'))
# Expected output: STATE_ACTIVE
disconnect()
exit()7. Step 4 — EBS Asserter Core Integration Code
The Asserter application contains a servlet that orchestrates the full SSO flow. Below is the core Java servlet and the PL/SQL package it calls to create EBS ICX sessions.
7.1 EBS Asserter Java Servlet
package oracle.apps.ebs.iam.asserter;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.sql.*;
import oracle.jdbc.pool.OracleDataSource;
/**
* Oracle EBS IAM Asserter Servlet
* Validates OAM token and creates EBS ICX session for SSO.
* Deployed at: /ebsasserter/asserter
*/
public class EBSAsserterServlet extends HttpServlet {
private static final String OAM_COOKIE_NAME = "OAM_ID";
private static final String EBS_SSO_COOKIE = "EBS_IAM_SSO_TOKEN";
private AssertorConfig config;
private OAMTokenValidator tokenValidator;
private LDAPUserResolver ldapResolver;
private EBSSessionCreator sessionCreator;
@Override
public void init(ServletConfig cfg) throws ServletException {
super.init(cfg);
config = new AssertorConfig("/WEB-INF/classes/asserter.properties");
tokenValidator = new OAMTokenValidator(config);
ldapResolver = new LDAPUserResolver(config);
sessionCreator = new EBSSessionCreator(config);
log("EBS Asserter Servlet initialized. OAM Host: " + config.getOamHost());
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
processAssertion(req, res);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
processAssertion(req, res);
}
private void processAssertion(HttpServletRequest req, HttpServletResponse res)
throws IOException {
String requestedUrl = req.getParameter("requestUrl");
String oamCookieVal = getCookieValue(req, OAM_COOKIE_NAME);
String oamHeader = req.getHeader("OAM_REMOTE_USER");
// ── Step 1: Extract identity from OAM ──────────────────
OAMIdentity identity = null;
if (oamCookieVal != null) {
identity = tokenValidator.validateCookie(oamCookieVal);
} else if (oamHeader != null) {
identity = tokenValidator.validateHeader(oamHeader);
}
if (identity == null || !identity.isValid()) {
log("Asserter: No valid OAM token. Redirecting to OAM login.");
res.sendRedirect(config.getOamLoginUrl() +
"?request_url=" + encodeUrl(req.getRequestURL().toString()));
return;
}
log("Asserter: Valid OAM identity: " + identity.getUid());
// ── Step 2: Resolve EBS username from OID ─────────────
String ebsUsername = ldapResolver.resolveEBSUsername(identity.getUid());
if (ebsUsername == null) {
log("Asserter: No EBS username found in OID for: " + identity.getUid());
res.sendError(HttpServletResponse.SC_FORBIDDEN,
"User not authorized: no EBS account found.");
return;
}
log("Asserter: OID-resolved EBS username: " + ebsUsername);
// ── Step 3: Resolve EBS responsibility from OAM groups ─
String respKey = resolveResponsibility(identity.getGroups());
String appName = resolveAppName(identity.getGroups());
// ── Step 4: Create EBS ICX session ─────────────────────
EBSSession ebsSession = sessionCreator.createSession(
ebsUsername, respKey, appName,
req.getRemoteAddr()
);
if (ebsSession == null || ebsSession.getSessionId() == null) {
log("Asserter: EBS session creation failed for: " + ebsUsername);
res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Failed to create EBS session.");
return;
}
log("Asserter: EBS ICX session created: " + ebsSession.getSessionId());
// ── Step 5: Set EBS cookies and redirect ───────────────
setEBSCookies(res, ebsSession, config.getCookieDomain());
String redirectUrl = (requestedUrl != null)
? requestedUrl
: config.getEbsLoginUrl()
+ "?icx_ticket=" + ebsSession.getSessionId();
// Write audit log
AuditLogger.log(ebsUsername, identity.getUid(),
req.getRemoteAddr(), "SSO_SUCCESS", redirectUrl);
res.sendRedirect(redirectUrl);
}
private void setEBSCookies(HttpServletResponse res, EBSSession sess, String domain) {
Cookie icxCookie = new Cookie("ICX_SESSION_TICKET", sess.getSessionId());
icxCookie.setDomain(domain);
icxCookie.setPath("/");
icxCookie.setSecure(true);
icxCookie.setHttpOnly(true);
icxCookie.setMaxAge(-1); // session cookie
res.addCookie(icxCookie);
Cookie ssoToken = new Cookie(EBS_SSO_COOKIE, sess.getSsoToken());
ssoToken.setDomain(domain);
ssoToken.setPath("/");
ssoToken.setSecure(true);
ssoToken.setHttpOnly(true);
ssoToken.setMaxAge(sess.getTimeoutSecs());
res.addCookie(ssoToken);
}
private String resolveResponsibility(java.util.List<String> groups) {
for (String grp : groups) {
String mapped = config.getRoleMapping(grp);
if (mapped != null) return mapped.split("\\|")[0];
}
return config.getDefaultResponsibilityKey();
}
private String resolveAppName(java.util.List<String> groups) {
for (String grp : groups) {
String mapped = config.getRoleMapping(grp);
if (mapped != null && mapped.contains("|"))
return mapped.split("\\|")[1];
}
return "FND";
}
private String getCookieValue(HttpServletRequest req, String name) {
Cookie[] cookies = req.getCookies();
if (cookies == null) return null;
for (Cookie c : cookies)
if (name.equals(c.getName())) return c.getValue();
return null;
}
private String encodeUrl(String url) {
try { return java.net.URLEncoder.encode(url, "UTF-8"); }
catch (Exception e) { return url; }
}
}7.2 EBS Session Creator — PL/SQL Bridge
CREATE OR REPLACE PACKAGE APPS.xx_iam_asserter_pkg AS
-- Create ICX session for SSO user (called by Java Asserter via JDBC)
PROCEDURE create_icx_session(
p_user_name IN VARCHAR2,
p_resp_key IN VARCHAR2,
p_app_name IN VARCHAR2,
p_ip_address IN VARCHAR2,
x_session_id OUT VARCHAR2,
x_sso_token OUT VARCHAR2,
x_return_status OUT VARCHAR2,
x_error_msg OUT VARCHAR2
);
-- Validate an existing ICX session (heartbeat check)
FUNCTION validate_icx_session(
p_session_id IN VARCHAR2,
p_ip_address IN VARCHAR2
) RETURN VARCHAR2; -- Returns 'Y' or 'N'
-- Terminate ICX session (for SLO)
PROCEDURE terminate_icx_session(
p_session_id IN VARCHAR2,
x_return_status OUT VARCHAR2
);
-- Get session information
PROCEDURE get_session_info(
p_session_id IN VARCHAR2,
x_user_name OUT VARCHAR2,
x_resp_name OUT VARCHAR2,
x_last_connect OUT DATE,
x_return_status OUT VARCHAR2
);
END xx_iam_asserter_pkg;
/
CREATE OR REPLACE PACKAGE BODY APPS.xx_iam_asserter_pkg AS
PROCEDURE create_icx_session(
p_user_name IN VARCHAR2,
p_resp_key IN VARCHAR2,
p_app_name IN VARCHAR2,
p_ip_address IN VARCHAR2,
x_session_id OUT VARCHAR2,
x_sso_token OUT VARCHAR2,
x_return_status OUT VARCHAR2,
x_error_msg OUT VARCHAR2
) IS
l_user_id NUMBER;
l_resp_id NUMBER;
l_app_id NUMBER;
l_session ICX_SESSIONS%ROWTYPE;
l_token VARCHAR2(2000);
BEGIN
x_return_status := 'S'; -- Success default
-- 1. Validate EBS user account is active
BEGIN
SELECT user_id
INTO l_user_id
FROM fnd_user
WHERE user_name = UPPER(p_user_name)
AND NVL(end_date, SYSDATE+1) > SYSDATE;
EXCEPTION
WHEN NO_DATA_FOUND THEN
x_return_status := 'E';
x_error_msg := 'EBS user not found or inactive: ' || p_user_name;
RETURN;
END;
-- 2. Get responsibility and application IDs
BEGIN
SELECT r.responsibility_id, r.application_id
INTO l_resp_id, l_app_id
FROM fnd_responsibility_vl r
JOIN fnd_application a ON a.application_id = r.application_id
WHERE r.responsibility_key = p_resp_key
AND a.application_short_name = p_app_name
AND NVL(r.end_date, SYSDATE+1) > SYSDATE
AND ROWNUM = 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
x_return_status := 'E';
x_error_msg := 'Responsibility not found: ' || p_resp_key || ' / ' || p_app_name;
RETURN;
END;
-- 3. Check user has the responsibility assigned
DECLARE
l_cnt NUMBER;
BEGIN
SELECT COUNT(1) INTO l_cnt
FROM fnd_user_resp_groups_direct
WHERE user_id = l_user_id
AND responsibility_id = l_resp_id
AND responsibility_application_id = l_app_id
AND NVL(end_date, SYSDATE+1) > SYSDATE;
IF l_cnt = 0 THEN
x_return_status := 'E';
x_error_msg := 'User '||p_user_name||' lacks responsibility: '||p_resp_key;
RETURN;
END IF;
END;
-- 4. Initialize FND Global context
FND_GLOBAL.apps_initialize(
user_id => l_user_id,
resp_id => l_resp_id,
resp_appl_id => l_app_id
);
-- 5. Create ICX session using ICX_SEC API
x_session_id := ICX_SEC.getsessioncookie(
p_session_id => NULL, -- creates new
p_user_id => l_user_id,
p_responsibility_id => l_resp_id,
p_resp_appl_id => l_app_id,
p_security_group_id => 0,
p_language_code => 'US',
p_date_format => 'DD-MON-RRRR',
p_nls_language => 'AMERICAN',
p_nls_numeric_characters=> '.,'
);
IF x_session_id IS NULL THEN
x_return_status := 'E';
x_error_msg := 'ICX_SEC.getsessioncookie returned NULL';
RETURN;
END IF;
-- 6. Update session with IP address for security
UPDATE icx_sessions
SET last_connect = SYSDATE,
counter = counter + 1,
proxy_user_id = l_user_id,
mode_code = 'SSOEXT',
disabled_flag = 'N'
WHERE session_id = x_session_id;
-- 7. Generate SSO audit token (base64 of session+timestamp)
x_sso_token := UTL_RAW.cast_to_varchar2(
UTL_ENCODE.base64_encode(
UTL_RAW.cast_to_raw(
x_session_id || '|'
|| TO_CHAR(SYSDATE, 'YYYYMMDDHH24MISS')
|| '|' || p_user_name
)
)
);
-- 8. Write IAM SSO audit record
INSERT INTO xx_iam_sso_audit_log (
log_id, user_name, session_id, sso_token,
ip_address, login_time, resp_key, status
) VALUES (
xx_iam_sso_audit_seq.NEXTVAL,
p_user_name, x_session_id, x_sso_token,
p_ip_address, SYSDATE, p_resp_key, 'LOGGED_IN'
);
COMMIT;
x_return_status := 'S';
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
x_return_status := 'E';
x_error_msg := SQLERRM;
END create_icx_session;
FUNCTION validate_icx_session(
p_session_id IN VARCHAR2,
p_ip_address IN VARCHAR2
) RETURN VARCHAR2 IS
l_valid VARCHAR2(1);
BEGIN
SELECT CASE WHEN COUNT(1) > 0 THEN 'Y' ELSE 'N' END
INTO l_valid
FROM icx_sessions
WHERE session_id = p_session_id
AND disabled_flag = 'N'
AND last_connect >= SYSDATE - (1/24) * 8; -- 8-hour timeout
RETURN l_valid;
END;
PROCEDURE terminate_icx_session(
p_session_id IN VARCHAR2,
x_return_status OUT VARCHAR2
) IS
BEGIN
UPDATE icx_sessions
SET disabled_flag = 'Y',
last_connect = SYSDATE
WHERE session_id = p_session_id;
-- Update audit log
UPDATE xx_iam_sso_audit_log
SET status = 'LOGGED_OUT', logout_time = SYSDATE
WHERE session_id = p_session_id;
COMMIT;
x_return_status := 'S';
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
x_return_status := 'E';
END;
PROCEDURE get_session_info(
p_session_id IN VARCHAR2,
x_user_name OUT VARCHAR2,
x_resp_name OUT VARCHAR2,
x_last_connect OUT DATE,
x_return_status OUT VARCHAR2
) IS
BEGIN
SELECT fu.user_name, rv.responsibility_name, s.last_connect
INTO x_user_name, x_resp_name, x_last_connect
FROM icx_sessions s
JOIN fnd_user fu
ON fu.user_id = s.user_id
JOIN fnd_responsibility_vl rv
ON rv.responsibility_id = s.responsibility_id
WHERE s.session_id = p_session_id;
x_return_status := 'S';
EXCEPTION
WHEN NO_DATA_FOUND THEN
x_return_status := 'E';
END;
END xx_iam_asserter_pkg;
/7.3 Audit Log DDL
-- IAM SSO Audit Log Table
CREATE TABLE APPS.xx_iam_sso_audit_log (
log_id NUMBER NOT NULL,
user_name VARCHAR2(100) NOT NULL,
session_id VARCHAR2(200) NOT NULL,
sso_token VARCHAR2(2000),
ip_address VARCHAR2(45),
login_time DATE DEFAULT SYSDATE,
logout_time DATE,
resp_key VARCHAR2(100),
status VARCHAR2(30) DEFAULT 'LOGGED_IN',
oam_uid VARCHAR2(200),
user_agent VARCHAR2(500),
created_by NUMBER DEFAULT -1,
creation_date DATE DEFAULT SYSDATE,
CONSTRAINT xx_iam_sso_audit_pk PRIMARY KEY (log_id)
);
CREATE SEQUENCE APPS.xx_iam_sso_audit_seq
START WITH 1 INCREMENT BY 1 NOCYCLE NOCACHE;
CREATE INDEX APPS.xx_iam_sso_audit_idx1
ON APPS.xx_iam_sso_audit_log(user_name, login_time);
CREATE INDEX APPS.xx_iam_sso_audit_idx2
ON APPS.xx_iam_sso_audit_log(session_id);
COMMENT ON TABLE APPS.xx_iam_sso_audit_log
IS 'IAM Asserter SSO Session Audit Log - tracks all SSO login/logout events';
-- Grant access for Asserter's JDBC user
GRANT INSERT, UPDATE, SELECT ON APPS.xx_iam_sso_audit_log TO apex_ebs_user;
GRANT SELECT ON APPS.xx_iam_sso_audit_seq TO apex_ebs_user;8. Step 5 — Oracle Identity Manager (OIM): EBS User Provisioning
OIM automates user lifecycle management — creating, modifying, and deactivating EBS accounts when HR events occur (new hire, role change, termination). The EBS connector for OIM communicates via a JDBC connector or the EBS SOAP/REST APIs.
8.1 OIM EBS Connector Configuration
8.2 OIM Custom Event Handler for EBS Provisioning
package oracle.apps.ebs.iam.oim;
import oracle.iam.platform.kernel.vo.EventResult;
import oracle.iam.platform.kernel.vo.Orchestration;
import oracle.iam.platform.kernel.spi.PostProcessHandler;
import oracle.iam.identity.usermgmt.api.UserManager;
import oracle.iam.identity.usermgmt.vo.User;
import oracle.iam.provisioning.api.ProvisioningService;
import oracle.iam.platform.OIMClient;
import java.sql.*;
import java.util.*;
/**
* OIM Post-Process Event Handler
* Triggered after a new OIM user is created.
* Automatically provisions EBS FND_USER account.
*/
public class EBSUserProvisionHandler implements PostProcessHandler {
private static final String EBS_RESOURCE_NAME = "OracleEBSProd";
@Override
public EventResult execute(long processId, long eventId, Orchestration orch) {
try {
HashMap<String, Object> params = orch.getParameters();
String uid = (String) params.get("usr_login");
String email = (String) params.get("usr_email");
String firstName = (String) params.get("usr_first_name");
String lastName = (String) params.get("usr_last_name");
String empNumber = (String) params.get("usr_emp_no");
// Call EBS to create the FND_USER account
provisionEBSUser(uid, email, firstName, lastName, empNumber);
// Assign default OAM group (maps to EBS responsibility)
assignDefaultOAMGroup(uid, "CN=EBS_GENERAL_USERS,OU=Groups,DC=example,DC=com");
return new EventResult(); // Success
} catch (Exception e) {
System.err.println("EBSUserProvisionHandler failed: " + e.getMessage());
return new EventResult(); // Non-blocking — log but don't fail user creation
}
}
private void provisionEBSUser(String uid, String email,
String first, String last, String empNo) throws Exception {
Connection conn = getEBSConnection();
CallableStatement cs = conn.prepareCall(
"{ call APPS.FND_USER_PKG.CreateUser("
+ " x_user_name => ?,"
+ " x_owner => 'CUST',"
+ " x_unencrypted_password => ?,"
+ " x_start_date => sysdate,"
+ " x_end_date => null,"
+ " x_email_address => ?,"
+ " x_description => ?,"
+ " x_employee_id => ?"
+ ") }"
);
cs.setString(1, uid.toUpperCase());
cs.setString(2, "Welcome#1Temp"); // Force change on first login
cs.setString(3, email);
cs.setString(4, first + " " + last + " (IAM Provisioned)");
if (empNo != null) cs.setInt(5, Integer.parseInt(empNo));
else cs.setNull(5, Types.INTEGER);
cs.execute();
conn.commit();
cs.close();
conn.close();
System.out.println("OIM→EBS: User provisioned: " + uid);
}
private void assignDefaultOAMGroup(String uid, String groupDN) {
// Add user to OAM/OID group via LDAP — triggers OAM policy assignment
// Implementation uses JNDI DirContext (omitted for brevity)
System.out.println("OIM: Assigned default OAM group to: " + uid);
}
private Connection getEBSConnection() throws Exception {
return DriverManager.getConnection(
"jdbc:oracle:thin:@ebsdb01.example.com:1521/EBSPROD",
"APPS", System.getenv("EBS_APPS_PWD")
);
}
@Override public void initialize(HashMap<String,String> p) {}
@Override public boolean cancel(long a, long b, Orchestration o) { return false; }
@Override public void compensate(long a, long b, Orchestration o) {}
}8.3 EBS Responsibility Sync PL/SQL
CREATE OR REPLACE PROCEDURE APPS.xx_iam_assign_responsibility(
p_user_name IN VARCHAR2,
p_resp_key IN VARCHAR2,
p_app_name IN VARCHAR2,
p_action IN VARCHAR2 DEFAULT 'GRANT', -- GRANT | REVOKE
x_status OUT VARCHAR2,
x_message OUT VARCHAR2
) AS
l_user_id NUMBER;
l_resp_id NUMBER;
l_app_id NUMBER;
BEGIN
-- Lookup user
SELECT user_id INTO l_user_id
FROM fnd_user WHERE user_name = UPPER(p_user_name);
-- Lookup responsibility
SELECT r.responsibility_id, r.application_id
INTO l_resp_id, l_app_id
FROM fnd_responsibility_vl r
JOIN fnd_application a ON a.application_id = r.application_id
WHERE r.responsibility_key = p_resp_key
AND a.application_short_name = p_app_name
AND ROWNUM = 1;
IF p_action = 'GRANT' THEN
-- Add responsibility using FND_USER_PKG
FND_USER_PKG.ADDRESP(
username => UPPER(p_user_name),
resp_appl => p_app_name,
responsibility => p_resp_key,
security_group => 'STANDARD',
description => 'Assigned by OIM IAM Integration',
start_date => SYSDATE,
end_date => NULL
);
ELSIF p_action = 'REVOKE' THEN
-- End-date the responsibility assignment
UPDATE fnd_user_resp_groups_direct
SET end_date = SYSDATE - 1/(24*60)
WHERE user_id = l_user_id
AND responsibility_id = l_resp_id
AND responsibility_application_id = l_app_id;
END IF;
COMMIT;
x_status := 'S';
x_message := p_action || ' SUCCESS: ' || p_resp_key || ' for ' || p_user_name;
EXCEPTION
WHEN NO_DATA_FOUND THEN
x_status := 'E';
x_message := 'User or responsibility not found: ' || p_user_name || ' / ' || p_resp_key;
WHEN OTHERS THEN
ROLLBACK;
x_status := 'E';
x_message := SQLERRM;
END xx_iam_assign_responsibility;
/9. Step 6 — OAM Authentication & Authorization Policies for EBS
Define OAM policies that protect EBS URL patterns and specify what authentication strength is required for each resource.
<ApplicationDomain
name="EBS_R12_Production"
description="OAM Policies for Oracle EBS R12.2 SSO via Asserter">
<Resources>
<Resource type="HTTP" host="ebsweb01.example.com"
port="443" pattern="/"
authzPolicy="EBS_AuthZ_Policy"
authnPolicy="EBS_AuthN_Policy"/>
<!-- Unprotected resources (no auth needed) -->
<Resource type="HTTP" pattern="/OA_HTML/AppsLocalLogin.jsp"
protected="false"/>
<Resource type="HTTP" pattern="/OA_MEDIA/**"
protected="false"/>
<Resource type="HTTP" pattern="/ebsasserter/**"
protected="false"/>
<!-- Higher-security resources require MFA -->
<Resource type="HTTP" pattern="/OA_HTML/OA.jsp?OAFunc=OAGLADMINPAGE"
authnPolicy="EBS_AuthN_MFA_Policy"/>
</Resources>
<AuthenticationPolicies>
<AuthenticationPolicy name="EBS_AuthN_Policy">
<AuthenticationScheme ref="LDAPScheme"/>
<!-- On success: redirect to EBS Asserter -->
<SuccessURL>https://ebsweb01.example.com/ebsasserter/asserter</SuccessURL>
<FailureURL>https://oam-server.example.com/oam/server/obrareq.cgi</FailureURL>
</AuthenticationPolicy>
<AuthenticationPolicy name="EBS_AuthN_MFA_Policy">
<AuthenticationScheme ref="MFAOTPScheme"/>
<SuccessURL>https://ebsweb01.example.com/ebsasserter/asserter</SuccessURL>
</AuthenticationPolicy>
</AuthenticationPolicies>
<AuthorizationPolicies>
<AuthorizationPolicy name="EBS_AuthZ_Policy">
<Conditions>
<Condition type="IdentityAssertion">
<Attribute name="memberOf"
value="CN=EBS_USERS,OU=Groups,DC=example,DC=com"/>
</Condition>
<Condition type="IPAddress">
<AllowedRange>10.0.0.0/8</AllowedRange>
<AllowedRange>192.168.0.0/16</AllowedRange>
</Condition>
</Conditions>
<ResponseAttrs>
<!-- OAM headers passed to OHS/Asserter after successful authZ -->
<Header name="OAM_REMOTE_USER" type="ldap" attr="uid"/>
<Header name="OAM_USER_DN" type="ldap" attr="dn"/>
<Header name="OAM_EBS_USERNAME" type="ldap" attr="orclEBSUserName"/>
<Header name="OAM_GROUPS" type="ldap" attr="isMemberOf"/>
<Header name="OAM_USER_EMAIL" type="ldap" attr="mail"/>
<Cookie name="OAM_ID" domain=".example.com"/>
</ResponseAttrs>
</AuthorizationPolicy>
</AuthorizationPolicies>
</ApplicationDomain>/ebsasserter/logout endpoint on logout, which triggers xx_iam_asserter_pkg.terminate_icx_session() in EBS. Configure the OAM Logout URL in the Agent registration to include: https://ebsweb01.example.com/ebsasserter/logout?returnUrl={logoutReturnUrl}10. Troubleshooting Common IAM Asserter Issues
| Error / Symptom | Root Cause | Resolution |
|---|---|---|
| Asserter returns HTTP 403 | OAM group not in Asserter role mapping or user missing orclEBSUserName in OID | Check asserter.properties role.map entries; run LDAP search to verify orclEBSUserName attribute |
| ICX_SEC.getsessioncookie returns NULL | EBS FND_USER is end-dated, or responsibility end-dated | Query SELECT * FROM fnd_user WHERE user_name='XX' and SELECT * FROM fnd_user_resp_groups_direct |
| OAM_ID cookie missing in Asserter | WebGate not forwarding OAM cookie headers; OHS mod_webgate config error | Check mod_webgate.conf; verify OblixHeaderVars includes OBOAMSESSIONCOOKIENAME |
| Login loop (redirect storm) | Asserter URL is OAM-protected; should be unprotected | Add /ebsasserter/** as unprotected resource in OAM Application Domain |
| OIM provisioning fails with ORA-20001 | FND_USER_PKG.CreateUser duplicate username or missing required fields | Check SELECT * FROM fnd_user WHERE user_name='XX' for duplicates; verify all required params |
| EBS session expires too fast | ICX profile ICX: Session Timeout set too low | Set via: FND_PROFILE.SAVE('ICX_SESSION_TIMEOUT','480','SITE'); (480 = 8 hours) |
| Kerberos SSO not working | WebGate Kerberos scheme not configured in OAM | Configure KerberosScheme in OAM Admin Console and ensure SPN is registered: setspn -A HTTP/ebsweb01.example.com svc_oam |
Diagnostic Queries
-- 1. Check ICX active sessions for a user
SELECT s.session_id,
fu.user_name,
s.last_connect,
s.disabled_flag,
s.mode_code,
r.responsibility_name
FROM icx_sessions s
JOIN fnd_user fu ON fu.user_id = s.user_id
JOIN fnd_responsibility_vl r
ON r.responsibility_id = s.responsibility_id
WHERE fu.user_name = 'SYSADMIN'
AND s.disabled_flag = 'N'
ORDER BY s.last_connect DESC;
-- 2. Check IAM SSO audit log
SELECT user_name, session_id, ip_address,
login_time, logout_time, resp_key, status
FROM xx_iam_sso_audit_log
WHERE login_time >= SYSDATE - 1
ORDER BY login_time DESC
FETCH FIRST 50 ROWS ONLY;
-- 3. EBS users without OID mapping (need sync)
SELECT u.user_name, u.email_address, u.creation_date
FROM fnd_user u
WHERE NVL(u.end_date, SYSDATE+1) > SYSDATE
AND u.user_name NOT IN (
-- Simulated: replace with actual OID sync verification table
SELECT ebs_user_name FROM xx_oid_sync_log
WHERE sync_status = 'SUCCESS'
)
ORDER BY u.user_name;
-- 4. ICX session timeout profile value
SELECT profile_option_value
FROM fnd_profile_option_values
JOIN fnd_profile_options USING (profile_option_id)
WHERE profile_option_name = 'ICX_SESSION_TIMEOUT'
AND level_id = 10001; -- SITE level
-- 5. OIM provisioned users (last 7 days)
SELECT user_name, email_address,
TO_CHAR(creation_date,'DD-MON-YYYY HH24:MI') created,
description
FROM fnd_user
WHERE description LIKE '%IAM Provisioned%'
AND creation_date >= SYSDATE - 7
ORDER BY creation_date DESC;11. Summary, Security Checklist & Next Steps
Security Hardening Checklist
| Item | Required | Notes |
|---|---|---|
| Asserter properties passwords encrypted | ✅ Mandatory | Use OPSS CSF KeyStore or OWallet for credential storage |
| HTTPS enforced on all EBS/OAM URLs | ✅ Mandatory | TLS 1.2+ only; disable TLS 1.0/1.1 |
| WebGate uses certificate-based trust | ✅ Mandatory | Configure OAM + WebGate with OWallet certs, not passwords |
| ICX session timeout ≤ 8 hours | ✅ Recommended | Profile: ICX_SESSION_TIMEOUT = 480 |
| OAM MFA for sensitive EBS functions | ⭐ Recommended | Apply MFAOTPScheme to GL/Finance/Admin URLs |
| Audit log purge policy | ✅ Mandatory | Retain IAM audit logs ≥ 90 days; archive to cold storage |
| OIM role approval workflow | ⭐ Recommended | Manager approval before EBS responsibility assignment |
Recommended Next Steps
- Configure Oracle Adaptive Authentication (OAA) for risk-based MFA on EBS login
- Integrate Oracle Privileged Account Manager (OPAM) for DBA / privileged EBS accounts
- Implement OIM Role Catalog with self-service EBS responsibility requests
- Set up OAM Token Relay for EBS ISG REST API OAuth 2.0 client credential flows
- Configure SAML 2.0 federation via OAM as IdP for cross-domain EBS access
- Deploy Oracle Identity Governance (OIG) Certification Campaigns for periodic EBS access reviews
Comments