Dynamic Profiles in OpenAM 13

Screenshot+2016-06-30+17.28.13-06faf5f28d324804d2789c205c946aaafe02242e

Dynamic Profiles in OpenAM 13

I recently had cause to play with ‘Dynamic Profiles’ in OpenAMv13.

If you don’t know then Dynamic Profile is a realm based configuration setting that can dynamically create a user profile in the configured DataStore.  This can be useful in many circumstances where the authentication of a user takes place in a service other than the DataStore.  Examples of this include using OpenAM as Social Media/Oauth2 client (allowing users to sign in with Facebook), and SAML Service Provider (e.g. allowing users to sign in with Federated credentials).  Having authenticated the credentials it now might be useful to maintain a profile of that user that is used throughout their interactions with the services protected by OpenAM.

The specific scenario that triggered this investigation (and therefore this blog post!) was one where the user is authenticated by credentials in an Active Directory, but the user profile (DataStore) was a separate OpenDJ instance.

Now before I go any further it is entirely possible to make the AD your DataStore making things simple.  However, there are many occasions where the schema changes needed in a directory to provide the full range of DataStore capabilities are simply not allowed to be applied to an Active Directory due to business or security policy.  Therefore it is necessary to configure a separate DataStore using, say, OpenDJ as well as allow users to authenticate against AD with their AD credentials.

By default OpenAM configures a realm to ‘Require’ a profile entry in the DataStore for an authenticated user.  Therefore a user profile has to exist in the DataStore in order for authentication to complete.

Now you could provision a set of user profiles into the DataStore using something like OpenIDM to ensure the required profile exists.  Or you can set ‘Dynamic’ profiles in OpenAM.  This causes OpenAM to dynamically create a profile in the configured DataStore if one does not exist.
So, if we’re ‘dynamically’ creating profiles, what data does OpenAM use to populate the DataStore?   Good question…and one that this post is designed to answer!

First, the basics…

  • Authentication Chain
    As described we want the user to authenticate using their AD credentials.  Therefore we need to define the Authentication Chain to contain an LDAP Authentication Module (or the AD module if you prefer).  Let’s assume you can get this working (you may choose to set profile as ‘Ignored’ whilst you set this up so that authentication can complete without the need for a profile at all).

 

  • DataStore
    We’ll be using OpenDJ as the DataStore.  Let’s assume you can setup OpenDJ and configure it as a DataStore.  The OpenAM documentation on Backstage provides guidance on this.

Ok, so now we need to tie these together.  The general idea is that a user logs in with their AD credentials using the Authentication Chain/Module.  OpenAM checks to see if there is an associated user profile record in the DataStore and creates it if not.

Let’s first of all consider the Authentication Module.  There are two key properties here:

  1. Attribute Used to Retrieve User Profile
  2. User Creation Attributes

 

 

Attribute Used to Retrieve User Profile

In my experience the description and helptext for this is not entirely accurate.  In the scenario I’m describing, OpenAM will retrieve the value of this attribute from the AD/LDAP.  It will then be placed into memory for use later on.  Let’s call this ‘AttributeX’.  Typically the attribute you want to retrieve is the unique identifier for the user, so might be sAMAccountName or uid, for example.  We’ll use the value in ‘AttributeX’ later when we search for and find the user profile in the DataStore.

User Creation Attributes

This is a list of attribute values to be retrieved from the AD/LDAP authentication source that we wish to pass through to the user profile creation step.
You can specify these attributes either as singletons such as ‘sn’ or ‘mail’.  Or as pipe separated maps such as phoneNum|telephoneNumber.
Again the description of the pipe mapping syntax is confusing.
In actual fact it should be read as:
OpenAM property|AD/LDAP property
i.e. the setting of phoneNum|telephoneNumber would take the value of telephoneNumber from AD/LDAP and store it in an OpenAM property called phoneNum.  We can then use the OpenAM property later when we create the record in the DataStore.
Note that the singleton syntax such as ‘sn’ will essentially be interpreted as ‘sn|sn’.
Also note that the list that appears here is explicit.  If a property is not in this list then the DataStore will not be able to access it during creation.

Ok, so now we know how to extract information from the AD/LDAP that we authenticate against and store it in OpenAM properties.  Now let’s look at the DataStore configuration.

There are a couple of things to look at here, but they’re mostly in the ‘User Configuration’ section:
1. LDAP Users Search Attribute
2. LDAP People Container Naming Attribute
3. LDAP People Container Value
4. Create User Attribute Mapping
and, in the Authentication Configuration section,
5. Authentication Naming Attribute

LDAP Users Search Attribute

This is the attribute that will contain the value of ‘AttributeX’ i.e. the unique key for the user.  Typically in OpenDJ this is often ‘uid’, but could be something else depending on your configuration.  For example I want a DN of a person record to be something like:
cn=<userid>,cn=users,dc=example,dc=com
whereas the default might be:
uid=<userid>,ou=people,dc=example,dc=com
Therefore I need the search attribute to be ‘cn’ not ‘uid’.

LDAP People Container Naming Attribute

As you can see from my desired DN, the container for the ‘people’ records is ‘users’ named by the ‘cn’ property.  Hence the value I specify here is ‘cn’.  The default (for OpenDJ) is ‘ou’ here.

LDAP People Container Value

Again, from the desired DN you can see that I needs to specify ‘users’ here, whereas the the default is ‘people’.

These settings are used to both find a user as well as set the DN when the user is dynamically created.
So, with my settings a user will be created thus:
cn=<userid>,cn=users,dc=example,dc=com

(The dc=example,dc=com section is defined as the ‘LDAP Organization DN’ elsewhere in the DataStore config but you should already have that setup correctly!)

Create User Attribute Mapping

Now this is the interesting bit!  This is where the values we retrieved from the AD/LDAP Authentication module and placed in to OpenAM properties can be mapped to attributes in the DataStore.
By default the list contains two singletons: cn and sn

But the helptext says you must specify the list as ‘TargetAttributeName=SourceAttributeName’ which the defaults don’t follow.
Remember that ‘AttributeX’ we collected?  Well any singleton attribute in this list will take the value of AttributeX…if it was not explicitly defined in the Authentication Module ‘User Creation Attributes’ map.
i.e. if User Creation Attributes included ‘sn’ then the value of that would be used for the ‘sn’ value here.  If there was no mapping then ‘sn’ here would take the value of AttributeX.

This particular nuance allows you to configure a setting to ensure that attributes that are defined as ‘mandatory’ in the DataStore to always have an attribute value.  This avoids record creation errors due to missing data constraints defined in the DataStore.

Now, what happens if we use the ‘TargetAttributeName=SourceAttributeName’ format?  Well, in this case ‘TargetAttributeName’ refers to the name of the attribute in the DataStore, whereas ‘SourceAttributeName’ refers to the OpenAM property as specified in Create User Attribute Mapping of the Authentication Module.

Oh, and there’s one extra consideration here…
If the OpenAM property name (as defined in Create User Attribute Mapping) exactly matches the name of a DataStore property then you don’t need to specify it at all in this list!!

For example, say you want to take the value of ‘givenName’ from the AD/LDAP and place it in the attribute called givenName in the DataStore then all you need to do is specify ‘givenName’ in the Authentication Module Create User Attribute Mapping settings.  There is no need to explicitly define it in this list.

However, if you do define it in this list then you must define it as givenName=givenName.
If you were to just specify givenName then it’s value will be that of the mysterious ‘AttributeX’.

and finally…

Authentication Naming Attribute

For my configuration I set this to ‘cn’.  This is a bit of conjecture, but I believe this configuration is used when the DataStore Authentication Module is used.  The DataStore Authn Module will take the username entered by the user on the login page and try to find a user record where the ‘cn’ equals it.
Now in my scenario this should never happen…I never intend the DataStore to be used as an authentication source…but, as they say, YMMV.

 

This blog post was first published @ yaunap.blogspot.no, included here with permission from the author.

2 Comments
  1. Profile photo of rob_swann
    rob_swann 8 months ago

    Hi Andrew,

    This is excellent article to properly understand the confusing attributes’ relationship. I really tried hard in beginning of this year to demystify myself.
    I have one thing to draw your attention and request to clarify.

    As you explained above in “Create User Attribute Mapping”, you said
    “If the OpenAM property name (as defined in Create User Attribute Mapping) exactly matches the name of a DataStore property then you don’t need to specify it at all in this list!!”
    But
    If you do define it in this list then you must define it as givenName=givenName.
    If you were to just specify givenName then it’s value will be that of the mysterious ‘AttributeX’.

    The last line above is confusing me as it is conflicting with
    “i.e. if User Creation Attributes included ‘sn’ then the value of that would be used for the ‘sn’ value here.
    If there was no mapping then ‘sn’ here would take the value of AttributeX.”

    But in this case there is a mapping in “User Creation Attributes” in auth. module.
    So why would AttributeX be applied ?

    Appreciate your help !!!

    Thanks.

  2. Profile photo of ytheva
    ytheva 4 months ago

    Hi,

    I am currently facing some issues with my SAML configuration and I would appreciate any kind of support or hints.

    Following starting point:

    I configured OpenAM (13.5.0) as a SP and an ADFS3 as IdP.

    My aim is that the user authenticates with SAML and get stored to my database (OpenDJ) after successful authentication.

    Actually my situation is that the SP initiates the authentication request and a response is coming back from the IdP and I am facing with a “HTTP Status 500 – Single Sign On failed”

    Following configurations are done:

    AuthenticationSettings  User Profile: Dynamic  Alias Attribute Search name: UID

    Following were created:

    Module 1 – LDAP with an OpenDJ

    Module 2 – SAML

    Chain 1 contains an SAML module and the “Linking authentication chain” is a chain 2 and Chain 2 contains the LDAPmodule

    Following “NameID Format” were agreed by the ADFS Team  „urn:oasis:names:tc:SAML:2.0:nameid-format:persistent“

    In the Federation Tab I configured the Entities as well as a CoT

    Following setting were also done: Global  Configure  Authentication  Realm Authentication Defaults “Core Attributes”  User Profile: Dynamic: Alias Attribute Search name: UID

    Test 1: I invoked the authentication chain from a client pc and I got a HTTP Status 500 – Single Sign On failed.

    I placed an extract from the log at the end…

    Test 2: If I create the AD account manually – SSO succeeds.

    Test 3: If I set up the user profile to „ignored“ – SSO succeeds

    Anyway “Test 1” is desired where the users are created on the first time to the database dynamically

    The from the AD contains following information: NameID as well as following AttributeStatements: „pnr“, „givenname“, „name“, „emailaddress“

    I mapped these in the LDAP module as follows: uid|NameID sn|name mail|mailadress givenName|givenname employeeNumber|pnr

    Where else do I have to map the attributes and how? Did I missed any settings?

    I appreciate any kind of support and or hints.

    Best regards,
    Yathu

    Federation Log extract:

    libSAML2:11/24/2016 06:33:02:747 PM MEZ: Thread[http-nio-80-exec-6,5,main]: TransactionId[789fc740-325b-4bc2-805c-0027e0dad637-13211]
    ERROR: spAssertionConsumer.jsp: SSO failed.
    com.sun.identity.saml2.common.SAML2Exception: Login failed with unknown reason.
    at com.sun.identity.saml2.profile.SPACSUtils.processResponse(SPACSUtils.java:1279)
    at org.apache.jsp.saml2.jsp.spAssertionConsumer_jsp._jspService(spAssertionConsumer_jsp.java:337)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443)
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.forgerock.openam.validation.ResponseValidationFilter.doFilter(ResponseValidationFilter.java:44)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at com.sun.identity.setup.AMSetupFilter.doFilter(AMSetupFilter.java:111)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.forgerock.openam.audit.context.AuditContextFilter.doFilter(AuditContextFilter.java:51)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:784)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:802)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1410)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)
    Caused by: com.sun.identity.plugin.session.SessionException: Login failed with unknown reason.
    at com.sun.identity.plugin.session.impl.FMSessionProvider.createSession(FMSessionProvider.java:284)
    at com.sun.identity.saml2.profile.SPACSUtils.processResponse(SPACSUtils.java:1258)
    … 38 more

    libSAML:11/24/2016 06:33:02:747 PM MEZ: Thread[http-nio-80-exec-6,5,main]: TransactionId[789fc740-325b-4bc2-805c-0027e0dad637-13211]
    SAMLUtils.sendError: error page/saml2/jsp/saml2error.jsp

    Best regards,
    Yathursan Theva

Leave a reply

©2017 ForgeRock - we provide an identity and access platform to secure every online relationship for the enterprise market, educational sector and even entire countries. Click to view our privacy policy and terms of use.

Log in with your username and password

Lost your password?

Forgot your details?