Configure Key Authentication
Learn how to secure your APIs by requiring clients to provide a unique API key in the request header or query string.
Applies to:API7 Gateway 3.10.x
Roles:Platform engineerTime:10 minutesImpact:No restart, no traffic change
Goal
A route that rejects unkeyed requests with 401 and forwards keyed requests to its upstream, with the caller identified as a named consumer.
The key-auth (opens in Plugin Hub docs) plugin reads the key from a request header or, at lower priority, from a query string parameter, and API7 Gateway resolves it against its consumer credentials before the request reaches the upstream. Anonymous consumers, credential forwarding and the full field list are in the plugin reference (opens in Plugin Hub docs).
Before you begin
- You have a token from the Dashboard.
Set the variables this guide uses before running the prerequisite checks:
export API7_ADMIN_URL="https://localhost:7443"
export API7_GATEWAY_GROUP_ID="default" # only the group the quickstart creates; otherwise copy the gateway group's UUID from Gateway Groups in the Dashboard
export GATEWAY_URL="http://127.0.0.1:9080"
read -rp "Gateway group name for ADC (if used): " API7_GATEWAY_GROUP_NAME && export API7_GATEWAY_GROUP_NAME
read -rsp "Dashboard token: " API7_ADMIN_KEY && export API7_ADMIN_KEY && echo-
An API7 Gateway control plane is reachable and your token is valid. Check it with:
curl -ks -o /dev/null -w '%{http_code}\n' "${API7_ADMIN_URL}/api/gateway_groups" \ -H "X-API-KEY: ${API7_ADMIN_KEY}"Expected:
200. -
A gateway group exists and at least one gateway instance is running in it. Check it with:
curl -ks "${API7_ADMIN_URL}/api/gateway_groups/${API7_GATEWAY_GROUP_ID}/instances" \ -H "X-API-KEY: ${API7_ADMIN_KEY}" | grep -o '"status": *"Healthy"' | wc -lExpected:
1or more — the number of instances reportingHealthy. -
For the ADC path, ADC is installed:
adc --versionExpected: a version string. ADC resolves
--gateway-groupby display name, soAPI7_GATEWAY_GROUP_NAMEis a different value from the UUID inAPI7_GATEWAY_GROUP_ID; enter the name of the same group.
Steps
Three steps: a service that knows where traffic goes, a route that closes it, and a consumer that can open it.
Step 1: Create a service with an upstream
Give the traffic a destination.
curl -ks "${API7_ADMIN_URL}/apisix/admin/services/key-auth-httpbin-service?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -X PUT \
-H "X-API-KEY: ${API7_ADMIN_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "key-auth-httpbin-service",
"upstream": {
"type": "roundrobin",
"scheme": "http",
"nodes": [
{
"host": "httpbin.org",
"port": 80,
"weight": 100
}
]
}
}'{ "value": { "id": "key-auth-httpbin-service", "name": "key-auth-httpbin-service", … } }services:
- name: key-auth-httpbin-service
upstream:
name: default
scheme: http
nodes:
- host: httpbin.org
port: 80
weight: 100
routes:
- name: key-auth-route
uris:
- /anything/key-auth
methods:
- GET
plugins:
key-auth:
header: apikey
consumers:
- username: key-auth-consumer
credentials:
- name: key-auth-consumer-cred
type: key-auth
config:
key: secret-api-keyexport ADC_BACKEND=api7ee
export ADC_SERVER="${API7_ADMIN_URL}"
export ADC_TOKEN="${API7_ADMIN_KEY}"
export ADC_GATEWAY_GROUP="${API7_GATEWAY_GROUP_NAME}"
adc sync -f adc.yamlEvery one of those has a flag equivalent — --backend, --server, --token, --gateway-group — but a token passed on the command line is visible to any local user through ps, so prefer the environment.
--gateway-group (and ADC_GATEWAY_GROUP) takes the gateway group's display name, not the UUID the Admin API uses; ADC looks the group up by name and resolves the ID itself.
Against a fresh install still using its self-signed certificate, add ADC_TLS_SKIP_VERIFY=true. That turns off verification of the control plane's certificate — fine while evaluating, not something to leave set in a pipeline. Install a certificate ADC can verify instead.
What changed: the gateway knows where to send this traffic, and nothing reaches it yet.
Step 2: Create a route and enable key-auth
Publish the service at a path, and close it in the same request.
curl -ks "${API7_ADMIN_URL}/apisix/admin/routes/key-auth-route?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -X PUT \
-H "X-API-KEY: ${API7_ADMIN_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "key-auth-route",
"paths": ["/anything/key-auth"],
"methods": ["GET"],
"service_id": "key-auth-httpbin-service",
"plugins": {
"key-auth": {
"header": "apikey"
}
}
}'{ "value": { "id": "key-auth-route", "name": "key-auth-route", … } }The route is already included in the previous adc.yaml example.
What changed: /anything/key-auth is live and returns 401 to every request, because no credential exists yet.
Step 3: Create a consumer and its credential
Create the identity, then the key that resolves to it.
# 1. Create the consumer
curl -ks "${API7_ADMIN_URL}/apisix/admin/consumers/key-auth-consumer?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -X PUT \
-H "X-API-KEY: ${API7_ADMIN_KEY}" \
-H "Content-Type: application/json" \
-d '{
"username": "key-auth-consumer"
}'
# 2. Create the key-auth credential
curl -ks "${API7_ADMIN_URL}/apisix/admin/consumers/key-auth-consumer/credentials/key-auth-consumer-cred?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -X PUT \
-H "X-API-KEY: ${API7_ADMIN_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "key-auth-consumer-cred",
"plugins": {
"key-auth": {
"key": "secret-api-key"
}
}
}'{ "value": { "username": "key-auth-consumer", … } }
{ "value": { "name": "key-auth-consumer-cred", … } }The consumer and credential are already included in the previous adc.yaml example.
What changed: one key now opens the route, and the gateway can name who used it.
Validate
Send one request without the key and one with it:
curl -is "${GATEWAY_URL}/anything/key-auth" | head -1
curl -is "${GATEWAY_URL}/anything/key-auth" -H "apikey: secret-api-key" | head -1HTTP/1.1 401 Unauthorized
HTTP/1.1 200 OKThe keyed response carries the caller's identity, which is what proves the consumer resolved rather than that the key merely matched:
curl -s "${GATEWAY_URL}/anything/key-auth" -H "apikey: secret-api-key"{
"headers": {
"Apikey": "secret-api-key",
"X-Consumer-Username": "key-auth-consumer",
"X-Credential-Identifier": "key-auth-consumer-cred"
}
}Roll back / Clean up
Remove everything this guide created, in reverse dependency order. Deleting the credential closes the route to every client; deleting the route takes the path offline.
curl -ks -X DELETE "${API7_ADMIN_URL}/apisix/admin/consumers/key-auth-consumer/credentials/key-auth-consumer-cred?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -H "X-API-KEY: ${API7_ADMIN_KEY}"
curl -ks -X DELETE "${API7_ADMIN_URL}/apisix/admin/consumers/key-auth-consumer?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -H "X-API-KEY: ${API7_ADMIN_KEY}"
curl -ks -X DELETE "${API7_ADMIN_URL}/apisix/admin/routes/key-auth-route?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -H "X-API-KEY: ${API7_ADMIN_KEY}"
curl -ks -X DELETE "${API7_ADMIN_URL}/apisix/admin/services/key-auth-httpbin-service?gateway_group_id=${API7_GATEWAY_GROUP_ID}" -H "X-API-KEY: ${API7_ADMIN_KEY}"Between the first command and the third, requests to /anything/key-auth return 401; after the third they return 404.
Troubleshoot
| Symptom | Cause | Fix |
|---|---|---|
The keyed request still returns 401. The body says which case it is: {"message":"Invalid API key in request"} means the gateway found a key and could not resolve it to a consumer; {"message":"Missing API key found in request"} means it found no key at all. | Invalid: the credential has not reached the gateway instance yet, or the key you send is not the one the credential holds. Missing: the key is not where the plugin looks for it. | Invalid: wait a few seconds and retry, then re-run step 3 and compare the key. Missing: compare the header you send with plugins.key-auth.header on the route. |
The request returns 404. | The route has not reached the gateway instance yet, or it belongs to a different gateway group. | Wait a few seconds and retry, then check that API7_GATEWAY_GROUP_ID is the group whose instance reports Healthy. |
| The key works in the header but not in the query string. | key-auth reads the query string at lower priority and from its own parameter, which defaults to apikey. | Set query (opens in Plugin Hub docs) on the plugin, or send the key in the header. |
Related
- Configure Basic Authentication — use standard username and password credentials.
- Configure JWT Authentication — use JSON Web Tokens for stateless authentication.
- Consumers and Credentials — how API7 Gateway manages API identities.
key-authplugin reference (opens in Plugin Hub docs) — every field, including anonymous consumers.