Describing API Security

One feature of OpenAPI that can prove useful to API providers and consumers is the means to describe API security. Providing information about the security that protects a given API and its Operations is useful to humans, as they can understand security restrictions and account for them in their implementation, and for tooling that can generate code or provide features that facilitate submitting authorization parameters.

OpenAPI provides the Security Scheme Object, which contains security definitions that can be referenced either globally or per Operation. Unlike other Component objects, Security Scheme objects are referenced by name using a Security Requirement Object. A security description for a given API or Operation therefore must be defined as a Security Scheme Object as they cannot be declared inline.

Security Scheme objects are referenced as Security Requirements, either globally or by an Operation.

A Security Requirement declared for a given Operation takes precedence over global Security Requirements. A number of security mechanisms are supported. Each mechanism is indicated using the type property, which is shown in the examples below.

There are currently five supported security types, namely:

  • API Keys
  • HTTP Authentication
  • Mutual TLS
  • OAuth 2.0
  • OpenID Connect

Each is discussed in the sections below.

API Keys

An API key is - generally speaking - the API equivalent of a password and is used to supply a secret known only by a given API consumer and the API provider. There are no formal standards that govern API keys and the industry generally relies on accepted best practices for their definition. OpenAPI therefore provides flexibility in how they are defined.

For example, an API provider might use the HTTP header api-key as the means for API consumers to send their API keys with each request. A Security Scheme object can be defined to indicate in which HTTP header the API Key should be sent:

components:
  securitySchemes:
    defaultApiKey:
      description: API key provided in console
      type: apiKey
      name: api-key
      in: header

The API provider can then use the name of this Scheme Scheme object to apply security either globally or for a given Operation (both are shown below for the sake of exemplar):

openapi: 3.1.0
info:
  title: Tic Tac Toe
  description: |
    This API allows writing down marks on a Tic Tac Toe board
    and requesting the state of the board or of individual squares.
  version: 1.0.0
security:
  defaultApiKey: []
paths:
  /board:
    get:
      security:
        defaultApiKey: []

This method of referencing Security Scheme objects is valid for all types. The array shown as the value of defaultApiKey above is populated for OAuth Flow and OpenID Connect objects, which have some additional features that are discussed below. An empty array is provided in all other cases.

Please note that different tools may resolve Security Requirement objects in referenced documents differently. You should check your tooling provider’s method before assuming a specific resolution approach.

HTTP Authentication

OpenAPI supports HTTP Authentication as defined in RFC7235, which implements the Authorization header as the means to send both the authorization scheme identifier and the parameter in the format Authorization: Basic b3BlbmFwaTppc2dyZWF0. The authorization scheme should be defined in the IANA registry for HTTP Authentication, but API providers can extend this to use custom values if required.

The example below shows a Security Scheme object that specifies both Basic Authentication and Bearer tokens. The Bearer token example includes an additional hint, the bearerFormat property, that gives the API consumer additional information on the format of the token (in this case a JSON Web Token or JWT).

components:
  securitySchemes:
    basicHttpAuthentication:
      description: Basic HTTP Authentication
      type: http
      scheme: Basic
    bearerHttpAuthentication:
      description: Bearer token using a JWT
      type: http
      scheme: Bearer
      bearerFormat: JWT

You’ll note that in both cases the information provided is relatively terse. OpenAPI provides enough information to give humans and tooling the means to understand the basic security requirements, but deployment information such as onboarding or key exchange is out-of-scope.

Mutual TLS

Mutual authentication over TLS (mTLS) is a very common API security approach in verticals such as financial services. This is due to its enhanced security posture through authentication of the HTTP Client at the transport layer.

Defining mTLS in OpenAPI is very simple:

type: mutualTLS

As before the information provided is terse. Establishing the private key infrastructure used to govern communications between the API provider and consumer, together with the associated deployment information such as certificate signing and so on, is beyond the scope of OpenAPI.

OAuth 2.0

OAuth 2.0 is a very popular authorization framework in the API world due to its support for delegated access without giving up End Users credentials to an untrusted application. It is described using a specific object, namely the OAuth Flows Object. This object is provided due to the relative complexity of the protocol compared to other security types, with different grant types representing different mechanisms for obtaining authorization to access protected resources.

The OAuth Flows Object has properties representing the different OAuth 2.0 grant types, each of which references the OAuth Flow Object. The OAuth Flow Object properties describe the Authorization Server URL, Token Endpoint and optionally the permitted OAuth scopes.

The example below shows the properties for the Client Credentials and Authorization Code grant types:

components:
  securitySchemes:
    oauth2Profiles:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: https://learn.openapis.org/oauth/2.0/token
          scopes:
            board:read: Read the board
            board:write: Write to the board
        authorizationCode:
          authorizationUrl: https://learn.openapis.org/oauth/2.0/auth
          tokenUrl: https://learn.openapis.org/oauth/2.0/token
          scopes:
            board:read: Read the board
            board:write: Write to the board

These can then be applied globally or for a given Operation, with a list of required scopes:

openapi: 3.1.0
info:
  title: Tic Tac Toe
  description: |
    This API allows writing down marks on a Tic Tac Toe board
    and requesting the state of the board or of individual squares.
  version: 1.0.0
security:
  oauth2Profiles:
    - board:read
    - board:write
paths:
  /board:
    get:
      security:
        oauth2Profiles: []

Note that if you want to segregate grant types - where, for example, Client Credentials is only supported for a specific Operation - you’ll need to create a separate Security Scheme object that can be applied individually. This is also true if you want to differentiate the available scopes, for example:

components:
  securitySchemes:
    app2AppOauth:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: https://learn.openapis.org/oauth/2.0/token
            # Only reading the board allow with delegated access
            board:read: Read the board
    user2AppOauth:
        authorizationCode:
          authorizationUrl: https://learn.openapis.org/oauth/2.0/auth
          tokenUrl: https://learn.openapis.org/oauth/2.0/token
          scopes:
            # Reads and writes permitted via authorization code flow
            board:read: Read the board
            board:write: Write to the board

These then be declared as separate Security Requirements:

info:
  title: Tic Tac Toe
  description: |
    This API allows writing down marks on a Tic Tac Toe board
    and requesting the state of the board or of individual squares.
  version: 1.0.0

paths:
  /board:
    get:
      security:
        app2AppOauth:
        - board:read
      ...
  /board/{row}/{column}:
    put:
      security:
        user2AppOauth:
        - board:read
        - board:write
      ...

Please refer to our example OpenAPI document for the complete example.

OpenID Connect

The final Security Scheme type is OpenID Connect, which provides information for OpenID Connect Discovery.

OpenID Connect Core is obviously an OAuth 2.0 profile and is supported by some of properties the OAuth Flow Object. However, OpenID Connect is generally more complex than plain OAuth 2.0 and given OpenID Connect Discovery provides a machine-readable format at the discovery endpoint it makes sense to outsource this functionality entirely.

Specifying OpenID Connect is therefore straightforward in that you provide the discovery endpoint in the property openIdConnectUrl:

components:
  securitySchemes:
    openIdConnect:
      type: openIdConnect
      openIdConnectUrl: https://learn.openapis.org/.well-known/openid-configuration

The neat trick here is that you do not need to declare scopes in your OpenAPI document. You can specify scopes in your discovery endpoint, and then use them in your OpenAPI document with the expectation that compatible tooling will have parsed and read them:

openapi: 3.1.0
info:
  title: Tic Tac Toe
  description: |
    This API allows writing down marks on a Tic Tac Toe board
    and requesting the state of the board or of individual squares.
  version: 1.0.0
security:
  openIdConnect:
    - board:read
    - board:write

This approach allows OpenAPI to provide just enough information for humans and tooling, whilst ensuring that OpenID Connect Discovery provides the system-or-record for security-related information.

Summary

In this page we’ve learnt that:

  • API security can be described in OpenAPI.
  • Security properties must be described using a Security Scheme object.
  • A Security Scheme object is referenced either globally or for a given Operation using a Security Requirement.
  • OpenAPI supports a number of built-in security types, with different properties dependent on the type.