Security
OpenAPI 2.0 splits authentication into two parts: security definitions name
the schemes (an API key, OAuth2, HTTP Basic), and security requirements
reference those schemes — document-wide and/or per operation. The panes below
are backed by the test-covered
docs/examples/concepts/security
package.
Declare the schemes
A SecurityDefinitions: block in swagger:meta declares every scheme once; a
Security: block sets the document-wide default requirement that applies to
operations that do not state their own.
// Package security Reports API.
//
// The swagger:meta block declares the security schemes once and sets the
// document-wide default requirement.
//
// Version: 1.0.0
//
// SecurityDefinitions:
// api_key:
// type: apiKey
// in: header
// name: X-API-Key
// oauth2:
// type: oauth2
// flow: accessCode
// authorizationUrl: https://example.com/auth
// tokenUrl: https://example.com/token
// scopes:
// read: read reports
// write: write reports
//
// Security:
// - api_key: []
//
// swagger:meta
package securityFull source: docs/examples/concepts/security/doc.go
{
"security": [
{
"api_key": []
}
],
"securityDefinitions": {
"api_key": {
"type": "apiKey",
"name": "X-API-Key",
"in": "header"
},
"oauth2": {
"type": "oauth2",
"flow": "accessCode",
"authorizationUrl": "https://example.com/auth",
"tokenUrl": "https://example.com/token",
"scopes": {
"read": "read reports",
"write": "write reports"
}
}
}
}
Full source: docs/examples/concepts/security/testdata/schemes.json
The scheme type drives the rest: apiKey needs in + name, oauth2 needs a
flow (and the URLs/scopes it implies), basic needs nothing more. The full
scheme surface is in the
securityDefinitions reference.
Require a scheme on a route
A route with no Security: keyword inherits the document-wide default
(api_key, above). A route that needs something different states its own
Security: requirement — here createReport requires oauth2 with the read
and write scopes, overriding the default:
// listReports inherits the document-wide default requirement (api_key) — no
// Security: keyword is needed.
//
// swagger:route GET /reports reports listReports
//
// responses:
// 200: description: the reports
// createReport overrides the default with its own Security: requirement —
// oauth2 with the read and write scopes. The Security: block is YAML: a sequence
// of requirement objects, scopes as a flow (or block) list.
//
// swagger:route POST /reports reports createReport
//
// Security:
// - oauth2: [read, write]
//
// responses:
// 201: description: created
// archiveReport requires BOTH schemes at once — two keys in a single sequence
// item are ANDed into one requirement object (separate items would mean OR).
//
// swagger:route POST /reports/archive reports archiveReport
//
// Security:
// - api_key: []
// oauth2: [write]
//
// responses:
// 200: description: archived
// publicReport opts out of the document default entirely — an empty
// `Security: []` emits an explicit empty requirement, marking the operation
// public regardless of the document-wide default.
//
// swagger:route GET /reports/public reports publicReport
//
// Security: []
//
// responses:
// 200: description: the public reportsFull source: docs/examples/concepts/security/routes.go
[
{
"oauth2": [
"read",
"write"
]
}
]
Full source: docs/examples/concepts/security/testdata/route.json
A Security: block is plain YAML — a sequence of requirement objects.
Scopes are a flow list ([read, write]) or a block list; an empty list
(api_key: []) is the scheme with no scopes. The combining rule follows
OpenAPI 2.0:
- multiple schemes in one item are ANDed — all are required;
- separate items are ORed — satisfying any one grants access.
So requiring both an API key and an OAuth2 scope is two keys under a single sequence item:
// listReports inherits the document-wide default requirement (api_key) — no
// Security: keyword is needed.
//
// swagger:route GET /reports reports listReports
//
// responses:
// 200: description: the reports
// createReport overrides the default with its own Security: requirement —
// oauth2 with the read and write scopes. The Security: block is YAML: a sequence
// of requirement objects, scopes as a flow (or block) list.
//
// swagger:route POST /reports reports createReport
//
// Security:
// - oauth2: [read, write]
//
// responses:
// 201: description: created
// archiveReport requires BOTH schemes at once — two keys in a single sequence
// item are ANDed into one requirement object (separate items would mean OR).
//
// swagger:route POST /reports/archive reports archiveReport
//
// Security:
// - api_key: []
// oauth2: [write]
//
// responses:
// 200: description: archived
// publicReport opts out of the document default entirely — an empty
// `Security: []` emits an explicit empty requirement, marking the operation
// public regardless of the document-wide default.
//
// swagger:route GET /reports/public reports publicReport
//
// Security: []
//
// responses:
// 200: description: the public reportsFull source: docs/examples/concepts/security/routes.go
[
{
"api_key": [],
"oauth2": [
"write"
]
}
]
Full source: docs/examples/concepts/security/testdata/and.json
A route’s requirements replace the document default for that operation. To make
one operation public — opting out of the document-wide default — give it an
empty Security: []. That emits an explicit empty requirement (distinct from
omitting the keyword, which inherits the default):
// listReports inherits the document-wide default requirement (api_key) — no
// Security: keyword is needed.
//
// swagger:route GET /reports reports listReports
//
// responses:
// 200: description: the reports
// createReport overrides the default with its own Security: requirement —
// oauth2 with the read and write scopes. The Security: block is YAML: a sequence
// of requirement objects, scopes as a flow (or block) list.
//
// swagger:route POST /reports reports createReport
//
// Security:
// - oauth2: [read, write]
//
// responses:
// 201: description: created
// archiveReport requires BOTH schemes at once — two keys in a single sequence
// item are ANDed into one requirement object (separate items would mean OR).
//
// swagger:route POST /reports/archive reports archiveReport
//
// Security:
// - api_key: []
// oauth2: [write]
//
// responses:
// 200: description: archived
// publicReport opts out of the document default entirely — an empty
// `Security: []` emits an explicit empty requirement, marking the operation
// public regardless of the document-wide default.
//
// swagger:route GET /reports/public reports publicReport
//
// Security: []
//
// responses:
// 200: description: the public reportsFull source: docs/examples/concepts/security/routes.go
{
"security": []
}
Full source: docs/examples/concepts/security/testdata/public.json
The same works from a swagger:operation YAML body — a security: key there
sets that operation’s requirement. (The schemes themselves are always global
swagger:meta — OpenAPI 2.0 has no per-operation securityDefinitions.)
Keep security out of your code
Authentication is often handled by a layer in front of the app — a gateway or
service mesh — and you may not want security details in the annotations at all.
In that case, leave the code free of security annotations and overlay the
schemes and requirements with Options.InputSpec:
The app package above (concepts/routes) carries no security annotations,
yet the merged document is secured — the schemes and the default requirement
come entirely from the base:
{
"security": [
{
"api_key": []
}
],
"securityDefinitions": {
"api_key": {
"type": "apiKey",
"name": "X-API-Key",
"in": "header"
}
}
}
Full source: docs/examples/concepts/security/testdata/overlay.json
See Overlaying a spec
for the full InputSpec merge semantics.
What’s next
- Document metadata — the other
top-level
swagger:metafields. - Routes & operations — the operations these requirements protect.