Lessons in safe identity linking
Identity linking consolidates duplicate accounts with their own authentication credentials into a single account. While this seems straightforward, it involves a number of considerations around email and domain verification. WorkOS handles these complexities and provides secure identity linking by default.
You’ve been there before: you start using an app that supports multiple authentication methods and you sign up with an email and password. A month later you return but unsure of which method you used to sign up, you sign in with your personal Google account. It works, but now you have two accounts for the same app. Confused about where your data is, you reach out to their support staff to help untangle these duplicate accounts.
As an app developer, you know how important it is to solve the problem of duplicate accounts in your own application. A common approach to solving this issue is using identity linking: linking credentials and merging profile details into a single account. On the surface, building this may seem simple but there are a number of considerations, especially in an enterprise context where security is paramount.
What is Identity Linking?
When a user is successfully authenticated by an identity provider, the provider returns a token or assertion that contains some number of claims about the user. Among these claims are the email address, an ID scoped to the identity provider, and some additional attributes. We’ll refer to this collection of claims as a Profile. At its core, identity linking is the act of associating this Profile to some concept of a User in the developer’s application. The goal of this linking is to allow a User to authenticate with multiple Profiles.
For the purposes of this post, a User is identified by an email address. Assume that the User is unique in the context of the application. In B2B contexts, there may be an Organization abstraction that supports grouping of Users. In this case, the User’s uniqueness is scoped to the Organization: there’s only one User in Organization A whose email is bob@hireos.com
.
In many application contexts, an email address is the primary identifier of a user’s authentication method. Magic links, email/password, consumer single sign-on, and enterprise single sign-on providers all use email to identify the user. Because of this, the email address can be used as the unifying identifier when confirming Profiles representing the same User.
Two conditions need to be met when linking a Profile to a User.
First, if Organizations are in play the User must be a member of the Organization to which the user is trying to authenticate. A malicious actor could configure an identity provider to return a Profile with the email address of a target user in a different Organization. If this check is not performed, account takeovers are possible and do happen in the wild. Implementing this is specific to the application logic so we won’t dive deeper in this post.
Second, the authenticating user should be able to prove they have access to the Profile’s email address. There are a few ways to do this, starting with a typical email verification process. However, depending on the identity provider, there are more frictionless ways to prove access. We’ll look at each of these approaches and caveats later.
Email verification
As mentioned above, a Profile’s email address is the unifying identifier. In order to safely link a new Profile to an existing User, the developer needs proof that the authenticating user owns the email address. The most straightforward way to prove this is to send an email to that address and ask the user to prove they can access its contents.
Often, developers place a link in the email body that the user can click on, returning them to the developer’s app which updates the Profile’s email verification state. The downside to this approach is that some email providers can be configured to follow links in email bodies to identify spam or security issues. These checks render the verification link useless since, for security reasons, they can generally only be followed once.
To avoid this issue, a better practice is to include a string of characters or digits in the email body that the user inputs to a form in the developer’s application. The Profile’s email verification state is updated upon successful form submission. Although this requires manual copying or copy/pasting, it results in a less frustrating user experience overall.
Avoiding email verification
Email verification works as a means of safely linking identities but it does add friction to the sign up process. Avoiding it altogether would be ideal. Doing so requires understanding the nuances of the identity provider. There are further considerations depending on how the provider is being used, but in general, providers can be broadly broken into two categories
- OAuth providers like Google or GitHub
- Enterprise SSO providers like Okta or Entra ID (formerly Azure AD)
OAuth Providers
Consumer OAuth providers offer a low friction way for a user to authenticate to an application. In some cases an application can rely on a Profile’s email claim and skip email verification but we’ll also go over a few examples where this is not the case.
Native domains for email providers
Some identity providers, like Google or Apple, also act as email providers. They can generally be trusted when emails from their native consumer domains are being used. If Google OAuth authenticates a profile with a gmail.com
domain or Apple OAuth authenticates a profile with an icloud.com
domain, developers have a guarantee that the user needed access to that email address to authenticate properly, so there’s no need to perform additional email verification.
An additional note on Apple’s OAuth implementation: in the sprit of privacy, users are presented with an option to “Hide my email” when they first sign in to an application. If selected, the returned Profile will use an anonymized email address that forwards to the user’s actual email inbox. In this case, identity linking is not possible because the anonymized email address will be unique and will not match any User’s email address. Depending on the application use case, a developer may need to make this clear to users.
Stale verifications
Other OAuth providers, like GitHub, don’t provide emails on their own domain but allow users to add email addresses from other providers. To authenticate an application using OAuth with one of these emails, GitHub requires a user to verify their email address. It’s reasonable to trust that GitHub will perform the email validation and make email claims properly, but there is a caveat. GitHub does not continually perform email verification so a user may have verified an email address held at an employer but they now no longer work there. GitHub will continue to make the same email claim even though the email verification is effectively stale. This opens the door for a previous employee, for example, to access resources to which they should no longer have access.
To account for this caveat, developers may choose to perform email verification when linking a new Profile to a User. This tradeoff in user experience for better security is a decision each developer needs to make based on the profile or needs of their application.
Non-Gmail Google Accounts
A similar issue arrises in Google’s support for non-Gmail domains when using OAuth. To summarize, non-Gmail Google accounts can be created using an email alias, potentially giving an employee the ability to access company resources undetected after they’ve left the company. For example alice@hireos.com
could create a Google account with the address alice+later@hireos.com
while she’s still working at HireOS. She’ll be able to verify the email address because of the plus-sign forwarding that Google offers. When she leaves HireOS, though, alice+later@hireos.com
is not under the control of the HireOS IT Admin and would not be part of the off-boarding process, meaning that she could still receive access to applications that don’t perform an additional verification of the email claim.
There are a couple of options available to account for this case. First, a developer can additionally confirm the Profie’s HD
claim matches the email domain and that it is part of a Google Workspace-managed account. Alternatively, a developer can disable just-in-time (JIT) account creation in the app and enforce invite-only flows to add a User to an Organization.
Microsoft Audience vulnerabilities
Finally, some providers, like Microsoft, have had more complicated policies around email verification within the OAuth flow. In short, Microsoft allows OAuth applications to choose between several “audiences,” which are like buckets of users that can include personal accounts, work accounts, or both. In a high profile incident, it was shown that opting into the “work account” audience meant that applications could be exposed to user profiles with unconfirmed email
claims and the potential for impersonation attacks.
With no guarantee that all applications were implementing email verification of their own, Microsoft issued a breaking change to their OAuth API as a solution to the since-dubbed “nOAuth” vulnerability. Staying ahead of provider differences like this is no simple task.
Enterprise SSO providers
Unlike consumer OAuth providers, enterprise SSO providers give IT admins more explicit control over who has access to which services. On top of the security and compliance benefits of SAML and OIDC, the additional control in the hands of IT admins means there are better guarantees that authenticated users are who they say they are.
Domain Capture
While email verification is the approach most broadly used to verify a Profile’s email claims, in the context of enterprise SSO, domain capture removes the need to verify emails within that domain. Domain capture, as a feature of enterprise applications, is a set of controls an organization’s IT admin can apply to everything related to its organizational resources. These controls rely on an IT admin’s verification of ownership of the domain — usually by making DNS configuration changes — confirming that they have full control over the profiles in that domain.
Once a domain has been verified, a Profile with an email claim from that domain can be assumed to be verified meaning safe identity linking can be performed.
It’s important to remember that an Organization that has enabled domain capture should be guided to disable their other authentication methods. The guarantees provided by domain capture are effectively nullified if users are able to authenticate via other means, so it’s important to enforce signing in with the enterprise SSO provider once domain capture has been enabled.
Conclusion
Identity linking addresses a critical user experience issue at the front door of an application by allowing users to merge multiple identities into a single account. While it seems straightforward on its face, there are meaningful security considerations, especially as your customer base expands and demands more authentication providers. Understanding and accounting for the behavior of different authentication providers when linking identities is essential to maintain the right level of security.
WorkOS allows developers to hand off the complexities of safe identity linking. Identity linking behavior in WorkOS is secure by default but allows developers to make informed decisions when it comes to making specific trade-offs between user experience and security.