Skip to content

Commit 8d2e174

Browse files
Merge pull request #99 from specmatic/operation_level_security_scheme
chore: update authentication support documentation
2 parents b69fa79 + 4be25fe commit 8d2e174

File tree

2 files changed

+182
-85
lines changed

2 files changed

+182
-85
lines changed

docs/features/authentication.mdx

Lines changed: 181 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,177 @@
11
---
22
sidebar_position: 1
3+
title: Authentication
34
---
5+
46
# Authentication
57

6-
Most APIs have some form of security (authentication and authorization). Specmatic reads [OpenAPI Security Schemes](https://spec.openapis.org/oas/v3.0.1#security-scheme-object) in your API Specifications to come up with appropriate request parameters.
7-
Specmatic supports the following security schemes:
8+
Most APIs use authentication (and sometimes authorization). Specmatic reads authentication requirements from your OpenAPI specification and sends the required headers or parameters during contract tests.
9+
10+
Specmatic supports these OpenAPI security schemes:
11+
812
- **OAuth2**
913
- **API Key**
10-
- **Bearer**
14+
- **HTTP Bearer**
15+
- **HTTP Basic**
16+
17+
## How Specmatic chooses authentication for a request
18+
19+
Specmatic reads the OpenAPI `security` requirement for each operation.
20+
21+
OpenAPI allows authentication to be defined at two levels:
22+
23+
- **Global level**: top-level `security` (applies to all operations by default)
24+
- **Operation level**: `security` inside a specific endpoint/method (applies only to that operation)
25+
26+
### Precedence rule
27+
28+
If both are present, **Specmatic gives preference to the operation-level security scheme**.
29+
30+
This is useful when most endpoints use one auth scheme, but some endpoints use a different one.
31+
32+
### Example: Global security with operation-level override
33+
34+
```yaml
35+
openapi: 3.0.1
36+
37+
security:
38+
- BearerAuth: [] # Global default
39+
40+
paths:
41+
/products:
42+
get:
43+
security:
44+
- basicAuth: [] # Overrides global for GET /products
45+
responses:
46+
"200":
47+
description: OK
48+
```
49+
50+
In this example:
51+
52+
- Most endpoints use `BearerAuth` (global)
53+
- `GET /products` uses `basicAuth` (operation-level override)
54+
55+
## Providing real auth credentials for contract tests
56+
57+
When contract tests run against an environment that requires valid credentials, Specmatic needs real tokens or keys.
58+
59+
You can provide them in two ways:
60+
61+
- **Environment variables**
62+
- **`specmatic.yaml` security configuration** (recommended; can read from environment variables)
63+
64+
### Important: Security scheme names must match the OpenAPI spec
65+
66+
The name you configure must match the security scheme name in `components.securitySchemes`.
67+
68+
For example, if your OpenAPI spec contains:
69+
70+
```yaml
71+
components:
72+
securitySchemes:
73+
oAuth2AuthCode:
74+
type: oauth2
75+
flows:
76+
authorizationCode:
77+
authorizationUrl: https://example.com/auth
78+
tokenUrl: https://example.com/token
79+
scopes: {}
80+
```
81+
82+
Then the configured name must be `oAuth2AuthCode`.
83+
84+
## Recommended: Configure auth in `specmatic.yaml`
85+
86+
You can configure auth values under the OpenAPI test configuration in `specmatic.yaml`.
87+
88+
```yaml
89+
specs:
90+
- spec:
91+
id: orderApiSpec
92+
securitySchemes:
93+
oAuth2AuthCode:
94+
type: oauth2
95+
token: ${OAUTH_TOKEN:OAUTH1234}
96+
basicAuth:
97+
type: basicAuth
98+
token: ${BASIC_AUTH_TOKEN:dXNlcjpwYXNzd29yZA==}
99+
apiKeyAuth:
100+
type: apiKey
101+
token: ${API_KEY:APIKEY1234}
102+
```
103+
104+
This lets you:
105+
106+
- Map each OpenAPI security scheme to a token/key
107+
- Read values from environment variables
108+
- Provide defaults for local testing
109+
110+
## Environment variable values: what to provide
11111

12-
## Testing with real auth
13-
To run contract tests in environments which require a valid security token present in the request, we can define environment variables which hold these valid tokens/api keys.
112+
Provide the **raw credential value**, not the full HTTP header.
14113

15-
The environment variable should match the name of the security scheme defined in the open api specification.
114+
Specmatic will construct the correct header format automatically based on the security scheme type.
16115

17-
When contract tests are executed, Specmatic will look for an environment variable with the same name as that of the security scheme. If such an environment variable exists, Specmatic will use it appropriately (based on the security scheme) while making an HTTP request.
116+
Examples:
117+
118+
- **OAuth2 / Bearer**: provide only the token (without `Bearer `)
119+
- **Basic**: provide only the Base64 credential (without `Basic `)
120+
- **API Key**: provide only the key value
121+
122+
## Security scheme examples
18123

19124
### OAuth2
20-
Here's an example of an OAuth2 security scheme in the open api specification:
125+
126+
OpenAPI example:
21127

22128
```yaml
23129
components:
24130
securitySchemes:
25131
oAuth2AuthCode:
26132
type: oauth2
27-
description: For more information, see https://example.com/docs/oauth
28133
flows:
29134
authorizationCode:
30135
authorizationUrl: https://api.example.com/oauth/authorize
31-
tokenUrl: https://api.example.com/api/oauth/token
136+
tokenUrl: https://api.example.com/oauth/token
32137
scopes:
33138
users:read: Read user information
34-
users:write: Modify user information
35-
im:read: Read messages
36-
im:write: Write messages
37-
im:history: Access the message archive
38-
search:read: Search messages, files, and so on
39139
```
40140

41-
To use a real OAuth2 token in contract tests, an environment variable with the name of the security scheme needs to be defined.
141+
Configure:
142+
143+
- Security scheme name: `oAuth2AuthCode`
144+
- Token value: `abc123` (not `Bearer abc123`)
42145

43-
For example, in the above case, we would define an environment variable named `oAuth2AuthCode`. Assuming that Authorization header value has to be `Bearer abc123`, set the value of this environment variable to `abc123` (leaving out the `Bearer ` prefix).
146+
Specmatic sends:
147+
148+
- `Authorization: Bearer abc123`
44149

45150
### API Key
46-
Here's an example of a Bearer security scheme in the open api specification:
151+
152+
OpenAPI example:
47153

48154
```yaml
49155
components:
50156
securitySchemes:
51-
ApiKeyAuthHeader:
52-
type: apiKey
53-
in: header
54-
name: X-API-KEY
157+
ApiKeyAuthHeader:
158+
type: apiKey
159+
in: header
160+
name: X-API-KEY
55161
```
56162

57-
To use a real API key header in contract tests, an environment variable with the name of the security scheme needs to be defined.
163+
Configure:
164+
165+
- Security scheme name: `ApiKeyAuthHeader`
166+
- Token value: `my-api-key-abc123`
58167

59-
For the above example, define an environment variable named `ApiKeyAuthHeader` having the API key as its value.
168+
Specmatic sends:
60169

61-
For example, in the above case, we would define an environment variable named `ApiKeyAuthHeader`. Assuming that Authorization header value has to be `my-api-key-abc123`, set the value of this environment variable to `my-api-key-abc123`.
170+
- `X-API-KEY: my-api-key-abc123`
62171

63-
### Bearer
64-
Here's an example of a Bearer security scheme in the open api specification:
172+
### HTTP Bearer
173+
174+
OpenAPI example:
65175

66176
```yaml
67177
components:
@@ -71,13 +181,18 @@ components:
71181
scheme: bearer
72182
```
73183

74-
To use a real bearer auth token in contract tests, an environment variable with the name of the security scheme needs to be defined.
184+
Configure:
185+
186+
- Security scheme name: `BearerAuth`
187+
- Token value: `abc123` (not `Bearer abc123`)
188+
189+
Specmatic sends:
75190

76-
For example, in the above case, we would define an environment variable named `BearerAuth`. Assuming that Authorization header value has to be `Bearer abc123`, set the value of this environment variable to `abc123` (leaving out the `Bearer ` prefix).
191+
- `Authorization: Bearer abc123`
77192

78-
### Basic Authentication
193+
### HTTP Basic
79194

80-
Here's an example of a Bearer security scheme in the open api specification:
195+
OpenAPI example:
81196

82197
```yaml
83198
components:
@@ -87,45 +202,57 @@ components:
87202
scheme: basic
88203
```
89204

90-
To use a real basic auth token in contract tests, an environment variable with the name of the security scheme needs to be defined.
205+
Configure:
91206

92-
For example, in the above case, we would define an environment variable named `BasicAuth`. Assuming that Authorization header value has to be `Basic abc123`, set the value of this environment variable to `abc123` (leaving out the `Basic ` prefix).
207+
- Security scheme name: `BasicAuth`
208+
- Token value: Base64 of `username:password`
209+
- Example: `dXNlcjpwYXNzd29yZA==` (`user:password` in Base64)
93210

211+
Specmatic sends:
94212

95-
## Testing with mock auth
213+
- `Authorization: Basic dXNlcjpwYXNzd29yZA==`
96214

97-
While Specmatic supports testing with real authentication as seen above, in a component / contract test like setup, it is recommended to isolate the SUT (System Under Test) which is your service from other dependencies such as auth providers. So at a contract / component test level it is sufficient to validate if an API implementation / service accepts the security parameters it is advertising in its API Specification. However, it is not necessary to validate if the security itself is working. That is for later stages of tests where you can hook up a security service dependency such as DB, OAuth provider, etc.
215+
## Testing with mock auth (recommended for contract/component tests)
98216

99-
So for Contract Tests we recommend having a “Test Security Configuration” where you are still exercise your security plumbing, however not actually fetching real user information. This is similar to running an in-memory DB in test setup instead of running a real DB in CI. Below are some examples of the same.
217+
For contract tests, it is usually best to isolate your application from real auth providers (OAuth servers, DB-backed auth, identity services).
100218

101-
### OAuth2
219+
At this stage, the main objective is to validate that:
220+
221+
- Your API advertises the correct auth requirements in the OpenAPI spec
222+
- Your application accepts the expected auth headers/parameters
223+
- Your service behavior matches the contract
224+
225+
You usually do **not** need to validate real token issuance/verification in contract tests. That is better covered in higher-level integration or end-to-end tests.
102226

103-
Please refer to this [sample API](https://github.com/specmatic/specmatic-order-contracts/blob/main/io/specmatic/examples/store/openapi/api_order_with_oauth_v3.yaml) specification which leverages [OAuth2](https://spec.openapis.org/oas/v3.0.1#implicit-oauth2-sample) to protect all endpoints that add, modify or delete data.
227+
### Recommended approach
104228

105-
#### Wiring up dummy / mock authentication
229+
Use a test security setup that:
106230

107-
![Specmatic Sample Application to demonstrate OpenAPI OAuth2 security scheme support](/img/SpecmaticOAuth.gif)
231+
- Checks header presence and format
232+
- Optionally creates a dummy authenticated principal/user
233+
- Avoids calling real auth dependencies
108234

109-
Please refer to [sample springboot application](https://github.com/specmatic/specmatic-order-api-java-with-oauth) that implements the [API](https://github.com/specmatic/specmatic-order-contracts/blob/main/io/specmatic/examples/store/openapi/api_order_with_oauth_v3.yaml) we saw above.
235+
This is similar to using an in-memory database in tests instead of a production database.
110236

111-
### API Key Authentication
237+
## Example: Different auth schemes per operation
112238

113-
Please refer to this [sample API](https://github.com/specmatic/specmatic-order-contracts/blob/main/io/specmatic/examples/store/openapi/api_order_v3.yaml) specification which leverages [ApiKeyAuth](https://spec.openapis.org/oas/v3.0.1#api-key-sample) to protect all endpoints that add, modify or delete data.
239+
Specmatic supports different auth schemes for different endpoints in the same OpenAPI specification.
114240

115-
#### Wiring up dummy / mock authentication
241+
Example use case:
116242

117-
Please refer to [sample springboot application](https://github.com/specmatic/specmatic-order-api-java) that implements the [API](https://github.com/specmatic/specmatic-order-contracts/blob/main/io/specmatic/examples/store/openapi/api_order_v3.yaml) we saw above.
243+
- `POST` endpoints use **OAuth2**
244+
- `GET` endpoints use **Basic Auth**
245+
- `DELETE` endpoints use **API Key**
118246

119-
This has two API security configurations
120-
* [Production Security Config](https://github.com/specmatic/specmatic-order-api-java/blob/main/src/main/java/com/store/config/SecurityConfig.kt) - This fetches user from the DB based on api token in request header
121-
* [Test Security Config](https://github.com/specmatic/specmatic-order-api-java/blob/main/src/test/java/com/store/config/TestSecurityConfig.kt) - This always returns a dummy user principal. However, rest of the code such as reading the authentication token from header etc. are still tested.
247+
Specmatic reads the operation-level `security` entries and sends the appropriate auth header for each request.
122248

123-
So when you run the [ContractTest](https://github.com/specmatic/specmatic-order-api-java/blob/main/src/test/java/com/store/ContractTest.java) it will still exercise your security plumbing (does the application accept the proper header name, datatype, etc.).
249+
Sample project:
250+
- [specmatic-order-api-java-with-oauth](https://github.com/specmatic/specmatic-order-api-java-with-oauth)
124251

125-
This is just an example of how we can wire up security configurations for test and production environments. Even in SpringBoot you can leverage other techniques such as [Spring Profiles](https://docs.spring.io/spring-boot/docs/1.2.0.M1/reference/html/boot-features-profiles.html) to achieve the same effect.
252+
## Tips
126253

127-
The same can be achieved in almost any programming language and stack.
128-
* Dot Net - Register a custom [AuthenticationHandler](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationhandler-1?view=aspnetcore-7.0) for mock authentication in tests
129-
* Node.js - Switch auth middleware based on ```process.env.NODE_ENV```
254+
- Security scheme names in `specmatic.yaml` must match names in `components.securitySchemes`.
255+
- If an operation defines `security`, it overrides global `security`.
256+
- Use environment variables in `specmatic.yaml` to switch credentials across local, CI, and test environments.
257+
- For contract tests, prefer mock/dummy auth unless your test specifically needs real auth.
130258

131-
In general the overall idea is to inject a mock authentication mechanism while running Specmatic Contract Tests

0 commit comments

Comments
 (0)