Nowadays, identity management is a fundamental concern of most digital businesses. From startups to enterprises, there is a prevalent need to create systems that are at the same time resilient to malicious attacks and friendly for users providing a seamless experience with a product. If it weren’t for modern identity management solutions, filling those requirements would be a daunting task requiring many resources.
With the changing landscape of identity management, at LeanCode we faced the challenge of selecting a new identity management solution that would be our default for the coming years. We want to share with you the whole journey, starting with the most basic one - an introduction to all the aspects of identity management.
Then we will walk you through the selection process and tell you about the options we considered and what we finally chose. Finally, we’ll show you in detail what the migration process looked like.
But what is identity management? In short, it can be defined as a set of tools and frameworks which ensure that specific resources present in the app can only be accessed by eligible clients. A client can be a user or an app requesting access to a resource. A client can be a user or an app requesting access to a resource. Moreover, identity management also includes handling user profiles, such as creating, deleting, and editing them.
There are two main parts of identity management which are often confused with each other. These are authentication (abbreviated as AuthN) and authorization (AuthZ). The difference between them lies in what they are responsible for. Authentication’s concern is whether a client is, in fact, the one who they claim to be. On the other hand, authorization checks if a given user can be granted access to a specific resource.
For example, imagine that you are driving a truck down the highway and getting pulled over by a police officer. They ask for your driver’s license. They first check your name, and if the photograph on the document matches the person behind the wheel, they perform authentication. Then, they take a closer look at your driver’s license to determine if you are allowed to drive such a big vehicle as a truck, thus performing authorization.
It is worth noting that identity data is used in different parts of an application. In virtually every system, there are some actions that are not related to identity management directly but have to make use of identity data nevertheless. For example, imagine an app that sends emails to users, informing them about important events happening in the system. Even though the act of sending an email is not a part of identity management, the email uses information from the user's profile, such as their name or the email address to which the message is sent. In the same way, an owner of an online shop should have the possibility to view a pending order with user data, like the address to which a package should be sent or their telephone number.
As you can imagine, managing identity with all the requirements that different apps may pose is not an easy task. A myriad of flows is possible for authenticating and authorizing users in apps. These include having the user data stored in the database, exchanging tokens in a secure way, authenticating via email address and password, or via an identity provider, etc.
Needless to say that a compiled specification of OAuth2.0, an industry-standard authorization framework, stands at a whopping 400 pages. Because it is easy to create a critical vulnerability when implementing security features by oneself, one should follow battle-tested specifications and proceed according to an old adage:
“Don’t implement security by yourself”
Identity is complex, but there are standards and standard approaches. When talking about identity and access management, we need to be aware of them and take them into account.
A token-based authentication is an approach that standardizes what you use to prove that you are authenticated (in a secure manner). Instead of having “something”, you know that after successful authentication, you will get a token (that might be a black box to you, but you might be able to decode it in some circumstances) that you then use to prove that you are really you without re-doing authentication.
Each and every server that accepts the provided method of authentication is able to cryptographically verify the token (by either checking some kind of signature or delegating the check to the issuer). Tokens also expire. That means that you can’t use them forever, and you need to re-authenticate or refresh (with refresh tokens) to get fresh ones. This is necessary to prevent a wide variety of attacks on authentication systems.
Other important standards that are strictly connected with this topic are OAuth2 and OpenID Connect (OIDC for short). The first one is more basic. It talks about authorization only (leaving authentication for the implementer) and is more of a framework than a ready-to-use solution. It standardizes how one needs to interact with others to fully and securely do authorization in a distributed system. OIDC adds standard identity verification, i.e. allows to say that someone with access to the system is named John, has this email address, and works for LeanCode.
OAuth2 & OIDC are also foundations for so-called social logins. These protocols allow you to sign in with Facebook, Google, and Apple and do so in the same format without many changes in the code.
One cannot make a secure system without multiple verification factors. It is said that we can be identified by what we know (passwords), what we have (hardware keys, authenticator apps), or what we are (biometrics, i.e. fingerprints & face scans). Basing the security of a system on a single factor is not enough - passwords get leaked, and fingerprints get stolen (less likely). If we have important actions in the system, we should verify at least two of them.
Currently, the main combo for the factors is password + one-time codes (authenticator apps) and biometrics (+ OTPs for the most important actions, e.g. when doing bank transfers), but both hardware keys and other forms of “being” are gaining traction.
Nowadays, virtually every device we use has some kind of biometric reader, be it a fingerprint scanner or FaceID. It is more and more common for apps and websites alike to allow signing with it. Unfortunately, it is a complex topic. Up until recently, there were no standard ways to do it, and everyone developed their own way of doing biometric auth.
Fortunately, since last year, passkeys are gaining popularity (because both Android and iOS started supporting them). They are a type of passwordless authentication that standardize biometric and security keys, allowing you to sign in with just your fingerprint OR secure software key (like YubiKey) to mobile apps and websites. They are being developed along the WebAuthn standard by FIDO Alliance and are being supported by more and more vendors, making very secure biometric auth a standard.
There exist two main characteristics when it comes to classifying an identity management solution in an application:
You can go and develop the full solution yourself, but you can also take a turnkey service and just use it. Which road is better? Let’s find out!
The first dimension is whether the identity management solution is an internal or external one. What is meant by that is whether the identity management is contained in the same process as the application itself. The alternative is to have identity management running separately, decoupled from the main application. Let’s dig deeper into this topic.
The main advantage of having identity management as a part of an application itself is the simplicity with which the identity data can be accessed throughout the code. Usually, it is a case of executing a query in the app’s database, which fetches all the necessary information about a specific user. Moreover, because both identity and application data are stored in the same place, it is possible to guarantee consistency in the system when updating identity information.
Additionally, when using internal identity management, programmers get full control of the identity data of their application. Generally, a database for user identity information must be configured manually by a developer for the solution to work properly. This can be both an advantage and a disadvantage. Certainly, configuring a database or a cache is somewhat of an overhead and requires additional work from developers.
On the other hand, having full control over users’ data can be invaluable in some circumstances. Some regulations may be required to be followed by law or specific business requirements present. For example, GDPR forbids storing European Union citizens’ data outside its territory. Having full control over the identity data makes it possible to adhere to such laws.
However, as was mentioned, when choosing an internal identity management solution, there is a need to provide the configuration as well as hosting for the created solution. With the advent of cloud technology and an abundance of storage and hosting solutions, this shouldn’t be much of an obstacle. Still, it is worth keeping in mind that it is an additional responsibility to be taken care of nonetheless. Moreover, providing the necessary infrastructure comes with the costs that need to be covered. These grow together with the scaling of the application and, in some cases, may become significant quickly.
Finally, an internal approach for identity management can only be applied to smaller projects where a backend part of the system consists of a single component. In more extensive, more complex applications, especially microservice-based ones, having identity management implemented as an internal one in any single component would automatically make it external for every other service anyways.
For example, one common solution in the .NET world to identity management is ASP.NET Core Identity (plus IdentityServer4 as an OAuth2 provider). It gives you all the necessary building blocks that you can use to manage user profiles, do authentication, and implement some flows. It defines a very basic user profile (it is basically a username and password) that you can extend but does so in a way that integrates well with other parts of the app. From the application perspective, it is “just” another table (or collection or whatever the data store has) in the database.
Recent years saw a rise in the popularity of external identity management platforms such as Auth0, Firebase Auth, Supabase Auth, and ORY Kratos. These provide a solid alternative to internal identity management solutions discussed before but come with their strings attached.
Firstly, compared to internal identity management, external one requires a paradigm shift when accessing identity data. Using internal identity management, all the data related to users is usually present in the same database that the rest of the system uses. Because of this, all the information necessary can be easily accessed, most often by executing a simple join query on the user ID in the database.
When using an external identity management solution, the only way to access users' identity data is to request it through an API. This, of course, limits what is possible when it comes to fetching necessary data but also introduces a risk that the API might fail to provide the requested information due to a technical failure. Developers should program their applications to expect and handle such situations accordingly. In addition, because the identity and application data are stored in separate places, ensuring consistency across the whole system may require more effort and caution.
However, having a separate process responsible for identity management also has its advantages. Especially in bigger, multi-component systems, it is valuable to have identity management encapsulated in a separate service. It creates a segregation of responsibility in the system and allows for the decoupling of the identity management component from the rest. Moreover, it creates a single source of truth regarding identity data, making it easier to manage. Finally, having identity management separate from the rest of the system introduces more freedom for the developers, who can choose technologies for the application and identity management independently from each other.
The other dimension that we need to consider is customizability. In general, having a more customizable solution would be a better choice. Unfortunately, we are talking about a security-critical part of the application, so there are many more nuances that we need to consider.
By definition, the more custom a solution is, the bigger the space of scenarios it can handle. This is, of course, a great advantage allowing for the implementation of non-standard ideas which would not be possible otherwise.
However, using highly custom solutions has its drawbacks. Creating a custom solution is always an additional challenge for the developers. It comes with additional costs and risks of introducing bugs, errors, or in the worst-case scenario, critical security vulnerabilities to the application. Moreover, an infrastructure has to be managed internally and scaled for such custom solutions, creating additional costs.
In the .NET environment, the go-to solution for implementing identity management in a highly custom manner has been to use an IdentityServer, a free and open source OpenID Connect and OAuth2.0 framework with official Microsoft support. However, October 2020 marked an important turning point in the trajectory of IdentityServer when the library's authors decided to start a proper company called Duende Software and made IdentityServer chargeable for commercial use.
Both ASP.NET Core Identity and IdentityServer follow a library approach, and thanks to it, the solution allows for a lot of customization and extensibility. As can be read on IdentityServer’s website: “In short, if what you want to achieve is doable in .NET Core, it is doable with IdentityServer”. It may be a major advantage if the business's identity management scenarios are not standard.
The alternative is to use identity management solutions that provide ready-to-use application components and flow. Examples of such products include Auth0 and ORY Kratos.
Using an out-of-the-box identity management system provides a wide array of authentication and authorization flows that can be easily integrated into the application. This, of course, means that there is less work to be done by a developer to set up a working application connected to the identity management solution of choice. Indeed, quickstart tutorials on the official Auth0 site are concise and show how to integrate it in a wide assortment of technologies, both client and server side.
However, such identity management solutions may hinder the customizability of authentication and authorization flows present in an application. Still, the vast majority of businesses don't require a non-standard approach to identity management or customization of authentication and authorization flows. Because of this, sacrificing some customization options for improved developer experience and faster time to market is most often a reasonable trade-off.
Moreover, such identity management solutions generally cater their offer to small and enterprise-level businesses. Because of that, they are well-versed in all the concerns that such companies face regarding identity management. This includes security standards as well as following legal regulations. Also, authentication and authorization flows supported by them usually cover the greatest part of the most commonly used ones. Hence, the chances that a case they cannot handle will appear are slim.
Also, in most cases, they can take some weight off developers’ minds when it comes to providing the hosting and storage for an identity management server. Auth0, for example, comes with its platform, which handles both hosting and storage, as well as provides some additional functionality, such as a graphical admin panel for managing existing users.
The final thing worth mentioning is pricing. Regarding cloud-based identity management platforms, the monthly cost of using them can get quite steep quickly. It usually depends on the use scenario (B2C, B2B, etc.) and the amount of daily or monthly active users. Because of this, it is advisable to analyze the current as well as prospective customer base to ensure the costs will be kept to an acceptable level and choose the identity management solution accordingly. However, some solutions, such as ORY Kratos, allow for both using their platform in the cloud and self-hosting, making it possible to make a choice on a case-by-case basis.
When starting to develop a new application, there are two questions to be asked:
To answer the first one, the main thing to consider is an application's size and level of complexity. Generally, the smaller and simpler the app, the more likely it is to benefit from an internal identity management solution. In contrast, bigger and more complex apps should lean towards using an external one
Smaller apps usually have fewer components that need to communicate with each other, and as such, managing all the flows and permissions alongside the application may be a feasible task. This may not hold true for big, multi-component applications with microservices architecture where all the different processes can communicate with each other in an intricate network of dependencies where most of the communication includes some form of authentication and authorization of the client making the request. In such a scenario, using an external solution can be a choice that centralizes identity management and saves developers a lot of pain down the road.
When it comes to deciding how much customizability will be needed when it comes to authentication and authorization, the answer usually is ‘not that much’. For most businesses, choosing standard authentication and authorization flows will be more than enough, allowing them to save costs, which will certainly arise when developing custom solutions. Moreover, using standard authentication and authorization flows is a good practice from the user experience perspective. A user will have a much smoother experience with a web application if authentication and authorization work in a way they are familiar with.
A question may arise, what if one decides to move from an internal to an external approach in the future? How to make the change possible and as manageable as possible? Usually, the change happens from an internal identity management system to an external one as the app grows, and managing the identity together with the application in a monolithic manner becomes more and more burdensome.
Because of this, it is best to assume, right from the start, that the application will migrate to an external model sometime in the future and develop the app accordingly. An internal solution can always be treated as if it were an external one, but the opposite is rarely, if ever, true. Moreover, external identity management platforms often provide a convenient way of importing identity data from other systems, making the transition easier.
We at LeanCode had to answer that question - which one to choose? There is no good or bad answer; all the approaches have advantages and disadvantages. Some solutions are better when you have very simple apps, and some are more suited for big and complex applications. Others shine when you need fully customizable solutions. We are somewhere in the middle - we both need to have some customizability, but the solution needs to be fully secure and ready for rapidly growing systems. Selecting the best approach for us is a much longer process than a single paragraph, so stay tuned for the next article.
In summary, identity management is an important functionality of virtually every application nowadays. Implementing security features from scratch poses a major risk of introducing critical security vulnerabilities; using ready-made solutions may be the only feasible alternative. Unfortunately, they might lack the customizations necessary to make them work for your project. At LeanCode, we needed to select our default approach to identity management, but a single article is not enough to describe the full process and all the necessary steps.
So stay tuned for Part 2, where we will describe how we selected the solution that is suitable for us, and Part 3, which will show you how we can migrate from an internal, fully custom solution to an off-the-shelf one. Along the way, we will look at the most popular solutions in this space - no worries, we neither miss Firebase Auth nor Supabase here. :)
5.00 / 5 Based on 10 reviews