Several years ago I was called in to help solve a technical argument with a product my company was buying. The argument was around licensing costs and the way the vendor applied licenses to people in the product’s directory, even if they never used the product. The company I worked for had a large number of people in the directory, who wouldn’t ever access the system, and therefore (in their eyes) shouldn’t need user licenses. I got pulled into the conversation as the technical expert to validate whether the company were being legitimate when they told us there were technical reasons as to why they’d have to license for every person even though they’d never use the app. To the execs in my company it sounded like a crazy and unreasonable constraint.
Since then I’ve stumbled across many different versions of this exact problem across various applications. Sometimes it’s the same licensing problem, sometimes it manifests in different ways such as requirements to register emails or create logins for people who will never use the system. Sometimes it’s even more of an edge case like people who need to be sandboxed for security reasons (have an admin account and a normal privilege user account) appearing twice in the application.
The root cause of the problem is a very simple mistake. There are two different domain concepts here. One is User, the other is a business domain concept which represents a Person in the application domain. For example an Employee (in my original case study), a Collaborator or a Customer. Yet many applications conflate the two (User vs Person) when they are actually different and distinct, although they do have a close relationship. In the case with my company, while people were an important part of the product’s domain the conflation resulted in the application treating every person as a user who required a license.
For many applications a Person is also a User (though not all of the time) and so the application represents them with one domain object. This is an easy mistake to make as Users and People share many of the same attributes such as Name or email address. Also, some People attributes may be used for authorisation, such as Department or Company. Yet their behaviours are very different. The User entity is responsible for encapsulating concerns related to sign-in and security (authn/authz) and other aspects which allow the user to manage how the application behaves for them, such as language settings or accessibility or colour customisations etc. The Person entity, on the other hand, is the actual domain concept that the application is attempting to model and will have behaviours particular to the domain (such as assigning a task or changing address), distinct from the user.
When People and Users are represented in the same domain entity other problems arise because this assumes that there is a one-to-one relationship which, in some scenarios, doesn’t always hold. Some People in the domain may never be Users - this was the case in the app with the licensing issue - whilst some People may actually have multiple User entities (something which is becoming more common as services allow you to Bring-Your-Own-Identity). And sometimes, there is more than one concept that models a Person all of which can be User’s. An example is when an application enables access to both Clients and Staff, or perhaps they are just API consumers from a partner. Increasingly Users aren’t even People at all, they could be shared logins, devices, services etc.
Keep Users out of your App
As more enterprises move to adopting Single Sign On solutions, this conflation between Users and People causes additional pain. The reason is that, in these situations, the SSO provider acts as the source of truth and holds the user directory and it also has the responsibility for user behaviours (authentication and authorization groups, provisioning and deprovisioning etc.), whilst the application still needs to model people for behaviour in its own domain. Once these concerns have been conflated, they are very difficult to tease apart and modifying an application to work with an SSO provider can be expensive and error prone. One common challenge is the user provisioning and deprovisioning cycle and how that relates to People concept. This can lead to all sorts of undesirable side effects such as loss of history when Users are deprovisioned, or the inability to work with an individual’s Person before a User is provisioned or even, in some cases, the inability to deprovision a user because the Person domain concept is still in use (a potentially dangerous security hole).
Seeing these challenges over the years has led me to the opinion that building IdPs into applications and products in an anti-pattern (this topic is worth its own post). Identity and access management (authentication and authorisation) is a very separate concern from the internal person domain. When products take on the responsibility of an IdP then this adds additional complexity because the product becomes responsible for create and manage users. Often this invokes undesired side-effects, including (for some products) increased licensing costs.
Google are a great example of a company who architected this right. Early on they extracted user concerns into a separate service known as Gaia. If you head on over to myaccount.google.com you get a good view of the many concerns there are around users and their accounts. This separation of concerns gave Google a distinct commercial advantage as they could essentially provide Gaia as a single sign-on provider for other Google services and third parties by leveraging OpenID Connect.
For those products who don’t want to go the expensive route of building your own IdP (which I strongly recommend against) when building new applications or products, start with an external IdP (prefer one of the many cloud ones). If it’s a consumer facing app leverage OpenID Connect and Bring Your Own Identity by outsourcing account management to Google or Facebook or another provider (unless you are working in a domain with specific security or privacy concerns). For other apps, consider IdPs such as Okta, PingIdentity, OneLogin or even Microsoft services if you are already on Azure or have Active Directory. This will not only make your application more secure, but it will enable you to play well with others and provide SSO for your customers when they demand it.