SSO Integration: Connecting PingFederate / PingAccess with Oracle Identity Governance

SO Integration of PingFederate with Oracle Identity Governance

A couple of years ago, IDMWORKS had a client who standardized on PingFederate and PingAccess for Single Sign-On (SSO) for all Web-based Applications (authentication and authorization) within the enterprise.  This was a fairly straightforward project that was successful for nearly all of the applications.

One of the integrations that was challenging was the deployment of Oracle Identity Governance (OIG 12c). The need to establish an authenticated user session that OIG would recognize became a challenge given the disparity between the PingIdentity and Oracle platforms. Without readily available out of the box (OOTB) component(s) and/or adapter(s) to integrate these platforms, a customized solution would have to be implemented.

Here’s how IDMWORKS addressed the integration:

The goal of the integration was to maintain the state of an already authenticated user from PingFederate/PingAccess to OIG. IDMWORKS created a custom security provider (i.e., Identity Asserter) within OIG’s WebLogic Server Security Realm (let’s call it PingAccess Identity Asserter). The PingAccess Identity Asserter retrieved a PingAccess cookie from the request header, from which we can extract the userID of the already authenticated user from PingFederate.

Since the value of this PingAccess cookie is an encrypted base64 JWT token, our Identity asserter had to decode this token in order to retrieve the userID value. Once the userID is retrieved, it was passed to the PingAccess Identity Asserter’s callback handler. From there, an authenticated user session was created within OIG’s WebLogic Domain. THis establishes the single sign-on between Ping Federate/Access and OIG.

Here’s snippet of the Example Code to how this custom PingAccess Identity Asserter was constructed:

Please Note: Please refer to Oracle’s Documentation for details on how the develop/build a custom Identity Asserter in WebLogic Server (https://docs.oracle.com/middleware/1213/wls/DEVSP/ia.htm#DEVSP244). The following is based upon the WebLogic OOTB Simple Sample Identity Asserter example of building a Custom Identity Asserter

To implement a custom PingAccess Identity Asserter for WebLogic Server a provider that implements AuthenticationProviderV2 and IdentityAsserterV2 needs to be written. A Mbean definition file and a callback handler must also be created.

import java.util.logging.Level;

 

import javax.security.auth.callback.CallbackHandler;

import javax.security.auth.login.AppConfigurationEntry;

import javax.servlet.http.Cookie;

import javax.servlet.http.HttpServletRequest;

 

import com.custom.utls.OAuth2Utils; // Custom Utility Class to decode JWT tokens

 

import weblogic.management.security.ProviderMBean;

import weblogic.security.service.ContextHandler;

import weblogic.security.spi.AuthenticationProviderV2;

import weblogic.security.spi.IdentityAsserterV2;

import weblogic.security.spi.IdentityAssertionException;

import weblogic.security.spi.PrincipalValidator;

import weblogic.security.spi.SecurityServices;

 

public final class PingAccessIdentityAsserterProviderImpl implements AuthenticationProviderV2, IdentityAsserterV2

{

final static private String TOKEN_TYPE   = “SM_USER”; // the kind of token we handle

final static private String TOKEN_PREFIX = “username=”; // the token contains a string in the form “username=someusername

 

private String description; // a description of this provider

 

/**

* Initialize the PingAccess identity asserter.

*

* @param mbean A ProviderMBean that holds the PingAccess identity asserter’s

* configuration data.  This mbean must be an instance of the PingAccess

* identity asserter’s mbean.

*

* @param services The SecurityServices gives access to the auditor

* so that the provider can to post audit events.

* The PingAccess role mapper doesn’t use this parameter.

*

* @see SecurityProvider

*/

public void initialize(ProviderMBean mbean, SecurityServices services)

{

System.out.println(“PingAccessIdentityAsserterProviderImpl.initialize”);

PingAccessIdentityAsserterMBean myMBean = (PingAccessIdentityAsserterMBean)mbean;

description                         = myMBean.getDescription() + “\n” + myMBean.getVersion();

}

 

/**

* Get the PingAccess identity asserter’s description.

*

* @return A String containing a brief description of the PingAccess identity asserter.

*

* @see SecurityProvider

*/

public String getDescription()

{

return description;

}

 

/**

* Shutdown the PingAccess identity asserter.

*

* A no-op.

*

* @see SecurityProvider

*/

public void shutdown()

{

System.out.println(“PingAccessdentityAsserterProviderImpl.shutdown”);

}

 

/**

* Gets the PingAccess identity assertion provider’s identity asserter object.

*

* @return The PingAccess identity assertion provider’s IdentityAsserter object.

*

* @see AuthenticationProvider

*/

public IdentityAsserterV2 getIdentityAsserter()

{

return this;

}

 

/**

* Assert identity given a token that identifies the user.

*

* @param type A String containing the token type.  The PingAccess identity

* asserter only supports tokens of type “SM_USER” which in this example is a string.

* Also, the PingAccess identity asserter’s mbean’s “ActiveTypes” attribute

* must be set to “String” (which is done by default

* when the mbean is created).

*

* @param token An Object containing the token that identifies the user.

* The PingAccess identity asserter’s token must be an array of bytes

* containing a String of the form “username=someusername“.

*

* @param handler A ContextHandler object that can optionally

* be used to obtain additional information that may be used in

* asserting the identity.  If the caller is unable to provide additional

* information, a null value should be specified.  This sample

* ignores the handler.

*

* While, for simplicity, this sample does not validate the

* contents of the token, identity asserters typically should do

* this (to prevent someone from forging a token).  For

* example, when using Kerberos, the token may be generated

* and “signed” by a Kerberos server and the identity asserter

* hands the token back to the Kerberos server to get it

* validated.  Another example: when asserting identity from

* X509 certificates, then identity asserter should validate the

* certificate – that it hasn’t been tampered, that it’s been

* signed by a trusted CA, that it hasn’t expired or revoked, etc.

*

* @return a CallbackHandler that stores the username from the token.

* The username can only be retrieved from the callback handler by

* passing in a NameCallback.  The sample returns an instance of

* its CallbackHandler implementation (PingAccessCallbackHandlerImpl).

*

* @throws IdentityAssertionException if another token type is passed

* in or the token doesn’t have the correct form.

*/

public CallbackHandler assertIdentity(String type, Object token, ContextHandler context) throwsIdentityAssertionException

{

System.out.println(“PingAccessIdentityAsserterProviderImpl.assertIdentity”);

System.out.println(“\tType\t\t= ”  + type);

System.out.println(“\tToken\t\t= ” + token);

 

 

String PING_ACCESS_OIM_COOKIE_NAME = “PA.OIMOracleIdentityManagement”;

 

String userName = null;

 

Object requestValue = context.getValue(“com.bea.contextelement.servlet.HttpServletRequest”);

if ((requestValue == null) || (!(requestValue instanceof HttpServletRequest))) {

// logger.log(Level.FINEST,”do nothing”);

} else {

 

HttpServletRequest request = (HttpServletRequest) requestValue;

// Get Ping Access Cookie from this Request

Cookie [] cookies = request.getCookies();

 

for (int i = 0; i< cookies.length; i++) {

Cookie cookie = (Cookie) cookies[i];

System.out.println(“Cookie Name: ” + cookie.getName() + ” Cookie Value: “+ cookie.getValue());

if (PING_ACCESS_OIM_COOKIE_NAME.equals(cookie.getName())) {

 

String paCookie = cookie.getValue();

// Custom Utility to retrieve UserID from encrypted JWT token

userName = new OAuth2Utils().getUserIDFromJWT(paCookie, “Segment2”, “sub”);

System.out.println(“Username => ” + userName);

break;

 

}

}

 

}

 

System.out.println(“\tuserName\t= ” + userName);

 

// store it in a callback handler that authenticators can use

// to retrieve the username.

 

return new PingAccessCallbackHandlerImpl(userName);

}

 

/**

* Return how to call the login module to perform authentication.

*

* @return A null AppConfigurationEntry since the PingAccess identity

* asserter is not an authenticator (thus doesn’t have a login module).

*/

public AppConfigurationEntry getLoginModuleConfiguration()

{

return null;

}

 

/**

* Return how to call the login module to complete identity

* assertion (where the identity asserter finds the user name

* and the authenticator puts the user and its groups into the

* subject).

*

* @return A null AppConfigurationEntry since the PingAccess identity

* asserter is not an authenticator (thus doesn’t have a login module).

*/

public AppConfigurationEntry getAssertionModuleConfiguration()

{

return null;

}

 

/**

* Return an object that can validate principals (eg. users

* and groups) that this provider puts into the subject.

*

* @return A null PrincipalValidator since the PingAccess identity asserter

* is not an authenticator (thus doesn’t put principals into the subject).

*/

public PrincipalValidator getPrincipalValidator()

{

return null;

}

}

The following is an example of how building a custom PingAccessCallBackHandler:

import javax.security.auth.callback.Callback;

import javax.security.auth.callback.NameCallback;

import javax.security.auth.callback.CallbackHandler;

import javax.security.auth.callback.UnsupportedCallbackException;

 

/**

* The PingAccess identity asserter’s implementation of the

* CallbackHandler interface.

*

* It is used to make the name of the user from the identity

* assertion token available to the authenticators (who, in

* turn, will populate the subject with the user and the user’s

* groups).

*

* This class is internal to the PingAccess identity asserter.

* It is not a public class.

*

* @author Copyright (c) 2002 by BEA Systems. All Rights Reserved.

*/

/*package*/ class PingAccessCallbackHandlerImpl implements CallbackHandler

{

private String userName; // the name of the user from the identity assertion token

 

/**

* Create a callback handler that stores the user name.

*

* @param user A String containing the name of the user

* from the identity assertion token

*/

PingAccessCallbackHandlerImpl(String user)

{

userName = user;

}

 

/**

* Used by the authenticators’ login modules to get the user name

* that the identity asserter extracted from the identity assertion token.

* This name can only be retrieved via a NameCallback.

*

* @param callbacks An array of Callback objects indicating what data

* the login module is trying to extract from this callback handler.

* It must only contain NameCallbacks.

*

* @exception UnsupportedCallbackException thrown if any of the callbacks

* aren’t NameCallbacks.

*

* @see CallbackHandler

*/

public void handle(Callback[] callbacks) throws UnsupportedCallbackException

{

// loop over the callbacks

for (int i = 0; i < callbacks.length; i++) {

 

Callback callback = callbacks[i];

 

// we only handle NameCallbacks

if (!(callback instanceof NameCallback)) {

throw new UnsupportedCallbackException(callback, “Unrecognized Callback”);

}

 

// send the user name to the name callback:

NameCallback nameCallback = (NameCallback)callback;

nameCallback.setName(userName);

}

}

}

The following is an example of the PingAccessIdentityAsserter.xml:

<?xml version=”1.0″ ?>

<!DOCTYPE MBeanType SYSTEM “commo.dtd”>

 

<!– MBean Definition File (MDF) for the PingAccess Identity Asserter.

 

Copyright (c) 2002 by BEA Systems, Inc.  All Rights Reserved.

–>

<!– Declare your mbean.

Since it is for an identity asserter, it must extend the

     weblogic.management.security.authentication.IdentityAsserter mbean.

The Name and DisplayName must be the same. They specify the name that will appear on the console for this provider.

Set the PeristPolicy to “OnUpdate” so that if an attribute value is changed, the new value is written to disk immediately. See the “Developing Security Services” manual for more info.

Note that since this is an xml document, you can’t use double quotes directly.  Instead you need to use &quot;

Note that setting “Writeable” to “false” on an attribute makes the attribute read-only.  The default is read-write.

–>

 

<MBeanType

Name          = “PingAccessIdentityAsserter”

DisplayName   = “PingAccessIdentityAsserter”

Package       = “com.example.security.providers.identityassertion.pingaccess”

Extends       = “weblogic.management.security.authentication.IdentityAsserter”

PersistPolicy = “OnUpdate”

>

 

<!– You must set the value of the ProviderClassName attribute

(inherited from the weblogic.management.security.Provider mbean)

to the name of the java class you wrote that implements the

weblogic.security.spi.AuthenticationProvider interface.

 

You can think of the provider’s mbean as the factory

for your provider’s runtime implementation.

–>

<MBeanAttribute

Name          = “ProviderClassName”

Type          = “java.lang.String”

Writeable     = “false”

Preprocessor  = “weblogic.management.configuration.LegalHelper.checkClassName(value)”

Default       = “&quot;com.example.security.providers.identityassertion.pingaccess.PingAccessIdentityAsserterProviderImpl&quot;”

/>

 

<!– You must set the value of the Description attribute

(inherited from the weblogic.management.security.Provider mbean)

to a brief description of your provider.

It is displayed in the console.

–>

<MBeanAttribute

Name          = “Description”

Type          = “java.lang.String”

Writeable     = “false”

Default       = “&quot;WebLogic Ping Access Identity Asserter Provider&quot;”

/>

 

<!– You must set the value of the Version attribute

(inherited from the weblogic.management.security.Provider mbean)

to your provider’s version.  There is no required format.

–>

<MBeanAttribute

Name          = “Version”

Type          = “java.lang.String”

Writeable     = “false”

Default       = “&quot;1.0&quot;”

/>

 

<!– You must set the value of the SupportedTypes attribute

(inherited from the

weblogic.management.security.authentication.IdentityAsserter mbean)

to the list of token types that your identity asserter supports.

 

Whoever is initiating the identity assertion (eg. a client sending

a perimeter authentication token via an HTTP request header), must

use the same token type.

–>

<MBeanAttribute

Name         = “SupportedTypes”

Type         = “java.lang.String[]”

Writeable    = “false”

Default      = “new String[] { &quot;SM_USER&quot; }”

/>

<!– The ActiveTypes attribute (a settable attribute inherited from the

weblogic.management.security.authentication.IdentityAsserter mbean)

contains the subset of your mbean’s SupportedTypes that are active

in the realm.

Which way you should default the active types attribute depends on your token types.

The basic rule is that for any token type, there must only be one identity asserter in the realm with that token type as an active type.  In short, you can only have one identity asserter turned on for a given type.

If your token types are commonly implemented by other identity asserters (e.g. X509 certificates), then you should not set them as default active types. Otherwise, it would be very easy for an administrator to configure an invalid realm where more than one identity asserter has the same type turned on. Best practice is that all the identity asserters turn off the type by default, and then the administrator manually turns on the type in one of the identity asserters that support it. Look at the weblogic.security.spi.IdentityAsserter javadoc for some standard token types.

On the other hand, if you have a custom token type that no other identity asserter will ever implement, you may default the active types attribute to include your token type. If you do, then the administrator doesn’t have to manually turn on your token type.

Since the PingAccess identity asserter’s token type is very specific to the sample (instead of a common type like X509), turn on the token type by default.

–>

<MBeanAttribute

Name         = “ActiveTypes”

Type         = “java.lang.String[]”

Default      = “new String[] { &quot;SM_USER&quot; }”

Writeable     = “false”

/>

 

<!– Add any custom attributes for your provider here.

 

The PingAccess identity asserter does not have any custom attributes.

 

Note: custom attributes do not appear in the

console in WLS 7.0.  Use the admin command line tool

(java weblogic.Admin) to view and set their values.

 

Refer to the “Developing Security Services” manual

for more info on defining custom attributes.

–>

 

</MBeanType>

 

Conclusion

Integrating PingFederate/PingAccess and Oracle Identity Governance for Single Sign-On (SSO) provides a seamless user experience while ensuring robust security. This step-by-step guide has detailed the important coding instructions to successfully assert an authenticated user from PingIdentity into an Oracle IAM platform. By following these steps, organizations can enhance their security postures, streamline user management, and foster a more efficient and secure access management system.

Author, Lahai Karmo, IDMWORKS, Senior IAM Architect