Chapter 1. Why Do You Need OAuth?

Around 2010, if you were part of a team developing digital solutions, you would probably have built a website that exposed data to a single client, the browser. In those days, users authenticated with a username and password, after which the website issued an authentication cookie. This cookie secured calls to the backend and included the user identity and an expiry timestamp. The backend then enforced security rules using the cookie user identity, such as preventing user A from accessing user B’s data.

To meet today’s business needs you use a more componentized system. You typically work on a backend platform consisting of multiple APIs, which serve data to web, mobile, and business-to-business (B2B) clients. As with websites, you need a way to authenticate and authorize requests to and between APIs. While cookies still qualify for authenticating requests in certain scenarios, you need a solution that works for a variety of clients. In addition, many organizations operate a large number of APIs and clients, so you also need a solution that scales. This is where OAuth comes into play.

OAuth provides the protocols and tools to implement consistent, scalable access controls in APIs. At its core lies the access token that conveys permissions to access APIs and consequently (business) data. APIs are now business products, whose exposed data generates the business value. Hardened API access is not a technical but a key business concern. OAuth allows you to customize API authorization to meet your business requirements.

In this chapter, you will first learn the main steps we recommend for enabling modern API-first security, with a primary focus on correct authorization. We explain how OAuth 2.0 enables this authorization and we recommend applying it to every API request. We call this zero-trust API security. To enable the right security you need components that support your APIs and applications. Next, you need to operate both APIs and supporting components. We explain how cloud native platforms provide some leading operational capabilities, including advanced infrastructure security. You will see how your technical choices play an important role in determining whether you achieve the best business outcomes.

API-First Security

Security for a digital business is a multi-faceted topic with many best practices:

  • Follow a secure development lifecycle to protect against threats like SQL injection.

  • Secure the software supply chain using techniques such as vulnerability scanning.

  • Run backend components on secure operating systems using a low-privilege service account.

  • Expose data to the internet using Transport Layer Security (TLS).

  • Apply network protection using proxies or firewalls.

All of these strategies are insufficient to secure business data. For that, you need a security architecture that focuses on your applications, APIs and users. A key behavior of such an architecture is the principle of least privilege. You should grant applications only the API permissions that they strictly need, and each API must enforce those permissions. At the same time you should use hardened security for user authentication but also provide a modern and friendly user experience. What is more, the security behaviors should scale to many APIs and clients without a linear increase in complexity. To achieve that, use the OAuth 2.0 authorization framework.

What is OAuth 2.0?

We explore the details of the OAuth 2.0 protocol in Chapter 2, but first we want to provide a high level overview of its primary behaviors. As the name suggests, OAuth is centered around authorization. In its core, it uses an API message credential, the access token, based on which APIs can enforce authorization. The framework was designed to meet the security needs of any type of API client, including browser-based apps and mobile apps, and to work in any technology stack. User privacy and consent are a core part of the design, and users can also authenticate in many possible ways.

There are a number of benefits when leveraging OAuth. Firstly, you avoid home grown security solutions because you can build upon best practices that many experts vetted. You can write simpler code and reduce the risk of security flaws. Secondly, an OAuth architecture is highly extensible and scalable. For example, you can reconfigure user logins in many possible ways, without needing to change any code in clients or APIs. Thirdly, since OAuth is a well-established standard, there are many security libraries that implement the protocol, which helps to cut the time to market for your secure solution.

OAuth 2.0 was introduced in the RFC 6749 specification1, published in 2012. It introduces a component called the authorization server. This component externalizes most low-level security from clients and APIs. Before calling APIs, the client must get an API message credential, the access token. As part of that process, the authorization server authenticates the user (if applicable) before it returns an access token to the client. The client then sends the access token to APIs, typically through an API gateway. APIs only accept requests if they include a valid access token. Figure 1-1 shows an end-to-end flow where the user authenticates before the client receives an access token that it forwards to APIs.

OAuth interaction between clients, the authorization server and APIs
Figure 1-1. OAuth interaction between clients, the authorization server and APIs

The user login is often the most visible part of OAuth, yet the heart of OAuth is the access token. The access token can provide the user identity and other secure values to APIs in a cryptographically verifiable and auditable manner. You design access tokens in terms of business privileges to APIs. You should customize the access token for each client to only give them least-privilege API access. Since every business is different there is no fixed way to design access tokens. Similarly, you can choose from many forms of user authentication provided by your authorization server. We explain your choices for access token design in Chapter 6 and your choices for authentication in Chapter 14.

Business authorization is part of your domain logic. Therefore, you usually implement it in the API code alongside other domain logic. On every request to APIs and microservices, the API must validate the access token and authorize access using the received token data. With the access token, you can forward the user identity and other secure values between microservices while keeping the user identity verifiable and auditable so that upstream microservices can also implement security correctly.

There are various other application security frameworks, though most of them provide only user authentication. If you use an authentication framework you eventually need to invent an API message credential to implement authorization. This results in home-grown solutions that are hard to maintain because of their complexity and reduced interoperability. OAuth 2.0 focuses on API authorization. With OAuth, you can use interoperable security standards for end-to-end flows and base your solution on the work of security experts. This work includes API, client and user behaviors. Finally, OAuth lets you implement security that scales to many clients and APIs.

OAuth is a framework rather than an out-of-the-box solution. How you apply the framework varies depending on your business requirements for authorization, user authentication, and other security areas. More generally, OAuth is a family of specifications, each mapping to use cases for software organizations. Implemented correctly, OAuth not only improves your security architecture but also simplifies it by externalizing security complexity from APIs and clients.

For all of these reasons, adopt OAuth as your security design if you have an API architecture with user-facing clients. We see it as a security strategy that can work for any organization. We also recommend a particular implementation approach to API security called the zero-trust architecture, where we define zero-trust in terms of business permissions to access your APIs.

Zero-Trust Security

Traditionally, security controls focused on the perimeter of the infrastructure. In that model, a strong border divides the infrastructure into external and internal parts. It assumes that internal parts are trustworthy and thus focuses only on external security threats. Cloud computing blurs the border, as organizations commonly use cloud services for internal functions. Consequently, the perimeter approach is insufficient for securing a digital business nowadays. Modern API security must therefore address both internal and external threats.

A zero-trust approach does not assume any implicit trust, e.g. based on infrastructure rules such as internal network addresses. Zero trust means that you should use explicit trust and not assume that requests come from a certain client or user. With regards to API security, this implies that APIs must always verify the caller. To understand the problems with implicit trust, let’s start by examining the threats inherent in perimeter security.

APIs with Perimeter Security

When organizations first built web APIs it was common to adopt a perimeter security approach, where the perimeter represents entry points into the backend cluster called by internet clients. Figure 1-2 shows an example, where the website implements strong security by requiring browser clients to send a secure encrypted cookie containing a user ID.

During the processing of web requests, the website calls internal APIs that are not exposed to the internet. The website uses weak security for internal connections. In the example, the website uses an API key (or HTTP basic authentication) for the API message credential and forwards the user identity to APIs in a plain HTTP header.

Microservices with weak security
Figure 1-2. Internal APIs with weak security

There are several problems with this design. First, a malicious party inside the network might be able to steal and reuse the API key. Secondly, you might only rarely renew API keys, in which case an attack could continue against the APIs for a long time. Finally, the user ID is a secure value that you need to protect. A malicious party should never be able to bypass security by injecting their own user ID, to get data for another user. With this security design, target APIs cannot know whether secure values received are correct.

A first attempt to improve the internal API security might rely on infrastructure. Let’s examine this type of solution next so that you understand the problems infrastructure cannot solve.

APIs with Infrastructure Security

You can improve API security by securing internal connections with the help of infrastructure security, such as the mutual TLS that cloud native service mesh solutions provide. Figure 1-3 shows an updated example where the entry point website continues to implement strong security by requiring a secure cookie. The website then interacts internally with microservices using a client certificate credential.

Internal APIs with infrastructure security
Figure 1-3. Internal APIs with infrastructure security

Adding infrastructure security to protect calls between microservices is certainly an improvement over perimeter security since it is now more difficult for a malicious party to call internal APIs. The stronger connection does not deal correctly with authorization, though. Instead it is an authentication-only solution between backend components. Sensitive values like user IDs continue to be sent in HTTP headers. The API cannot verify that it receives a correct user identity. A malicious or misbehaving API client in the environment could call the API with incorrect HTTP headers and access unauthorized data. To avoid that, we recommend a zero-trust OAuth implementation using token-based security. Let’s look at that next and explain its benefits.

APIs with Token-Based Security

You can easily enforce explicit trust with OAuth. You simply require every call to every API endpoint to contain an access token with cryptographically verifiable information about the caller. Every API validates the access token to verify its integrity and to ensure that the access token meets that particular API’s security requirements. If a token is altered or is not valid for other reasons, the API immediately rejects the request. This means, the API only accepts requests if they contain a valid access token. The trust is explicit because all internal APIs validate the token on every request.

Access tokens are your API credentials. You design them to enable APIs to enforce least privilege access. First, access tokens can be designed to restrict access by business area. Next, you can include values in the access token that your APIs use for authorization. These can be user attributes or runtime values such as the authentication strength. This provides the most powerful ways for your APIs to authorize requests. Finally, assign access tokens a short lifetime to limit the impact of any potential misuse.

When an API validates access tokens, it immediately rejects any requests containing altered or expired tokens. Otherwise, the API trusts the identity attributes in the access token and uses them for business authorization. When implemented correctly this approach provides zero-trust in terms of your business. The access token payload in Example 1-1 shows some example attributes.

Example 1-1. Access token payload
{
    "sub": "556c8ae33f8c0ffdd94a57b7eab37e9833445143cf6847357c65fcb8570413c4",
    "customer_id": "16771",
    "iss": "https://login.example.com",
    "aud": ["api.example.com"],
    "scope": "products",
    "membership_level": "1",
    "level_of_assurance": 3,
    "exp": 1721980485
}

In Chapter 5 we explain the meaning of the standard fields of an access token, such as the ìss, aud, sub and scope fields in Example 1-1. We also show how you can write API code to correctly validate access tokens. For now, you should understand that you have full flexibility in how you design the access token and lock down API access, to meet your business needs.

There is also a more subtle point about APIs only allowing requests that include valid access tokens. Figure 1-4 shows how each API downloads a cryptographic public key from the authorization server and uses it to verify access tokens. You should configure each API with a trusted URL to the authorization server that expresses a trust relationship. APIs do not trust each other. Instead, they only trust the authorization server.

API trust
Figure 1-4. API trust

OAuth and infrastructure security solve different problems in your security architecture. Infrastructure security provides data confidentiality and can require an immediate backend caller to authenticate with your API using a strong credential. OAuth enables clients to send restricted access tokens to APIs, which then authorize requests based on the attributes in the access token. You can use OAuth to scale security to many APIs. If you design access tokens effectively this can be done in a simple and maintainable way. We explain our recommendations for designing and scaling the use of access tokens in Chapter 6.

So far we have described how to protect against API internal threats. Next, you need to think more about clients and users, since they are located outside the backend cluster and subject to external threats. You need to prevent a malicious party from impersonating the real client and user by calling your APIs. To prevent such attacks, let’s briefly summarize what zero-trust means for clients and users.

Zero-Trust for Clients

Since clients receive access tokens you must ensure that a malicious party cannot steal access tokens from the client environment and use them against your APIs. Most commonly these clients are web applications, mobile applications or backend APIs from your business partners. In Chapter 7 we explain how you can harden the use of access tokens differently for each type of client. When designing zero-trust for clients, you must first consider their execution environment and what threats exist there. You must then follow best practices for protecting against those threats.

Web applications typically run in the browser and use JavaScript to enable a fast interactive user experience. The Single Page Application style is popular, due to its use of modern and productive web frameworks. At the same time, the browser is a hostile environment for executing code since many attack vectors exist there. We dive deep into using browser security with OAuth in Chapter 13. You will learn that following current best security practices requires backend components that help to secure the front end.

A general threat for any type of client is that of impersonation, where a malicious app tricks the user into initiating a login flow. This could lead to the malicious app receiving valid access tokens with which to call your APIs. Keeping up to date with standards and best practices provides the best protection. Impersonation is a particular concern for platform-specific applications. Chapter 12 explains the OAuth current best practices for both desktop and mobile applications.

Even if both APIs and clients have great security, you still need to involve the user in security. The most visible place where security involves users is during authentication. These days your security solutions should always consider user privacy. Let’s provide a brief overview of zero-trust for users next.

Zero-Trust for Users

Often, the caller of APIs is a user. Consequently, the user must authenticate before calling APIs. Users do not interact directly with APIs and instead use a client application as their delegate. When possible authenticate both the user and the client application before issuing the client an access token. Also, consider user privacy as part of your base setup when integrating OAuth. We say more about user privacy in Chapter 4.

You should design authentication processes according to your particular requirements and preferences. Traditionally, users have authenticated with passwords, yet these have many weaknesses. Since users often use the same password across multiple applications, there is considerable scope for password abuse. A malicious site could use a phishing attack to trick users into revealing passwords. Even worse, server breaches could potentially reveal the passwords of many users. Your authorization server should provide up-to-date authentication methods, to give you choices that enable user authentication with both strong security and modern usability.

These days the trend is to move away from passwords and use more secure options such as WebAuthn, Passkeys and digital credentials. We believe that cryptography-backed authentication methods will become the norm. These methods use standardized capabilities built into operating systems, browsers or wallet applications. The result is that users generate a key pair using those capabilities. When authenticating, the user produces a cryptographic signature with the private key of the key pair. This signature serves as a user credential that the user sends to the authorization server. The authorization server knows the public key of the user’s key pair with which it can verify the signature to authenticate the user. We say more about modern forms of user authentication in Chapter 14.

OAuth allows you to apply a zero-trust architecture that supports modern, secure and user-friendly authentication methods via the authorization server. The authorization server is not the only security component though. You also need to run other components alongside your APIs, which perform both security and operational tasks. Next, let’s take a closer look at what we mean by supporting components.

API Supporting Components

You can host APIs and their data in multiple ways in the cloud. One option is to spin up and manage a virtual private cloud (VPC) with an isolated network running virtual machines. Another strategy is platform as a service (PaaS) hosting, where a cloud provider manages the infrastructure behind the scenes. Both of these options may require investing in vendor-specific technology, complicating future backend migrations. Some hosting options may also impose restrictions on the API code that you write, the data storage options, or the supporting components you can use. Ideally, you should instead be able to make technology choices without limitations.

Your APIs use supporting components such as a message broker and a log aggregation system. The authorization server is a supporting security component, but other security components also play an important role. In Chapter 3 we explain how best to separate security responsibilities across components to simplify code and provide the best future extensibility. Aim to host such supporting components right next to your APIs as we illustrate in Figure 1-5. This provides optimal performance and also enables you to reduce the OAuth endpoints you expose to the internet.

Cluster with APIs and supporting components.
Figure 1-5. APIs are surrounded by supporting components

The platform on which APIs run must support a heterogeneous landscape and provide its own advanced behaviors. These include elasticity, service discoverability and observability. Ideally, once all components are working together, the entire platform should be portable so that you can deploy it wherever best meets your needs.

You may make suboptimal choices when you select supporting components and hosting platforms, which can work against your goals. For example, though OAuth enables a certain degree of interoperability, it leaves room for vendor specific implementations. You need to make sure that your authorization server not only supports the authentication methods you need, but also a suitable interpretation of standards required for your integrations.

Ensure that you identify important business requirements. Various stakeholders have different requirements:

  • Business owners care about technical costs, time to market, and future expansion that may lead to hosting APIs in additional regions.

  • The user experience (UX) team needs to blend login security with a simple UX.

  • Software architects want the best tools and technologies for future software designs and to keep solutions portable.

  • Developers are most productive when they have powerful development options on their local workstations.

  • DevOps teams need the best observability and management features for reliable operation and fast problem resolution.

  • Lastly, compliance teams care about data privacy and require visibility into security-related events.

Take some time to think through your future requirements when choosing technical foundations. At first sight, choices related to security and backend hosting may seem like technical topics relevant only to engineers. In the end, however, they also have a considerable business impact. A modern approach to host APIs uses cloud native platforms and design patterns. Consequently, your API security solution needs to fit into such an environment. OAuth is a distributed architecture that you need to operate. The features of cloud native platform provide operational benefits which we explain in Chapter 10.

Cloud Native Platforms

Cloud native is a technology approach for using cloud infrastructure in a vendor-neutral way. Combining OAuth with cloud native allows us to describe and focus on a security solution that works for the majority of modern deployments. When designing your security architecture we recommend that you start with OAuth, to enable an end-to-end security solution that includes users, clients, APIs, and business data. Ensure that access to all of your APIs use access tokens that restrict access to business data. Add cloud native infrastructure security to harden your security posture further.

As APIs communicate with each other, access tokens flow through your cloud native environment, and join it to the outside world. We illustrate the cloud native deployment with OAuth in Figure 1-6.

OAuth in a cloud native platform
Figure 1-6. OAuth in a cloud native platform

The most mainstream way to apply cloud native is to use a platform like Kubernetes. We use Kubernetes and cloud native design patterns, practices, and tools from the Cloud Native Computing Foundation (CNCF)2 to show how to utilize cloud native infrastructure security for OAuth. One CNCF project of particular interest to API security is the Secure Production Identity Framework For Everyone (SPIFFE)3. This is a standard for infrastructure security to enable trusted and encrypted connections between workloads. A workload is the running instances of your software4. A workload can span multiple instances, e.g. you can deploy an API workload as a Kubernetes Deployment containing a ReplicaSet with 4 instances. You can integrate the SPIFFE Runtime Environment (SPIRE) by deploying a service mesh to a Kubernetes cluster. When workloads call each other, SPIRE transparently upgrades HTTP connections to mutual TLS. This ensures the confidentiality and integrity of requests inside the cluster.

SPIRE generates short-lived X.509 client certificates and keys which serve as workload identities when components call each other. The platform takes care of the credential management and automatically renews workload identities. These technologies are a valuable addition to your OAuth security toolbox. We demonstrate the use of SPIFFE and SPIRE in Chapter 10.

The result of a cloud native deployment is a componentized architecture where you divide responsibilities. To implement the overall technical behavior your need to orchestrate multiple components. This separation enables reuse as you add new components, such as additional APIs, to your architecture.

Summary

We believe that the optimal way to implement OAuth security is within a modern cloud native platform. This enables you to choose the best-of-breed components to support your APIs. The cloud native platform also provides built-in capabilities, which both your APIs and their supporting components use. Solid technical choices then help to ensure the right business outcomes. We explain more about the security architecture and the setup of security components in Chapter 3.

You should apply a zero-trust approach to address external as well as internal risks. In terms of API security, it means that you should authenticate and authorize every caller of a request. OAuth 2.0 enables a zero-trust approach for API authorization and access to business data. In our experience, OAuth 2.0 combined with some cloud native patterns provides the most complete architectural solution for a scalable security architecture. It allows you to externalize much security code from APIs (and clients). Let’s have a closer look at OAuth and various flows.

Get Cloud Native Data Security with OAuth now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.