Sunday, May 19, 2013

Creating a Custom Authentication Plugin


Creating a Custom Authentication Plugin in OAM 11gR1
The oracle documentation is not very clear on how to create a sample OAM plugin and it takes a while to create and get one working. Recently I had to work on creating a custom cookie for a legacy web application to achieve single sign on between EPP applications protected by OAM and legacy web portal. Below are the brief steps on how to create one. The code was created using eclipse. Below are the steps on how to create a sample OAM plugin.
1)      Create a Sample Java Project. Say SampleOAMPlugin. Important: Please note that the name of java project, java class and the Meta data xml should be same.
2)      Add the following jar files to the build path of the eclipse project. Felix.jar, felix-service.jar, extensibility_lifecycle.jar, oam-plugin.jar,identitystore.jar,identity provider.jar and utilities. .jar. These jar files will be found in a tmp folder under Domain home . $DOMAIN_HOME/servers/$ADMIN_SERVER_NAME/tmp/_WL_user/oam_admin_11.1.1.3.0/XXXXXX/APP-INF/lib/oam-plugin.jar
3)      Under the src folder in eclipse create a folder called META_INF and inside it create a file called MANIFEST.MF
4)      Under the src folder create a xml file called SampleOAMPlugin.xml.
5)      If you have any third party library dependent jar files create a folder say lib under the project at the same level as the src folder. The jar file which will be created will have only these jar files and not the ones in the step 2.
6)      Create a java package say sample and create a java class SampleOAMPlugin , it has to be same name as of the project.
7)      The java class should extend AbstractAuthenticationPlugIn found in the package oracle.security.am.plugin.authn.So the java project structure should be like this:-
8)      Out of the several inherited methods in the SampleOAMPlugin class we need to implement the the process method .public ExecutionStatus process(AuthenticationContext context)throws AuthenticationException method.
9)      To extract the username and password entered on the custom login page use the below methods. The below methods will only work when the username and password fields defined on the login form are username and password.
CredentialParam credentialParam = context.getCredential().getParam(PluginConstants.KEY_USERNAME);
 String userName = (String)credentialParam.getValue();
credentialParam = context.getCredential().getParam(PluginConstants.PASSWORD); String password = (String)credentialParam.getValue();
10)   If you need any values which have to be read at runtime(like a properties a file in java), like let’s say domain name for the cookie, or the identity store against which we may want to authenticate the users  you can define that in the meta data xml created in step 4 . To read those values use below statements.. for e.g  if KEY_IDENTITY_STORE_REF is a field defined in meta data xml file.
String stepName = context getStringAttribute(PluginConstants.KEY_STEP_NAME);
String identityStoreRef = PlugInUtil.getFlowParam(stepName,"KEY_IDENTITY_STORE_REF", context);
11)   For authenticating the users use the below statements. Though you may not need these if you are authenticating against an OID. Oracle has two out of box plugins for identifying and authenticating the users. UserIdentificationPlugin and UserAuthenticationPlugin.
 UserIdentityProvider provider = UserIdentityProviderFactory.getProvider(identityStoreRef);
boolean isAuthenticated = provider.authenticateUser(userName, password);
12)   For looking up some attribute from user identity store like OID, for example email you can use the below methods. String[] userAttributeName = {"mail" };
 AuthnUser userauth = new AuthnUser();
 userauth.setUserName(userName);
List<String> attributeNames = Arrays.asList(userAttributeName);
Map<String, String> resultMap = provider.getUserAttributes(userauth, attributeNames);
String resultAttributeValue = resultMap.get(userAttributeName[0]);
 String emailId = resultAttributeValue;
13)   If you are using this plugin as a for authentication then, you have to return some mandatory responses in the plugin response and also set the subject if the user is authenticated, else if you are using Oracle’s plugin for authentication/identification , the following steps are not required. Create a subject as follows for the authenticated users.
Subject subject = new Subject();
if (isAuthenticated) {
subject.getPrincipals().add(new OAMUserPrincipal(userIdentity));
subject.getPrincipals().add(new OAMUserDNPrincipal(userDN));
if (guid != null)subject.getPrincipals().add(new OAMGUIDPrincipal(guid));
else subject.getPrincipals().add(new OAMGUIDPrincipal(userIdentity));
}context.setSubject(subject);
Set mandatory responses in Plugin Response.  The three responses which need to be set are KEY_RETURN_ATTRIBUTE, KEY_IDENTITY_STORE_REF, KEY_AUTHENTICATED_USER_NAME. PluginResponse rsp = new PluginResponse();
rsp.setName(PluginConstants.KEY_RETURN_ATTRIBUTE);
rsp.setType(PluginAttributeContextType.LITERAL);
rsp.setValue(provider.getReturnAttributes());//provider is the user identity provider
context.addResponse(rsp);
// 2 nd response
IDPAdmin idpAdmin = UserIdentityProviderFactory.getIDPAdmin();
String runtimeIDStore = idpAdmin.getDefaultProviderName();
rsp = new PluginResponse();
rsp.setName(PluginConstants.KEY_IDENTITY_STORE_REF);
rsp.setType(PluginAttributeContextType.LITERAL);
rsp.setValue(runtimeIDStore);
context.addResponse(rsp);
//3 rd response
UserInfo user = provider.locateUser(userName);
String userIdentity = user.getUserObject().getPrincipal().getName();
rsp = new PluginResponse();
rsp.setName(PluginConstants.KEY_AUTHENTICATED_USER_NAME);
rsp.setType(PluginAttributeContextType.LITERAL);
rsp.setValue(userIdentity);
context.addResponse(rsp);
14)   If we need set the custom cookie we need to create class in the package which should extend oracle.security.am.plugin.GenericTransportToken and implements the getter and setter methods.
 import oracle.security.am.plugin.GenericTransportToken;
public class TokenClass implements GenericTransportToken {
/*
* This is a sample Tokenclass that creates a GenericTransportToken,
* which can be set on the transportContext as a cookie.
* It has all the cookie details:Name,Value,MaxAge, Version, Domain and secure flag.
*/
/** The token name. */
private final String m_tokenName;
/** The token version. */
private String m_tokenVersion;
...
...
//Instantiates a new TransportToken.
public TokenClass(String tokenName, String tokenValue) {
m_tokenName = tokenName;
m_tokenValue = tokenValue;
}
//Retrieve the token name
public String getTokenName() {
return m_tokenName;
}
..
..
@Override
public void setMaxAgeInSeconds(int age) {
this.m_maxAgeInSeconds = age;
}
...
....
@Override
public void setTokenVersion(String version) {
this.m_tokenVersion = version;
}
}
In the plugin class, call this class constructor and access the values using the getter and setter methods. Set the cookie using oracle.security.am.plugin.GenericTransportContext.
GenericTransportContext trContext = context.getTransportContext();
TokenClass tok = new TokenClass(cookieName, "cookieValue");
tok.setMaxAgeInSeconds(12000);
tok.setTokenDomain(“.abc.org”);
trContext.setToken(tok, false);
15)   For logging use
private final static Logger LOGGER = java.util.Logger.getLogger(SampeCookieCreationPlugin.class.getCanonicalName());
LOGGER.info(CLASS_NAME + " Entering SampleCookieCreationPlugin.process");
To view this logs in oam server diagnostic logs run the following commands using wlst. connect('weblogic','weblogic1','t3://localhost:7001')
domainRuntime()
setLogLevel(logger="oracle.oam.plugin",level="TRACE:32", persist="0", target="oam_server1")
After the above commands are run you should see the following line in logs.
[sample.SampleOAMPlugin] [tid: [ACTIVE].ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'] [userId: <anonymous>] [ecid: 0d58708dceef42e5:7313f42d:13ea0e820c9:-8000-0000000000002f7a,0] [APP: oam_server]sample.SampleOAMPlugin Entering SampleCookieCreationPlugin.process
16)   Jar file Manifest file .In the bundle class path include the current class. And dependent jar files. Make sure the Symbolic Name and bundle name are same as the java project name.
 Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-SymbolicName: SampleOAMPlugin
Bundle-Name: SampleOAMPlugin
Bundle-Version: 10
Bundle-Activator: sample.SampleOAMPlugin
Import-Package: javax.security.auth,
 javax.crypto.spec,
 javax.crypto,
 oracle.security.am.common.utilities.principal,
 oracle.security.am.engines.common.identity.provider,
 oracle.security.am.plugin,
 oracle.security.am.plugin.api,
 oracle.security.am.plugin.authn,
 oracle.security.idm,
 org.osgi.framework;version="1.3.0"
Bundle-ClassPath: .,
lib/core.jar,
lib/j2ee.jar,
 lib/redpoint-core.jar

17)   Meta data xml file. Although the oracle documents says that the interface and implementation elements are optional but it seems they are required. You will not be able to activate the oam plugin without these parameters. The Attribute Value pair provides us with an ability to define parameters whose value can be changed from OAM console. Refer Step 10 on how to read from this xml file in plugin class.
<Plugin name="SampleOAMPlugin" type="Authentication">
<author>uid=cn=orcladmin</author>
                <email>abc@abc.dev</email>
                <creationDate>09:32:20, 2010-12-02</creationDate>
                <version>10</version>
                <description>Custom Sample Auth Plugin</description>
                <interface>oracle.security.am.plugin.authn.AbstractAuthenticationPlugIn</interface>
                <implementation>sample.SampleOAMPlugin</implementation>
                <configuration>
                                <AttributeValuePair>
                                                <Attribute type="string" length="20">KEY_IDENTITY_STORE_REF</Attribute>
                                                <mandatory>true</mandatory>
                                                <instanceOverride>false</instanceOverride>
                                                <globalUIOverride>false</globalUIOverride>
                                                <value>DEVOID</value>
                </AttributeValuePair>
                                <AttributeValuePair>
                                                <Attribute type="string" length="20">CookieDomain</Attribute>
                                                <mandatory>true</mandatory>
                                                <instanceOverride>false</instanceOverride>
                                                <globalUIOverride>false</globalUIOverride>
                                                <value>abc.dev</value>
                                </AttributeValuePair>
                </configuration>
</Plugin>
18)   When exporting this jar file from eclipse  just keep in mind to use the manifest file which we created. Right click on the java project and say export jar file. Browse to our manifest file.
19)   That’s it . After this we need to upload this jar file using the OAM console. Reference http://docs.oracle.com/cd/E21764_01/doc.1111/e12491/authnapi.htm#autoId17