In OpenText Directory Services (OTDS), you have multiple ways to handle two-factor authentication (2FA) or multi-factor authentication (MFA). As a quick reminder, 2FA is simply the fact that you need to prove your identity in 2 different ways (e.g. a username/password + a One-Time-Password or a token or a biometric signature or …) while the MFA is at least 2 different ways (i.e. it includes 2FA, but you can have more than 2 factors if needed). In this blog, I will talk about a customer case that I faced recently, with mixed MFA & non-MFA.

OTDS capabilities for 2FA/MFA

OTDS itself provides some 2FA capabilities, which you can enable globally, for all Users/Groups/OU/Partitions (OU = Organizational Units), or partially for some specific Users/Groups/OU/Partitions. That would be the first option if you don’t have anything else. However, you might already have 2FA or MFA managed outside of OTDS, like within your Azure Tenant. In that case, you can delegate the 2FA or MFA to the Auth Handler. This can be done, for example, by creating an OAuth 2 / OpenID Connect handler that could be connected to an Azure Application. In this case, users accessing an OTDS-managed client (Dctm, D2, …) would be redirected to OTDS, which could (or not) redirect automatically to your external SSO provider (Azure in previous example) that could perform the 2FA / MFA.

The reason why I’m writing this blog is because there are actually some limits in the way OTDS works. It’s a bit difficult to explain, but basically it looks like OTDS was made for rather small and simple setup / organizations and might not really be adapted for a fully centralized and global identity management system. From my point of view, it seems like OTDS was designed with the expectation that a specific user would only be present in a single partition. In theory, that could indeed make sense. But what if you have a very big enterprise with hundred of thousands of real users? What if you have a huge set of users and groups for some Applications? Somehow, it feels like OTDS was designed to be deployed not as a centralized solution but just as a side-application of each and every OpenText environment. If you have 10 Documentum Repositories on sandbox, 10 for dvlp, 10 for test, 10 for integration and 10 for prod, then you can obviously deploy 10 OTDS on sandbox (1 per Repo), 10 on dvlp, etc… But that just means that much more work! Technically, it feels like it was designed for that purpose. However, for simplicity, most companies will most probably not do that and instead might deploy 1 OTDS for sandbox, 1 for dvlp, etc. Or maybe even just 1 OTDS for sandbox/dvlp, 1 OTDS for test/integration and 1 for prod. To still keep some segregation between types of environments while reducing as much as possible the overhead for the operational management.

Important note: starting with Dctm 24.4, users need to be assigned a license by OTDS, to be able to login to Dctm clients. I won’t talk about that specific aspect in this blog, but just know that what I will describe later down below might have an impact on the license topic too (because of the randomness)…

Currently, in OTDS, you define Partitions (most of the time synchronized partition(s) which imports users/groups from an AD). Then you have Resources which represent a Documentum Repository in our case, for user push to Dctm and authentication. Resources come with Access Roles, which defines which Users/Groups/OU/Partitions are going to be synchronized to the associated Resource(s). Then you have OAuth Clients for your end-user application like D2. And finally, Auth Handlers, which basically represents authentication sources for users (already generated cookie, LDAP, external SSO providers, …).

Design problems?

The problem with the current approach is when you have a lot of users/groups and a lot of resources. Let’s assume that you have 500’000 users and 50’000 groups in your AD. How would you setup OTDS for 10 different Documentum Repositories, each of these repositories with thousands of users and groups, knowing that each Repositories might share parts of the users and groups (e.g. a userA has access to 5 of the 10 Repositories). You will have to create Resources for each Repository but how will you configure the Access Roles? You will obviously NOT manually select 10’000 users inside the Access Role, and you will probably not select manually 1’000 groups either. Especially so that, if you do that manually, then when something gets added to the AD in the future, it won’t automatically be sync to the Repository unless you manually do it on OTDS too… That’s not maintainable. What other solutions do you have then? Using an OU? That’s not really usable in most cases, as all users would most probably be under some “USERS” OU (and in there, you might have region-specific branches like EU/NA/JP or other split), and you cannot do exclusions. So how do you select multiple OU but excluding some of the lower OU? Most AD weren’t setup with end-user applications in mind, so the OU definitions usually do not reflect application needs for users import/sync. Therefore, the only remaining “practical” solution is Partitions…

In turns, you will probably end-up creating multiple Partitions with the goal that 1 Partition = 1 Repository. That’s actually pretty much how the LDAP Synchronization was working before OTDS was introduced… In Documentum, you configured your dm_ldap_config object with user source, group source, user filters, group filters and an LDAP hostname/port. That’s the same thing as an OTDS partition… And another Repository could have a very different configuration altogether. Therefore, you will have 10 Partitions, named based on your Applications (like APP1, APP2, …, APP10) and 10 Resources (1 per Repository for the user push through the JMS dmotdsrest URL). If we take the case of our “userA” from before, then that means you will have, in OTDS, 5 accounts “userA” from 5 of the Partitions (since this account needs access to 5 of the 10 applications, it must be part of some of the groups that these 5 applications are importing and therefore the Partition will import these groups with our userA).

Here comes the first problem: users are used to login to Applications with their userIDs, which might be an email or the sAMAccountName for example. And usually, they don’t want to change that. So, when OTDS comes in and tells you that you will have to login with “USERID@PARTITION“, so that the account actually used can be uniquely identified by OTDS, most people will just say “f*** off”, and rightly so! It’s not exactly the same topic, but you can take a look at two previous blog posts here and there where I talked about using sAMAccountName instead of the default userPrincipalName for logins.

But that’s not all, there is a second problem the comes from the first one: how do you restrict / link which OAuth Client uses which user / which partition for the authentication? Well, you can’t… For segregation, you will most probably create 1 OAuth Client for each D2 of the 10 Repository. So APP1 = REPO1 will have an OAUTHCLIENT1 for its D2. APP2 = REPO2 will have an OAUTHCLIENT2 for its D2, etc… In the OAuth Client, you can restrict which Partition will be allowed to login, but it doesn’t control which Partition will actually be used before that. Let’s assume userA has access to APP1 through APP5 and it wants to login to APP5. The OAUTHCLIENT5 will therefore be used in this case. When the user access D2, it gets redirected to OTDS. OTDS doesn’t know which user it is, so either it displays the OTDS login page or redirects to an external Auth Handler like the Azure integration I was talking about previously. Azure will return the user “[email protected]” to OTDS. However, OTDS has 5 accounts that match that, in the APP1 to APP5 Partitions, and it will pick randomly one of them. Therefore, APP2 or APP3 might be used in this case, while the OAUTHCLIENT5 was the one that initiated the request. Which means that you cannot restrict a specific Partition for the login, because you don’t know which one might have been selected by OTDS.

MFA & non-MFA in OTDS

I haven’t talked much about the MFA & non-MFA since the beginning of this blog, but fear not, it is coming. In addition to all the complexity above, what would happen if you had different classification per Application? For example, let’s assume REPO1 is a High-Risk application that requires MFA while REPO2 is a Low-Risk application that does not require MFA. How do you manage that with OTDS?

And here comes the third problem: you can’t either. As mentioned at the beginning of this blog, OTDS only allows 2FA to be configured based on Users/Groups/OU/Partitions. You might think that this could then work if we set APP1 with 2FA and APP2 without. But as I was trying to explain on the second problem above, when you have the same accounts in multiple Partitions, you cannot control which one OTDS will use. That means that userA accessing APP2 (Low-Risk/non-MFA) might actually use the account userA from APP1, which means that user will have to enter MFA details for the non-MFA application. In this way, it’s annoying but not “critical”. However, you cannot make sure that the opposite will never happen (bypassing MFA for the High-Risk/MFA protected application). That was with the OTDS 2FA solution, but what if you are using an external Auth Handler (like Azure) to provide the MFA capability?

Well, here comes the fourth problem (which is quite similar to the second one): you cannot control which Auth Handler is being used by an OAuth Client. In above case, you would have defined 2 SSO Providers with Azure, one that can be named “SSO_MFA” (which would be MFA enabled on Azure side and force all users to provide additional authentication details) and another “SSO_NON_MFA” (which would not be MFA enabled on Azure side). Therefore, userA accessing the D2 of REPO1 (supposed to be MFA) would use the OAUTHCLIENT1. Since you have 2 SSO providers, you would probably make them both as non-default (“Active By Default: false” in the Auth Handler “Parameters” page). In this case, you would end-up on the OTDS login page and it would be up to the user to select either “SSO_MFA” or “SSO_NON_MFA“… Which means you are NOT restricting/forcing the MFA in the end… Nothing would prevent userA to always click on “SSO_NON_MFA” and it would actually work for him. If you make the “SSO_MFA” Auth Handler as default, then users will always have to provide MFA details, even if they are trying to access APP2 (which wouldn’t require that), so that’s not really a solution either.

Workaround

What to do then? Since it looks like there are no other alternatives, at that customer, we simply installed another OTDS… We are using our own containers and automation in Kubernetes Clusters, so adding a new environment wasn’t much work, but that just mean that instead of having X OTDS (like 1 for sandbox/dvlp, 1 for test/integration and 1 for prod), we had to deploy 2X OTDS – always one that will be dedicated to MFA-enabled applications and one for the rest (non-MFA applications). I asked OpenText to open an improvement/feature request (OCTIM77DS4926338), to add the capability to link an OAuth Client to Auth Handler(s). With the reasoning that when a user tries to login to their end-user applications, it’s always linked to a specific OAuth Client no? So why can’t that Application/OAuth Client specify that it needs to use (can be made optional) a specific Auth Handler? If you can select multiple, then you would allow for default SSO but still have the capability to fallback to username/password (through the LDAP credentials for example) or other solutions you might have configured. There might be some work / update to be done on the cookie management to make that possible, but I will let OT look into it. In any cases, the workaround is already in place and it works as expected for the customer, so he is happy with that.