OpenAPI’s security schemes are fundamentally just declarations of intent, not enforcement mechanisms.

Let’s look at how you define API key authentication, which is a common way to secure your endpoints. The key is to tell clients how they should authenticate before they even attempt to call your API.

Imagine you have an API that requires an API key to be sent in a header. Here’s how you’d declare that in your OpenAPI specification (formerly known as Swagger):

openapi: 3.0.0
info:
  title: My Secure API
  version: 1.0.0
paths:
  /items:
    get:
      summary: Get a list of items
      security:
        - ApiKeyAuth: [] # This is the crucial line
      responses:
        '200':
          description: A list of items
components:
  securitySchemes:
    ApiKeyAuth: # This name must match the one in the path definition
      type: apiKey
      in: header # Or 'query' or 'cookie'
      name: X-API-Key # The actual name of the header/query parameter/cookie

In this example:

  • securitySchemes is where you define all your security mechanisms.
  • ApiKeyAuth is a custom name for this specific scheme. You can name it anything, but it’s good practice to be descriptive.
  • type: apiKey tells OpenAPI this is an API key.
  • in: header specifies that the API key should be sent in the HTTP request headers. Other common values are query (for query parameters) and cookie (for cookies).
  • name: X-API-Key is the exact name of the header (or query parameter, or cookie) that your API expects. This is what the client will use.

Now, in the paths section, under the get operation for /items, we have security: - ApiKeyAuth: []. This line references the ApiKeyAuth scheme defined in components/securitySchemes. The empty array [] means there are no specific scopes required for this particular operation.

When a client tool (like Swagger UI, Postman, or an SDK generator) reads this spec, it understands that for the /items GET request, it needs to prompt the user for an API key and send it in the X-API-Key header.

This is what the security declaration looks like in action within the paths object. It’s a direct reference to a defined securityScheme.

If you wanted the API key to be in a query parameter instead, you’d change the in field:

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: query # Changed from header
      name: api_key # Changed the name to a common query param convention

And the path declaration would remain the same:

paths:
  /items:
    get:
      summary: Get a list of items
      security:
        - ApiKeyAuth: []
      responses:
        '200':
          description: A list of items

The client would then expect the request to look like GET /items?api_key=YOUR_API_KEY.

The security property can also be defined at the root level of your OpenAPI document. If you do this, it applies to all operations by default, unless an operation explicitly overrides it.

openapi: 3.0.0
info:
  title: My Secure API
  version: 1.0.0
# Global security requirement
security:
  - ApiKeyAuth: [] # Applies to all paths/operations by default

paths:
  /items:
    get:
      summary: Get a list of items
      # This operation inherits ApiKeyAuth from the root level
      responses:
        '200':
          description: A list of items
  /users:
    post:
      summary: Create a new user
      # This operation also inherits ApiKeyAuth
      responses:
        '201':
          description: User created

If an operation doesn’t want to use the global security scheme, or wants to use a different one, it would have its own security entry that either omits ApiKeyAuth or lists other schemes.

The most surprising thing about OpenAPI security schemes is how little they actually secure anything on their own; they are purely descriptive contracts for clients and documentation generators. The actual enforcement of checking that X-API-Key header (or query param, or cookie) and validating its value is entirely up to your API server’s implementation. Without server-side logic to read and validate the key, the security declaration is just flavor text.

If you define your API key using in: cookie, the name field refers to the cookie’s name, and the client tool will expect to set and send that cookie. The path and domain attributes of the cookie are not specified in OpenAPI, so your server implementation would need to handle setting those appropriately if it were also responsible for issuing the cookie.

Next, you’ll likely want to explore how to define rate limiting using OpenAPI.

Want structured learning?

Take the full Swagger course →