Master Difficult User Authentication Requirements with OAuth

Curity
6 min readDec 17, 2024

--

The article was written by Curity’s Gary Archer and originally published on The New Stack

Many organizations fail to recognize that user authentication for your digital services will evolve. Here’s how to switch methods.

User authentication has always been a complex topic for organizations building digital services. You need to implement low-level security to manage credentials so that malicious actors cannot perform account takeover attacks and gain access to data that belongs to your customers.

Yet many organizations fail to recognize that user authentication for your digital services will evolve. There are a handful of reasons why you might need to change authentication methods:

  • A newer option might increase security and enable you to meet your strong customer authentication (SCA) requirements.
  • A newer option might improve the login user experience to reduce users getting stuck or locked out of your apps.
  • You might receive requests from important stakeholders that you cannot ignore. For example, an important customer organization might require your apps to authenticate its users using its authentication system.
  • You might need to authenticate users at a particular system to call that system’s APIs on behalf of the user.

Effectively managing these types of concerns requires insight. Therefore, I will explain how you can implement authentication and then easily change the method(s) used, both to keep your security up to date and to meet your business requirements.

Implement the Code Flow

Many modern applications use the OAuth 2.0 authorization framework, a family of specifications to secure APIs and their clients. Typically, you use OAuth together with related standards like OpenID Connect. I won’t dive into specific security details here. Instead, I will explain how OAuth provides you with a powerful design pattern to manage user authentication.

When using OAuth, your frontend application (described in OAuth as the client) initiates user authentication by running a code flow using the system browser. The user (described in OAuth as the resource owner) then signs in using the login forms provided by your authorization server. Your client receives an authorization code in the browser response and swaps it for an access token containing the user’s identity and permissions. When calling your APIs, your client uses the access token as a message credential. Here’s an example of a high-level sequence:

It’s common to misunderstand the authentication part of OAuth. Developers might have heard technical statements like, “OAuth is not an authentication protocol” and think OAuth does not help with authentication, or that OAuth should only be used when you need toOgrant access to a third party.

Yet OAuth is a complete and modern security framework for any solution involving APIs, clients and users. Implementing OAuth correctly should provide your clients with flexibility so that you can scale the way user authentication works and thus improve your business options.

Scale User Authentication Methods

The statement that “OAuth is not an authentication protocol” does not mean that OAuth bypasses user authentication. Rather, it means that the OAuth standards do not define the way you prove user identities — they are left to the implementation of the authorization server. The reason for this is that there are myriad possible solutions for user authentication. These are constantly evolving and range from state-of-the-art authentication methods, like passkeys to vendor-specific or bespoke solutions that you may have to use.

To understand how authentication scales with OAuth, you should gain a deeper appreciation of what the code flow enables. The best authorization servers offer many sophisticated categories of user authentication methods:

  • Passwords + multifactor authentication (MFA)
  • Passkeys
  • Digital wallets
  • Standards-based external systems
  • Other external systems
  • Custom authentication methods

Sometimes, upgrades to an authorization server provide new security standard implementations. For example, passkeys use WebAuthn standards from the FIDO alliance and logins with digital wallets may require a Digital Credentials Protocols (DCP) implementation.

Yet, above all else, the code flow is a design pattern that provides business value. Rather than developers struggling with security, they outsource it so that user authentication is forever separated from your frontend applications. When you need to change the way users authenticate, you do so at the authorization server with zero code changes to your applications.

Scale Business Integration

You often need to integrate a subset of users with a particular external authentication system. This type of system is usually described as an identity provider (IDP). An IDP often implements an organizational security policy for your organization’s employees or those of your business partners. When an employee joins an organization, an account may be created in the IDP. If the employee later leaves the organization, the account is deactivated to deny access to all systems, including your frontend application.

As a design pattern, the code flow copes elegantly with this requirement. One of the authorization server’s authentication methods should enable a second code flow that the authorization server implements to connect to an OAuth-based external IDP. Your authorization server may also be able to connect to IDPs using other security protocols, like SAML 2.0.

Therefore, when planning your next-generation user authentication, consider how different sets of users will authenticate in the coming years. The following diagram provides an example where you use passkeys as the default. Meanwhile, your employees can log in with their cloud provider, such as Microsoft Entra ID, and each of your business partners can integrate their IDP.

Once you have different authentication methods per user type, you need to deal with authentication selection. By default, your authorization server might present a list of all authentication methods to all users. Yet this may lead to confusion if a user chooses the wrong option and cannot sign in.

To prevent this, your authorization server should first enable you to present a form to prompt the user for an identifier, such as a username or email. You can then apply custom logic, such as looking up the user’s tenant ID, to determine the user’s authentication method and then route them directly to it. Such a login user experience can also help to ensure that the same user identity is issued to access tokens when the user’s primary authentication method changes.

Implement Custom Authentication

The world of authentication is not just about security standards. You could run into requirements that do not fit cleanly into OAuth:

  • Regulatory requirements may mean you must implement additional custom login forms that prompt the user for industry-specific proofs of their identity.
  • You may need to sign users in with a social identity provider to get an access token to call its APIs, yet the IDP does not use standards-based authentication.

No authorization server provides built-in options that cover all possible ways to authenticate users. You might therefore run into a blocking issue. Some developers may revert to implementing custom authentication within their frontend applications. Yet the developer will then discover that they do not receive the correct access token with which to call their APIs. Any workarounds to enable API access would add significant complexity and technical debt.

Instead, extensibility is a critical behavior to look for in your authorization server. For example, the authorization server may enable you to use or develop plugins that meet your custom authentication requirements. Your frontend applications then continue to run a code flow. You compose the authorization server’s built-in behaviors with custom extensions you develop.

While this approach still requires some development effort, the benefit is you remain within the OAuth framework and implement all responsibilities in the correct places, including auditing of login attempts. You should be able to implement any possible custom authentication and avoid ever being blocked. When required, multiple clients can reuse your customizations.

Finally, when customizing authentication, you should not change your authorization server’s core code. Doing so could result in security mistakes or additional work when you upgrade the authorization server.

Conclusion

You can take full control over authentication requirements in any type of frontend application if you follow these steps:

  • Implement the code flow in frontend applications to initiate user authentication.
  • Scale user authentication methods by using an authorization server with up-to-date support for authentication standards.
  • Scale business integration using an authorization server that can connect to external IDPs and enable reliable authentication selection.
  • If you ever encounter custom requirements, implement custom authentication to ensure you are never blocked.

Therefore, the next time you need to change the way user authentication works, consider upgrading to the OAuth approach to improve the capabilities and reach of your applications. Ensure that your authorization server provides a security toolbox with the design patterns you need to use. You can read much more about the approach in our website resources:

--

--

Curity
Curity

Written by Curity

Curity is the leading supplier of API-driven identity management, providing unified security for digital services. Visit curity.io or contact info@curity.io

No responses yet