Decouple User Identity from API Design to Build Scalable Microservices

Decouple User Identity from API Design to Build Scalable Microservices

Decouple User Identity from API Design to Build Scalable MicroservicesHow does one go about securing APIs, microservices, and websites? One way to do this is by focusing on the identity — knowing who the caller is, and what the caller is allowed to do with your data. Too often, though, providers rely too heavily on user social identity, pairing it way too closely with the design of their APIs.

As OAuth doesn’t authenticate by itself, the way these flows are structured means that API access often ultimately relies on user social logins, which is an unfavorable dependency that actually decreases API security and scalability. APIs are far better secured with a proxy in-between the API and authentication mechanism, utilizing scopes that delineate the type of access that the API grants.

In this post we’ll see why APIs and microservices should decouple user identity from their designs, and how to go about this implementation. We’ll review some sample flows, and briefly walk through how OAuth scopes can be used to create a more valuable, knowledgeable API. Following these cues, the end result will emboss state of the art Identity and Access Management (IAM) practices within actual API design, in effect utilizing identity data to secure the entire API lifecycle.

AAA

When an API call is made, we must know who made the request, and if they are allowed to read and access the requested data. Identity and Access Management (IAM) is also described as AAA; an important initialism made up of:

  • Authentication: Validation that the user is who they say they are.
  • Authorization: Validation of the user, their application, and privileges.
  • Auditing: Accounting for user behavior, logging metadata like what is accessed, when it’s accessed, with what device, and more.

Caching user logs is great, but it doesn’t prevent malformed requests to the server from the onset. We don’t want to waste resources, so verifying requests must be processed as early on as possible in the code pipeline. So, typically you block this with a proxy. BUT how do you make sure that the proxy knows what to do? How do we instruct the proxy to decipher what user or application is accessing the data, and what data they are allowed to access?

This post was modeled after this talk by Jacob Ideskog of Curity. Watch him describe how to decouple user identity from API design:

Overview of the OAuth Flow

You may be thinking, just use OAuth, problem solved. Yes, OAuth is a necessary protocol within the security workflow — but OAuth cannot authenticate — a separate server must tell OAuth who the user is. To understand where we’re heading, here’s a quick overview of a simple OAuth flow. To review, the actors are:

  • Resource Owner (RO): The user
  • Client: The application, mobile device, server, or website requesting data from the API
  • Authentication Server: The login service that authenticates users
  • OAuth Server (AS): Also called the Authorization Server
  • Resource Server (RS): The API providing data

Let’s assume that we have created a mail server with an API that provides information so that a third party app client can sort emails in an improved way. In our walkthrough we’ll assume that the app uses Google+ as an authentication service. There are variants of these flows, but a simple OAuth flow for this scenario would be:

  1. The Client first requests accesses the OAuth Server.
  2. The OAuth Server next delegates authentication responsibility to a third party Authentication Server (Google).
  3. The User enters credentials with the Authentication Server to authenticate.
  4. The Authentication Server tells the OAuth Server the authentication was successful, issuing a Token to the OAuth Server.
  5. The OAuth Server sends the Token to the Client.
  6. The Client uses the Token to access resources from the Resource Server.
  7. The Resource Server verifies with the OAuth Server that the Token is valid.
  8. The Resource Server (API) then sends the data to the Client app.
Standard OAuth Flow Gif

Standard OAuth flow

For more in-depth analysis, see our Deep Dive into OAuth and OpenIDConnect

Describing Login Information

Considering these three important actors (Resource Server (the API), Authorization Server (OAuth), Authentication Server (Login)), we need to know who is requesting access, which type of application is requesting access, and the privileges that user has. Accounting for the login process is important as it completes the picture of these flows.

So, how do you describe login information? You do so with Federation — a concept that has been around for sometime. When you describe authentication between federated systems, typically you log the following:

  • subject: who logged in
  • auth_time: when the login occurred
  • acr: Short for Authentication Context Class Reference, this stipulates how the login happened. This is where the communicating parties can describe and understand the form of authentication used for the login, whether it be Facebook, Google+, or others.

Often, with this information in hand, an API will simply look at the acr record and give the appropriate resources based on it. When this happens though, the Authentication Server and OAuth Server essentially delegate responsibility to the social login, in this case Google+.

The great fallacy in this approach is that it creates a direct dependency from your API to a social login. Having a workflow rely on a single authenticator like Google can create assumptions in standard behavior that completely drop out if you decide to use another login instead, like Facebook or Twitter. And what happens when you have 150+ microservices deployed all relying on authentication in this way? Scaling microservices that depend on a single social authenticator can create unnecessary headaches.

The Solution is in Abstraction

To solve this, what we need to create is a standardized abstraction method. The solution is creating a wall between the API and the Authentication Server and OAuth Server. If you spice OAuth up, and pair it with a service for authentication you have the tools to do this.

Keep in mind there are thousands of unique ways to authenticate a user — pin code, phone number, voice, biometric data — thus it is impossible to create a standard that combines all authentication styles. However, using this concept, we can track and employ useful identity data with OAuth Tokens. OAuth defines its tokens as containing such data points:

  • subject: The user
  • client ID: The application
  • scope: What the token can be used for
  • expiration: How long the access token is valid

Designing an API with Scopes from the Bottom Up

Located within the OAuth token, scope is an interesting data point that you’ve likely used before. Scope specifies the extent of tokens and are akin to the permissions listed on a consent UI. They are extremely useful, as scopes can be used to delineate API access tiers. Furthermore, OAuth doesn’t specify that you have to give the same scopes that you are requesting — if the scope changes you must simply notify the client/user. For access management designers, this grants us a lot of power and flexibility in how we handle scopes and identity. We’ll see that building an API with scopes hardwired into the design can be extremely helpful.

Let’s build a sample API to see what we’re talking about. Let’s say we are designing an Articles API that taps into a newspaper platform. The API provides access to customers, who are reading the content, as well as to employees, who are writing the content. The newspaper also has a premium subscription plan, so the API should reflect this higher access tier as well. Essentially, the API must provide the ability to read, add, delete, and update articles.

On the other end, ArticleReader is an application that consumes the Articles API. You can think of ArticleReader as the client in our OAuth flow. As it’s a reader client, it will be limited in scope without editing capability.

So how do we create permissions? To do so means we define the scopes in the API. The beautiful magic here is that we assign these scopes with different strengths as follows:

Scope Strength Behavior
articles_regular_read Weak Allows social authentication
articles_premium_read Middle Social authentication is ok, but must be a customer
articles_write Strong The user must be an employee, and login must be done over integrated network.

Let OAuth Filter These Scopes

Next we let the OAuth server filter API access based on these scopes. Since we can change our scopes throughout the process, when ArticleReader sends a request with an articles scope, a new flow would look like this:

  1. The ArticleReader Client makes request to the OAuth Server sending a basic read scope.
  2. The OAuth Server next delegates authentication responsibility to a third party Authentication Server (Google).
  3. The User enters credentials with Google, the Authentication Server, to authenticate.
  4. The Authentication Server tells the OAuth Server the authentication was successful, and sends an OAuth Token. Within the token is information that will affect the scopes, namely the ACR (which in this case is social) and the subject (the username).
  5. The OAuth Server checks its Customer Database to see if the username is in fact a customer.
  6. In this case it does find the username among the customer files, and thus grants the client an Access Token with both the articles_regular_read and articles_premium_read scopes. Note the OAuth Server also tells the Client what scopes it returned, since these were different from the initial scope that was requested. However, keep in mind that the Client must under no circumstances dissect the actual token — it is only meant for the API to consume.
  7. The ArticleReader Client then sends the Access Token to the Articles API Resource Server. At this point we now know many more things than simply the social ACR.
  8. The Resource Server verifies with the OAuth Server that the Token is valid.
  9. The Resource Server (API) then sends the data to the Client app.
Updated OAuth Flow Using Scopes

Updated OAuth flow using scopes

Using this flow, the API becomes much more knowledgeable. By returning different scopes in step 6, we give the client the ability to custom tailor the UI, and enable or disable certain functions. Even better is that since the power of the scope told it that sufficient strength was used during authentication, the API now doesn’t even care which authentication was originally made. Empowered with data on the client, the user, and the access granted by the scopes, it can effectively filter data to the proper channels using an improved provisioning schematic.

The Proxy Accepts Only Valid Requests

The last step in decoupling social authentication from API design is constructing a proxy to separate our API from the authentication mechanism. The final result is a proxy that only allows access when the following criteria are met:

  • The token exists
  • The token is valid
  • The token contains one or more scopes. For the Articles API, that would be one or more of the following:
    • articles_regular_read
    • articles_premium_read
    • articles_write

Now we have a more secure, strict API front end that only allows access once these three rules are met, blocking unregistered users in the proxy.

Conclusion: Separate the API From Authentication

In order to build scalable API infrastructure that is ideal for microservices, you must design your APIs in a way that separates them from authentication. Using scopes to map the permissions, and defining them in your API, can create a robust platform that better protects and informs you as an API provider.

Benefits of this approach also include:

  • Overall API security is improved with abstraction;
  • API identity control now maps your access tiers, enabling easy enforcement for freemium business models;
  • This pattern is simple to grasp and implement;
  • Constructing scopes into API design rather than third party authentication means freedom of any authentication method without bothering the APIs with all the details;
  • Can help in separating private, public, partner APIs — complementing platform strategy and adding business potential;
  • Could be used to inform usage analytics;
  • As marketing departments have high demand on smooth customer journeys, this provides a quicker time to market when it comes to authentication.

But perhaps the most critical point is that one and only one pattern is needed for microservices design. This increases the ability to not only build APIs, but easily share identity knowledge across an organization, increasing the service maintainability over time. Authentication is a moving target, whereas APIs may not be.