The Developer's Guide to User Management
Developer resource for modern day user management, including 101 topics like SSO and MFA as well as more advanced concepts like identity linking, email verification, and JIT provisioning.
Building an enterprise-ready, resilient B2B authentication system is one of the harder, more complex tasks developers are facing these days. Today, even mid-large size startups are demanding security features that used to be the purview of the Fortune 500, especially SSO. IT admin requirements, org auth policies, invitations flows, and many other sub-tasks will likely haunt your dreams.
But fear not: our guide will walk you through much of what you need to consider, and how to go about implementing it.
What is User Management?
User management is the process of managing user identities, access privileges, and authentication within a system or organization. It involves creating, updating, and deleting user accounts, as well as controlling what resources and actions each user is authorized to access.
The key difference between user management and authentication is:
- Authentication focuses on verifying a user's identity, confirming that the user is who they claim to be.
- User management is the broader process of managing user accounts, profiles, and access privileges. It determines what a user is authorized to do or access within the system, after they have been authenticated.
User Management 101: SSO, MFA, and sessions
Building SSO into your product
10 years ago, SSO was something you didn’t need to think about until you were negotiating million dollar deals; today, even mid-large startups are demanding it and the level of added security it promises.
Our developer’s guide to SSO dives deep into how SSO and SAML work, plus practical considerations for implementing them from scratch.
Building MFA into your product
Another table stakes feature for enterprises, and increasingly just startups, is MFA. Multi-factor authentication adds a layer beyond basic passwords to make your login more secure: it can be email, SMS, authenticator apps using TOTP, or even something like biometrics. It has taken a while for everyone to realize the passwords are inherently insecure, and MFA is increasingly becoming a must-have feature if you’re selling into any organization of meaningful size.
Our guide to building MFA into your product has a bunch of useful details on why MFA is important, implementing email vs. SMS vs. TOTP, and frontend/UI considerations.
Sessions
SSO and MFA deal with how you get a user logged in; sessions deal with how you keep them logged in. Most developers will already have some level of familiarity with the concept of a session, so we’ll go a bit deeper here.
1. Implementing sessions
Today there are two major ways developers implement sessions: via cookies and via JWT. Cookie-based sessions are often just called sessions, while JWT-based sessions are just called JWT.
In cookie-based auth / sessions, it is your backend’s job to handle keeping users logged in. When a user authenticates, you create a cookie with basic information (user_id, session expiration, etc.) on the frontend, and an associated row in your sessions table on the backend. On every request, you include the cookie and compare it to the info on the backend: if the session hasn’t expired (or been revoked on the backend), the request is good to go.
In JWT-based auth on the other hand, it is your frontend’s job to handle keeping users logged in. When a user authenticates, you create a signed JSON object that gets stored on the client with all the information (roles, session expiration, etc.) needed to continue to make requests. We then rely on the magic of public key cryptography, plus checking the expiration date, to ensure that subsequent requests are coming from an authenticated user.
Both of these schemes are used heavily in practice. Cookie-based is more straightforward to implement and gives you tighter control, but requires a network trip; if your app is one that suffers from any added latency, this can be a problem. JWTs streamline everything and don’t require backend involvement, but they’re more complicated to create and manage, plus there’s no straightforward way to manually revoke a session (an important feature for enterprises).
2. Login state information
Beyond just handling if a user’s subsequent requests are accepted and tied to their original login, sessions can also help with context (or state) on a user’s login. A good example is an application that needs to support the ability for a user to be part of multiple organizations.
Consider a fictional model employee, a software engineer who does contract work. This model employee has one login to PagerDuty, but is part of two different organizations: Initech and VanDelay Industries. If you sign into your account and choose to sign into the Initech organization, that is authorization context that the app needs to have, and a session cookie (or JWT) is a convenient place to put it.
3. Lifecycle and expiration
You don’t want your user to stay logged in forever – that would be a massive security risk – so sessions usually have an expiration time and date attached to the data object, however you choose to implement it. When your app checks to see if there’s an active session, it will compare the current time to the expiration time of that session and revoke it if the time has passed.
The nuance here is how long that expiration period should be. If you intend to sell to enterprises, the answer is easy: this is something that an IT admin needs to be able to customize for their particular organization. But if you’re too early to build that feature, the answer depends on the nature of your product and how sensitive user data is. Gmail almost never asks me to re-authenticate, probably because I use it every day and having to do so would be annoying. Brex, on the other hand, asks me to re-authenticate most every day.
User Management 201: bot protection, org auth policies, and UI considerations
Our authentication journey continues with more fun side quests. This 201 section covers important to have, but often overlooked, pieces of building your own user management system.
Bot protection
Bot protection is in theory pretty simple: it’s a collection of automated ways that your app handles sketchy authentication behavior.
There is a certain humility, or perhaps it’s naivete, involved in the fact that most early implementations of user management ignore bot protection. Sadly, it is not a question of if, but instead a question of when a malicious actor will test the rigor of your authentication scheme, and without protection from bots you will be left in the cold. For earlier stage companies, there are two main reasons you’ll want to build something here:
1. Avoiding a credential stuffing attack
Credential stuffing is exactly what it sounds like: a hacker using some sort of script to brute force username and password combinations. Without bot protection, there’s nothing preventing this from happening, and perhaps even worse, nothing that would raise an alarm or notify you that it’s happening in the first place. Credential stuffing is one of the most common techniques for taking over user accounts, and is responsible for several high profile breaches (Dropbox, JPMC, Sony, etc.).
2. Handling spam accounts
Anyone who has been on the product team at an early stage startup will tell you about how defeating it can be to deal with spam accounts. Just when you think your product is going viral and you’re finally breaking through, it turns out that you owe most of your newfound success to IP addresses without people behind them . Bot protection can help you avoid this problem in the first place by identifying which visitors are real and which aren’t, and blocking the fake ones.
The most popular off the shelf solution for bot protection is Cloudflare. They go well beyond CAPTCHAs to identify bots (the sauce is very secretive) and it works. Generally it’s hard to justify trying to build your own unless you have a very special use case, especially for early stage teams.
The nuance though – and a tough thing to get right – is figuring out the UX flows. In certain cases, Cloudflare will require a user to click a box to prove they’re human. When does this show up in the flow? Is it styled in the same theme as your signup flow, so it doesn’t introduce odd flashes? This is all work to be aware of.
Org auth policies
IT admins at your customers’ organizations will often want to mandate a specific type of authentication policy for their users. Usually it will be some sort of SSO (Okta, Google, or otherwise) plus a type of MFA. As part of your authentication system, you need to build the ability for IT admins to set up this kind of stuff, plus flows on the backend to require it for members of their organization.
Edge cases rule the day here. If you’ve modeled your organizations such that a user can be part of multiple organizations, and they have different org auth policies, your system is going to need to account for force-logging-out a user. Back to our earlier employee: suppose VanDelay Industries’s IT admin allows users to sign in with any auth, but Initech’s IT admin requires SSO via Okta plus MFA through TOTP. If our employee is signed into VanDelay, but wants to switch to Initech, you’ll need to force them to reauthenticate.
UI considerations
UI is usually the last thing that developers think about when it comes to authentication, save for when that one growth person starts asking about A/B testing different colors on the signup page. But there is actually a lot to get right here; there’s a reason that providers like Auth0 offer a hosted signup page. Building modern auth UI is more complex than you might think. A few examples:
Email verification flows
The deeper you go into account linking (see below) and SSO, it becomes increasingly important to verify that a user actually has access to the email that they used to sign up. If and when you choose to implement this, you need a dedicated screen for it on signup or login.
Password reset
A simple and common one, but one that requires a dedicated screen nonetheless. And if you also want to allow for situations where a user forgets their username (if your app has those), that will require its own flow too.
If an account is already taken
If a user tries to create an account with an email that’s already in use in your system, it’s ideal to account for that on the signup page itself. It’s going to require a network call – but do you do that when the user clicks “sign up” or when they finish typing? If your product has usernames, do you suggest another potential username inline?
Choosing which org to sign into
If a user is part of multiple orgs, you’ll need a dedicated screen that allows them to pick which organization they intend to sign into when they’re, well, signing in.
A smart person once said: “login used to be a simple box; now it’s a mini app.”
User Management 301: invitation flows, email verification, and identity linking
The 301 section deals with the actual identity of your accounts and how you know for sure that different emails actually belong to the people who say they do.
Invitation flows
Modern B2B products are collaborative team products, so you need to thoughtfully consider the user experience for inviting new people to an organization. Beyond the obvious use case of just inviting a new team member, there is a lot to consider here (there are more edge cases than you might think).
Setting up an email service and HTML templating
This bullet point really applies to any emails you’re sending as part of user management, but might as well mention it here! Setting up transactional email isn’t exactly rocket science, but it isn’t something that works the first time either. I like to say that there are two types of developers: those who hate HTML templating, and those who are lucky enough to have avoided working with it. HTML in email sucks! It’s difficult to structure, and even more difficult to style. Each major email client handles styling differently. Set aside a few days and a therapy session.
If you’re just getting started, a single email saying “hey, you’ve been invited to Org X” is sufficient. But that same growth person is eventually going to ask you why you aren’t sending follow up emails when recipients ignore the first.
What if a user already has an account?
Most invites will go to emails that don’t already have an account. If they do have an account though, you’ll need to change the action to “join this organization” instead of “create an account.” This means a database lookup to see if the account exists (by the way, you need to do this anyway to make sure the user isn’t already part of the org they’re invited to), and conditional logic that sends different emails based on the answer.
A more sinister edge case: a user already has an account through their personal email, but the IT admin of the inviting org requires that users use their work email. You will need yet another flow that notifies them that they’ve been invited to an org, but can’t join it with their existing account email, so they need to create a new one. You can just send a brand new invite to their work email, but you run the risk of confusing someone who has already signed up for your product without providing an explanation as to why you’re asking them to do so again.
Automatically joining orgs based on domain
A common invite flow is to automatically add new signups with emails from a specific domain to the organization that owns that domain. If someone with an Initech email signs up for your product, you’d automatically assume they want to be part of the Initech organization. For seat-based pricing products especially, but really for any product, this should be a setting that admins at the organization can set.
Email verification and guest domains
When it comes to email verification, the first question that needs to be addressed is always: why do this in the first place?
The answer is security. In short, we’ve seen several high profile breaches over the past few years stemming from OAuth vulnerabilities that are predicated upon people not actually having access to the email that you think they do. Because when you get down to it, the OAuth provider (with maybe the exception of Google) doesn’t really have any way to verify that you own a particular email address. If this sounds too abstract, consider the follow scenario:
- A user creates an account with the justin@initech.com email and some password
- Someone (this user?) later creates an account or signs in with Google OAuth that tracks to this same email address
- Is this the same person? Should they have access to the same things?
In this case, the answer is yes: because the OAuth provider is Google, who also happens to own Gmail – which the Initech Google Workspace account runs on – they can know for sure that this user both (A) can authenticate to Google, and (B) can authenticate to the underlying email account, which is Gmail.
But what happens if the OAuth provider is someone like GitHub, who doesn’t own the underlying email account? In Azure for example, nothing is stopping you from creating a new organization, adding whatever emails you want to that organization, and using them to OAuth to external tools, even if you don’t actually own those email accounts.
Another not entirely uncommon edge case is guest accounts. For many organizations, all members will share the same email domain. But other organizations work frequently with contractors who have their own email domain, and it’s not practical to create new email accounts for each of them. Interactions between these kinds of accounts and identity providers like Okta can be a bit hairy.
Imagine you’re Nike, and you use Okta as your IdP. Your accounting team is considering a new application for managing your books, which the other you (the reader) are a developer of. For members of the Nike organization with emails at nike.com, Nike can know for sure that the owner of that email is an actual Nike employee, in the accounting department, who should have access to this software. But what about contractors?
The reality is that logging in through an IdP like Okta does not necessarily mean that the person in question actually owns the underlying email address; which means that it also doesn’t necessarily mean that they are who they say they are. If your software is sending sensitive emails that have company information in them, this could be a serious issue.
So please – verify email addresses! It will save you pounds of security headache down the road. And if our favorite growth friend is concerned about attrition from “yet another email step” you can tell them then anyone with a meaningful enough use case will not be deterred by important security measures.
Identity linking
Identity linking is another common flow that can have overlaps with email verification. It deals with what happens when a single user tries to sign in with multiple versions of the same credentials. Consider the following scenario:
- A user creates an account with their business email, and creates a password
- A week later, the same user tries to sign in with Google OAuth credentials that are tied to that same business email address
Was the user trying to sign into that first account they created, and mistakenly thought they had authenticated with OAuth the first time around? Or were they trying to create a second, different account?
Most companies assume the former, and link these two accounts on the backend in order to avoid maintaining several records for what is essentially the same actual user. To account for the (edge?) case where the user actually did want two separate accounts, some apps will design a flow that explicitly asks “hey, you already have an account with that email, do you want to merge them?” But many just do it automatically.
Identity linking is yet another case that demonstrates how critical email verification is. Because if you’re linking accounts where the user hasn’t verified that they actually own the email address in question, you open yourself up to a similar vulnerability that we’ve covered in the past:
- A real, not hacker user creates an account using OAuth
- A hacker signs up for your product with the same (stolen) email address as the real user
- → hacker gets access to the real user’s account via identity linking
Or the reverse:
- A real, not hacker user creates an account with username and password
- A hacker creates an OAuth account in a service that doesn’t verify emails
- The hacker signs up for your product with the OAuth account
- → hacker gets access to the real user’s account via identity linking
So make sure to verify emails!
Just-in-time (JIT) provisioning
One last acronym before we finish: JIT provisioning deals with the automated creation of accounts (or memberships) based on IT admin preferences. It’s a commonly required feature for enterprises, and allows IT admins to avoid dealing with manually onboarding new users to all of their different tools. There are two types to consider:
1. JIT account provisioning
Imagine the Initech corporation has an organization in your SaaS tool with a bunch of members. Their IT admin wants anyone who tries to sign into this tool to automatically have an account created for them on the fly, instead of getting an error message like “sorry, no account with these credentials exists.”
2. JIT organization membership
Usually in addition to the first type, JIT organization membership basically says “any time someone with an email that has the same domain as our organization signs up, automatically add them to our organization.” In this case the IT admin might not want a user account to be automatically provisioned, but instead wants any accounts that are provisioned to automatically be added to their org.
Sometimes, an IT admin might want both of these to happen, so that anyone who tries to sign in with an email at the right domain (a) has an account provisioned on the fly, and (b) is automatically added to the right organization.
For JIT provisioning to work, you will want to do domain verification – verifying that the admin of an organization actually does own the domain that they say they do. If you don’t do this, you open yourself up the following vulnerability:
- A hacker creates a new organization in your product that purports to own the Initech.org domain
- The hacker sets a JIT provisioning policy that automatically adds users who sign up with an Initech.org domain to this fake organization
- Real users who work at Initech are mistakenly added to this fake org via JIT provisioning when they sign up
- Said users connect their data, add IP, etc.
Domain verification is typically done via asking the user to add a TXT record to their domain server, or (less common) adding a tag to the website sitting at the root of the domain. Either one works.
How to implement User Management the easy way
User management is an integral part of building B2B SaaS apps. There are countless ways to go about building this in-house and just as many options to consider if you decide to outsource to an external provider.
WorkOS abstracts away all the complexity and the engineering burden of maintaining a user management solution in house. With easy-to-use APIs and intuitive docs, you can get started quickly and never worry about auth again.
AuthKit provides endlessly customizability for your login box, and User Management supports session management, roles, JIT provisioning, and more. Best of all, it's free up to 1 million MAUs. As your business scales, it's just as easy to add SSO or SCIM provisioning on top.
Sign up today and start selling to enterprises with just a few lines of code.