Security
- API Keys
- HTTP Authentication
- OAuth 2
- OpenID Connect
- Mutual TLS
- Applying Security to Paths
- Applying Security Globally
- Overriding Path Security
- Scopes
OpenAPI uses the term “security scheme” to cover both authentication and authorization schemes.
- Authentication: Who is this user, are they who they say they are.
- Authorization: What data can this user see, what actions can they take.
OpenAPI v3.1 lets you describe APIs protected using the following security schemes:
- HTTP authentication schemes (anything using the
Authorization
header)- Basic
- Bearer
- Digest
- OAuth (1.0)
- others defined in RFC 7235 and HTTP Authentication Scheme Registry
- API keys in headers, query string or cookies
- OAuth 2.x
- OpenID Connect Discovery
- Mutual TLS
Some of these are very niche and specific forms of security scheme, so we won’t get stuck into all of them. Let’s look at the most common ones for now.
components:
securitySchemes:
# API Key Authentication
ApiKeyHeader:
type: apiKey
in: header
name: X-API-Key
ApiKeyQuery:
type: apiKey
in: query
name: key
# HTTP Authentication
HttpBasicAuth:
type: http
scheme: basic
HttpBearerToken:
type: http
scheme: bearer
JWT:
type: http
scheme: bearer
bearerFormat: JWT
# OAuth 2.0 Authentication
OAuth2ReadWrite:
type: oauth2
flows:
authorizationCode:
scopes:
read: Grants read access
write: Grants write access
authorizationUrl: https://example.com/oauth/authorize
tokenUrl: https://example.com/oauth/token
refreshUrl: https://example.com/oauth/refresh
This is 90% of what you’ll bump into in the wild when it comes to security schemes.
API Keys #
in: header
API Keys in headers are often stored in some arbitrary header name like X-API-Key
, which is frowned upon but is still commonly used.
ApiKeyHeader:
type: apiKey
in: header
name: X-API-Key
That would describe a request that looks like this:
GET /bookings
Host: api.example.com
X-API-Key: <your-api-key>
Instead of specifically using
X-
headers, you can create custom headers with your company name at the start likeStipe-API-Key
if something is specific to you. If it’s not then it’s better to use the standard HTTP headers so generic tooling knows how to work with them out of the box.
in: query
API Keys in query
are also common, but best avoided for other reasons: it’s incredibly insecure.
GET /bookings?key=<your-api-key>
Host: api.example.com
The OWASP API Security Top 10 lists Broken Authentication as one of the top security issues for APIs, citing specifically “Sends sensitive authentication details, such as auth tokens and passwords in the URL.”
Generally the “API Key” scheme is best used for legacy API security schemes, which are being deprecated and replaced with better security schemes.
HTTP Authentication #
HTTP Authentication is a widely used security scheme in OpenAPI. It allows you to authenticate and authorize users based on the Authorization
header in the HTTP request.
Basic Authentication #
One of the most common HTTP authentication schemes is Basic Authentication. It involves sending the username and password in the Authorization
header, encoded in Base64. Here’s an example:
HttpBasicAuth:
type: http
scheme: basic
To make a request using Basic Authentication, the Authorization
header would look like this:
GET /bookings
Host: api.example.com
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
That dXNlcm5hbWU6cGFzc3dvcmQ=
is base64 encoded username:password
, which is exactly why this security scheme is considered unwise for sending long-lived credentials. Short-lived frequently changing tokens might be less problematic especially if being sent over TLS.
Bearer Token Authentication #
Bearer Token Authentication is another popular HTTP authentication scheme. It involves sending a token in the Authorization
header, prefixed with the word Bearer
. Here’s an example:
HttpBearerToken:
type: http
scheme: bearer
To make a request using Bearer Token Authentication, the Authorization
header would look like this:
GET /bookings
Host: api.example.com
Authorization: Bearer <your-token-here>
This token is in plain text, but it’s usually randomly generated and short lives so it’s not so concerning if somebody nabs it, especially if you’re using JWT or OAuth 2 which operates over bearer.
JWT Authentication #
Using a JWT (JSON Web Token) allows for more advanced authentication scenarios and includes additional information in the token payload. Here’s an example:
JWT:
type: http
scheme: bearer
bearerFormat: JWT
In OpenAPI a JWT security scheme is actually just Bearer Token Authentication again, with bearerFormat: JWT
letting documentation tools know to mention that the token is a JWT. It doesn’t make much difference, its just how the token was generated before it was passed to the API.
To make a request using JWT Authentication, the Authorization
header would look similar to Bearer Token Authentication:
GET /bookings
Host: api.example.com
Authorization: Bearer <your-jwt-here>
OAuth 2 #
OAuth 2 is a widely used authorization framework that allows users to grant third-party applications access to their resources without sharing their credentials. In OpenAPI, you can describe OAuth 2 authentication using the following example:
OAuth2ReadWrite:
type: oauth2
flows:
authorizationCode:
scopes:
read: Grants read access
write: Grants write access
authorizationUrl: https://example.com/oauth/authorize
tokenUrl: https://example.com/oauth/token
refreshUrl: https://example.com/oauth/refresh
In this example, the OAuth2ReadWrite
security scheme represents OAuth 2 authentication. It uses the authorizationCode
flow, which is one of the most common flows in OAuth 2. The scopes
section defines the available scopes for this security scheme, such as read
and write
. The authorizationUrl
specifies the URL where users can authorize the application, the tokenUrl
is used to obtain access tokens, and the refreshUrl
is used to refresh expired tokens.
To apply OAuth 2 authentication to a specific operation, you can use the security
keyword and specify the security scheme and scopes:
paths:
/bookings:
get:
security:
- OAuth2ReadWrite: [read]
In this example, the /bookings
path requires the OAuth2ReadWrite
security scheme with the read
scope for the GET
operation.
OpenID Connect #
OpenID Connect is an identity layer built on top of OAuth 2.0 that allows clients to verify the identity of end-users based on the authentication performed by an authorization server. It provides a standardized way to authenticate users and obtain their identity information, such as name, email, and profile picture.
In OpenAPI v3.1, you can describe OpenID Connect authentication using the following example:
OpenIDConnect:
type: openIdConnect
openIdConnectUrl: https://example.com/.well-known/openid-configuration
In this example, the OpenIDConnect
security scheme represents OpenID Connect authentication. The openIdConnectUrl
specifies the URL where the OpenID Connect provider’s configuration can be found. This configuration includes important information such as the authorization endpoint, token endpoint, and public keys used for verifying ID tokens.
To apply OpenID Connect authentication to a specific operation, you can use the security
keyword and specify the security scheme:
paths:
/bookings:
get:
security:
- OpenIDConnect: []
In this example, the /bookings
path requires the OpenIDConnect
security scheme for the GET
operation.
OpenID Connect is a powerful authentication mechanism that allows you to leverage existing identity providers and provide a seamless login experience for your users. It is widely adopted and supported by major identity providers such as Google, Microsoft, and Okta.
Mutual TLS #
Mutual TLS (Transport Layer Security) is an authentication type in OpenAPI that provides a secure way to authenticate both the client and the server. It involves the exchange of digital certificates between the client and the server to establish a trusted connection, meaning it’s commonly used in scenarios where strong authentication and secure communication are required: banking, healthcare, and other sensitive industries.
With mutual TLS authentication, the client presents its own certificate to the server during the TLS handshake process. The server then verifies the client’s certificate against its trusted certificate authority (CA) to ensure the client’s identity. Similarly, the server presents its own certificate to the client, and the client verifies the server’s certificate.
There’s not much to it in OpenAPI v3.1.
MutualTLS:
type: mutualTLS
This is basically just a flag to let clients know they’ll need to set up Mutual TLS or no connections are going to work, but the specifics of that happen outside of OpenAPI. It takes a bit of work, but this authentication method adds an extra layer of security by ensuring that both the client and the server are authenticated before any data is exchanged. It helps prevent unauthorized access and protects against man-in-the-middle attacks.
Applying Security to Paths #
The security schemes are just setting there until they are applied to paths in your OpenAPI document. You can use the security
keyword at the path level to do this:
openapi: 3.1.0
paths:
/bookings:
get:
security:
- HttpBearerToken: []
In this example, the /bookings
path requires the HttpBearerToken
security scheme for all requests.
openapi: 3.1.0
paths:
/bookings:
get:
security:
- ApiKeyQuery: []
- HttpBearerToken: []
In this example either the ApiKeyHeader
or HttpBearerToken
security schemes, meaning a client could use either. This is great when one method is being deprecated.
Security schemes can also be applied as an AND
:
openapi: 3.1.0
paths:
/bookings:
get:
security:
- ApiKeyHeader: []
HttpBearerToken: []
This example would only pass if both the ApiKeyHeader
and HttpBearerToken
are passed.
Applying Security Globally #
To avoid needing to set similar security
keywords on every path (and possibly missing a few) you can apply security globally.
openapi: 3.1.0
security:
- HttpBearerToken: []
paths:
/bookings:
get:
# ...
This will now be applied to all paths unless turned off.
Overriding Path Security #
Globally applied rules can be overridden with a path-level security keyword.
openapi: 3.1.0
security:
- HttpBearerToken: []
paths:
/bookings:
get:
security: [] # no security
All other paths will continue to be secured regardless of what order they’re in.
Scopes #
You might have noticed the empty array showing up: HttpBearerToken: []
. This empty array is where “scopes” go.
Scopes allow you to define fine-grained permissions within some types of security schema that support them, and in OpenAPI v3.1 that means OAuth 2 and OpenID Connect.
Each security scheme can have its own set of scopes, which can be used to control access to specific resources or actions.
OAuth2ReadWrite:
type: oauth2
flows:
authorizationCode:
scopes:
read: Grants read access
write: Grants write access
In this example, the OAuth2ReadWrite
security scheme has two scopes: read
and write
, which can be used to check certain actions, maybe limiting GET
to read
and POST
, PUT
, PATCH
, and DELETE
to write.
To apply scopes to specific operations, you can use the security
keyword and fill in that array at the end with the relevant scopes.
paths:
/bookings:
get:
security:
- OAuth2ReadWrite: [read]
post:
security:
- OAuth2ReadWrite: [write]
You can get a lot more advanced with scopes than this, with some API developers breaking down the API into manageable chunks. Here are just a few from the Shopify API as an example:
read_orders
write_orders
read_content
write_content
read_customers
write_customers
read_customer_payment_methods
write_customer_payment_methods