Chapter 10: Securing APIs

Design and Build Great Web APIs — by Mike Amundsen

6 min readJul 1, 2022

--

Understanding Security Basics

The key to understanding API security is to focus on two related elements: identity and access control. These work at the API level. In other words, when you are implementing the API, you need to decide if and when you’ll apply identity and access control checks.

It’s also important to understand the role of encryption as an additional layer of security. For HTTP-based APIs, this works at the protocol level. The most common way to recognize the use of encryption on the web is through the use of the https identifier (called a URI scheme) instead of the http identifier in your URLs.

These two items — identity/access control and encryption — can work independently of one another too.

Identifying API Users with Authentication

A good place to start when tackling the topic of API security is the concept of identity (sometimes called authentication). Determining the identity of the person or machine making the API call is a key step in the process of securing your API.

The most common way to determine identity on the web is by using the OpenID and OAuth protocols together. OAuth is a set of standards focused on authorization. OpenID is a set of standards focused on authentication. OpenID Connect is the identity layer built on top of the OAuth access control specification.

OAuth is a common API security solution because it offers quite a number of authentication workflows (called grant types) to fit various levels of security and interaction needs. An important feature of OAuth is that it supports what’s called three-legged authentication. This means the actual authentication event happens at the provider service, which provides an authentication token that’s then used by the client (application or API) to present to the actual service.

The process works like this:

  1. The client application makes a request to an OAuth provider service to get a valid token. This is where the username and password are supplied by the client application. For machine-to-machine APIs, we’ll supply the client ID and the client secret (more on this later).
  2. The OAuth provider validates the login identity and returns a special encoded token. This token is what the client application will use when making requests to the API service.
  3. The client application sends a request to the API service (for example, GET http://api.example.org/company/132435). The client application includes the token it got from the OAuth provider in the Authorization HTTP header.
  4. The API service accepts the request and the token from the client application and sends the token to the OAuth provider to ask if that token is valid.
  5. The OAuth provider validates the token and returns it to the API service.
  6. The API service, having determined this is a valid token for this request, returns the requested resource response that the client application asked for in Step 3.

Although this process is a bit more complicated than sending the API service a simple username and password, three-legged models have a number of important advantages to consider. First, using an external OAuth provider to handle the login steps means the user passwords don’t need to be stored by the API service. Second, it allows the API service and the client application to support more than one OAuth provider. For example, the API service might support using Microsoft, Google, Twitter, Facebook, and other OAuth providers. They can even add new providers over time without disrupting the existing client applications.

Identifying the API user making the request is just part of the job of completing a secure API transaction. It’s also important to understand the access control limits each request has for any services it attempts to contact.

Controlling API Access with Authorization

The act of authorizing a request is, essentially, associating access rights to the request. For example, if an API call to the company services attempting to execute the removeCompany action was initiated by me, and the third-party authentication service (in our case, Auth0) was able to identify that it was me (MikeAmundsen) making that request, then the work of authorizing that API request would be to verify that MikeAmundsen has rights to access the company service and that MikeAmundsen has rights to execute the removeCompany action.

Access control can be applied and validated a couple of different ways. For example, identities can be associated with roles (such as admin, manager, user, guest). The company might have a built-in rule that any request with an identity associated with the admin role is allowed to execute the removeCompany action. Access control can also be applied directly to identities. Using the earlier example, that would mean that the company service would know about a list of identities (such as MikeAmundsen) and their associated execution rights.

In both of these examples, the real work of sorting out which identity has which access right is the work of matching validated identities with pre-determined actions. How and where that’s done varies by organization.

Protecting API Traffic with Encryption

Encrypting API requests and responses offers a level of security for messages as they pass from one place (the client app) to another (the service). It’s good practice to implement message-level encryption for your API. This is especially true when interaction with your service crosses a network boundary. Your company’s network, for example, is a boundary. If any API calls come from outside that network, you should encrypt the traffic in order to protect it from eavesdropping, tampering, and message forgery.

The most common message-level encryption implementation is to use Transport Layer Security (TLS). The role of TLS is to prove what’s called a “secure channel” between two parties. TLS requires a bit of setup, or handshaking, to establish the identities of each party and a mutual encryption scheme. When that’s done, the two parties use the same encryption details for each message they pass back and forth. You may be familiar with the Secure Sockets Layer (SSL) protocol. TLS is the successor to SSL. They work pretty much the same way and are used for the same purpose.

In order to access TLS encryption over HTTP, you need to use the Hypertext Transfer Protocol Secure, or HTTPS protocol. That means using the https URI scheme when making your calls to another machine on the web. The HTTPS protocol encrypts the HTTP headers, body, and cookies before it sends them to the other party. The party receiving the message will be able to decrypt these same message parts (headers, body, and cookies) once the message arrives at its destination.

WARNINGIt’s important to point out that the contents of the HTTP request line are not encrypted. If you’re passing sensitive data over the URL, as in /user-service/filter?SSN=123–45–6789&lastName=Smith), this data will be sent unencrypted, or “in the clear,” even when using HTTPS protocol.

The good news is that the TLS encryption and decryption functionality is built into all browsers and almost all HTTP coding libraries.

Implementing API Security with Auth0

We’re going to use the Auth0 online platform for our API security. We’ll need to log in (or sign up) at the website, define our API in the Auth0 system, and collect key authentication parameters that we’ll need in order to access the secured API (for example, our access token). We’ll also learn how to validate access tokens with the JWT.io website.

Once we have that taken care of, we can modify our API service to support secure connections, and then we can test that using the access token supplied by Auth0.

You can find more information on Auth0 and the latest features in this link.

Validating your Access Token

Access tokens from the Auth0 website are based on the JSON Web Token (JWT) specifications (RFC7519). JWTs are encoded tokens that contain three parts: a header, a body, and a signature. You can actually decode the contents of a JSON Web Token too. This can come in handy when you want to validate or debug a token that doesn’t seem to be working as expected.

You’ll find a JWT decoder at the JWT.io website, which is operated by Auth0.

Supporting Machine-to-Machine Security

Adding support for machine-to-machine (M2M) security to your API takes just a few steps. You need to add a few modules to your project that contain the functionality to communicate with OAuth providers and evaluate JWTs. You also need to modify your own API service interface to look for and process JWTs when they appear. Finally, you need to import the OAuth authentication parameters you collected from the third-party provider.

This wraps up the process of securing your API using the OAuth protocol and the Auth0 provider service.

--

--

Aditi Lonhari
Aditi Lonhari

Written by Aditi Lonhari

The mind is everything. What you think, you become!

No responses yet