forward-auth
The forward-auth plugin supports the integration with an external authorization service for authentication and authorization. If the authentication fails, a customizable error message will be returned to the client. If the authentication succeeds, the request will be forwarded to the upstream service along with the following request headers that APISIX added:
X-Forwarded-Proto: schemeX-Forwarded-Method: HTTP methodX-Forwarded-Host: hostX-Forwarded-Uri: URIX-Forwarded-For: source IP
Examples
The examples below demonstrate how you can use forward-auth for different scenarios.
To follow along the first two examples, please have your external authorization service set up, or create a mock auth service using the serverless function plugin as shown below:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"id": "auth-mock",
"uri": "/auth",
"plugins": {
"serverless-pre-function": {
"phase": "rewrite",
"functions": [
"return function (conf, ctx)
local core = require(\"apisix.core\");
local authorization = core.request.header(ctx, \"Authorization\");
if authorization == \"123\" then
core.response.exit(200);
elseif authorization == \"321\" then
core.response.set_header(\"X-User-ID\", \"i-am-user\");
core.response.exit(200);
else core.response.set_header(\"X-Forward-Auth\", \"Fail\");
core.response.exit(403);
end
end"
]
}
}
}'
❶ If the Authorization header has a value of 123, respond with 200 OK;
❷ If the Authorization header has a value of 321, set a header X-User-ID: i-am-user and respond with 200 OK;
❸ Otherwise, set a header X-Forward-Auth: Fail and respond with 403 Forbidden.
services:
- name: auth-mock-service
routes:
- name: auth-mock-route
uris:
- /auth
plugins:
serverless-pre-function:
phase: rewrite
functions:
- |
return function(conf, ctx)
local core = require("apisix.core")
local authorization = core.request.header(ctx, "Authorization")
if authorization == "123" then
core.response.exit(200)
elseif authorization == "321" then
core.response.set_header("X-User-ID", "i-am-user")
core.response.exit(200)
else
core.response.set_header("X-Forward-Auth", "Fail")
core.response.exit(403)
end
end
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ If the Authorization header has a value of 123, respond with 200 OK;
❷ If the Authorization header has a value of 321, set a header X-User-ID: i-am-user and respond with 200 OK;
❸ Otherwise, set a header X-Forward-Auth: Fail and respond with 403 Forbidden.
Synchronize the configuration to the gateway:
adc sync -f adc-auth-mock.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: auth-mock-plugin-config
spec:
plugins:
- name: serverless-pre-function
config:
phase: rewrite
functions:
- |
return function(conf, ctx)
local core = require("apisix.core")
local authorization = core.request.header(ctx, "Authorization")
if authorization == "123" then
core.response.exit(200)
elseif authorization == "321" then
core.response.set_header("X-User-ID", "i-am-user")
core.response.exit(200)
else
core.response.set_header("X-Forward-Auth", "Fail")
core.response.exit(403)
end
end
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: auth-mock-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /auth
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: auth-mock-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
❶ If the Authorization header has a value of 123, respond with 200 OK;
❷ If the Authorization header has a value of 321, set a header X-User-ID: i-am-user and respond with 200 OK;
❸ Otherwise, set a header X-Forward-Auth: Fail and respond with 403 Forbidden.
Apply the configuration to your cluster:
kubectl apply -f forward-auth-mock-ic.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: auth-mock-route
spec:
ingressClassName: apisix
http:
- name: auth-mock-route
match:
paths:
- /auth
upstreams:
- name: httpbin-external-domain
plugins:
- name: serverless-pre-function
enable: true
config:
phase: rewrite
functions:
- |
return function(conf, ctx)
local core = require("apisix.core")
local authorization = core.request.header(ctx, "Authorization")
if authorization == "123" then
core.response.exit(200)
elseif authorization == "321" then
core.response.set_header("X-User-ID", "i-am-user")
core.response.exit(200)
else
core.response.set_header("X-Forward-Auth", "Fail")
core.response.exit(403)
end
end
❶ If the Authorization header has a value of 123, respond with 200 OK;
❷ If the Authorization header has a value of 321, set a header X-User-ID: i-am-user and respond with 200 OK;
❸ Otherwise, set a header X-Forward-Auth: Fail and respond with 403 Forbidden.
Apply the configuration to your cluster:
kubectl apply -f forward-auth-mock-ic.yaml
Forward Designated Headers to Upstream Resource
The following example demonstrates how to set up forward-auth on a route to regulate client access to the resources upstream based on a value in the request header. It also allows passing a specific header from the authorization service to the upstream resource.
Create a route with the forward-auth plugin as such:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "forward-auth-route",
"uri": "/headers",
"plugins": {
"forward-auth": {
"uri": "http://127.0.0.1:9080/auth",
"request_headers": ["Authorization"],
"upstream_headers": ["X-User-ID"]
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
❶ The URI of the authorization service.
❷ The request header that should be forwarded to the authorization service.
❸ The request header set by the authorization service that should be forwarded to the upstream resource when the authorization succeeds.
services:
- name: forward-auth-service
routes:
- name: forward-auth-route
uris:
- /headers
plugins:
forward-auth:
uri: http://127.0.0.1:9080/auth
request_headers:
- Authorization
upstream_headers:
- X-User-ID
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ The URI of the authorization service.
❷ The request header that should be forwarded to the authorization service.
❸ The request header set by the authorization service that should be forwarded to the upstream resource when the authorization succeeds.
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: forward-auth-plugin-config
spec:
plugins:
- name: forward-auth
config:
uri: http://apisix-gateway.aic.svc.cluster.local/auth
request_headers:
- Authorization
upstream_headers:
- X-User-ID
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: forward-auth-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /headers
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: forward-auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
❶ The URI of the authorization service. When using the Ingress Controller, reference the mock auth service using its Kubernetes service address.
❷ The request header that should be forwarded to the authorization service.
❸ The request header set by the authorization service that should be forwarded to the upstream resource when the authorization succeeds.
Apply the configuration to your cluster:
kubectl apply -f forward-auth-ic.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: forward-auth-route
spec:
ingressClassName: apisix
http:
- name: forward-auth-route
match:
paths:
- /headers
upstreams:
- name: httpbin-external-domain
plugins:
- name: forward-auth
enable: true
config:
uri: http://apisix-gateway.aic.svc.cluster.local/auth
request_headers:
- Authorization
upstream_headers:
- X-User-ID
❶ The URI of the authorization service. When using the Ingress Controller, reference the mock auth service using its Kubernetes service address.
❷ The request header that should be forwarded to the authorization service.
❸ The request header set by the authorization service that should be forwarded to the upstream resource when the authorization succeeds.
Apply the configuration to your cluster:
kubectl apply -f forward-auth-ic.yaml
Send a request to the route with authorization detail in the header:
curl "http://127.0.0.1:9080/headers" -H 'Authorization: 123'
You should see an HTTP/1.1 200 OK response of the following:
{
"headers": {
"Accept": "*/*",
"Authorization": "123",
...
}
}
To verify if the X-User-ID header set by the authorization service will be forwarded to the upstream service, send a request to the route with the corresponding authorization detail:
curl "http://127.0.0.1:9080/headers" -H 'Authorization: 321'
You should see an HTTP/1.1 200 OK response of the following, showing the header is forwarded to the upstream:
{
"headers": {
"Accept": "*/*",
"Authorization": "123",
"X-User-ID": "i-am-user",
...
}
}
Return Designated Headers to Clients on Authentication Failure
The following example demonstrates how you can configure forward-auth on a route to regulate client access to the upstream resources. It also passes a specific header returned by the authorization service to the client when the authentication fails.
Create a route with the forward-auth plugin as such:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "forward-auth-route",
"uri": "/headers",
"plugins": {
"forward-auth": {
"uri": "http://127.0.0.1:9080/auth",
"request_headers": ["Authorization"],
"client_headers": ["X-Forward-Auth"]
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
❶ Pass the X-Forward-Auth header from the authorization service back to the client when authentication fails.
services:
- name: forward-auth-service
routes:
- name: forward-auth-route
uris:
- /headers
plugins:
forward-auth:
uri: http://127.0.0.1:9080/auth
request_headers:
- Authorization
client_headers:
- X-Forward-Auth
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ Pass the X-Forward-Auth header from the authorization service back to the client when authentication fails.
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: forward-auth-plugin-config
spec:
plugins:
- name: forward-auth
config:
uri: http://apisix-gateway.aic.svc.cluster.local/auth
request_headers:
- Authorization
client_headers:
- X-Forward-Auth
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: forward-auth-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /headers
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: forward-auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
❶ Pass the X-Forward-Auth header from the authorization service back to the client when authentication fails.
Apply the configuration to your cluster:
kubectl apply -f forward-auth-ic.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: forward-auth-route
spec:
ingressClassName: apisix
http:
- name: forward-auth-route
match:
paths:
- /headers
upstreams:
- name: httpbin-external-domain
plugins:
- name: forward-auth
enable: true
config:
uri: http://apisix-gateway.aic.svc.cluster.local/auth
request_headers:
- Authorization
client_headers:
- X-Forward-Auth
❶ Pass the X-Forward-Auth header from the authorization service back to the client when authentication fails.
Apply the configuration to your cluster:
kubectl apply -f forward-auth-ic.yaml
Send a request without any authentication information:
curl -i "http://127.0.0.1:9080/headers"
You should receive an HTTP/1.1 403 Forbidden response:
...
X-Forward-Auth: Fail
Server: APISIX/3.x.x
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>openresty</center>
<p><em>Powered by <a href="https://apisix.apache.org/">APISIX</a>.</em></p></body>
</html>
Authorize Based on POST Body
This example demonstrates how to configure the forward-auth plugin to control access based on POST body data, pass values as headers to the authorization service, and reject the request when authorization failed per the body data.
Please have your external authorization service set up, or create a mock auth service using the serverless function plugin. The function checks if the tenant_id header is 123 and returns 200 OK if it is, otherwise it returns 403 with an error message.
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"id": "auth-mock",
"uri": "/auth",
"plugins": {
"serverless-pre-function": {
"phase": "rewrite",
"functions": [
"return function(conf, ctx)
local core = require(\"apisix.core\")
local tenant_id = core.request.header(ctx, \"tenant_id\")
if tenant_id == \"123\" then
core.response.exit(200);
else
core.response.exit(403, \"tenant_id is \"..tenant_id .. \" but expecting 123\");
end
end"
]
}
}
}'
services:
- name: auth-mock-service
routes:
- name: auth-mock-route
uris:
- /auth
plugins:
serverless-pre-function:
phase: rewrite
functions:
- |
return function(conf, ctx)
local core = require("apisix.core")
local tenant_id = core.request.header(ctx, "tenant_id")
if tenant_id == "123" then
core.response.exit(200)
else
core.response.exit(403, "tenant_id is " .. tenant_id .. " but expecting 123")
end
end
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Synchronize the configuration to the gateway:
adc sync -f adc-auth-mock.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: auth-mock-plugin-config
spec:
plugins:
- name: serverless-pre-function
config:
phase: rewrite
functions:
- |
return function(conf, ctx)
local core = require("apisix.core")
local tenant_id = core.request.header(ctx, "tenant_id")
if tenant_id == "123" then
core.response.exit(200)
else
core.response.exit(403, "tenant_id is " .. tenant_id .. " but expecting 123")
end
end
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: auth-mock-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /auth
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: auth-mock-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
Apply the configuration to your cluster:
kubectl apply -f forward-auth-post-mock-ic.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: auth-mock-route
spec:
ingressClassName: apisix
http:
- name: auth-mock-route
match:
paths:
- /auth
upstreams:
- name: httpbin-external-domain
plugins:
- name: serverless-pre-function
enable: true
config:
phase: rewrite
functions:
- |
return function(conf, ctx)
local core = require("apisix.core")
local tenant_id = core.request.header(ctx, "tenant_id")
if tenant_id == "123" then
core.response.exit(200)
else
core.response.exit(403, "tenant_id is " .. tenant_id .. " but expecting 123")
end
end
Apply the configuration to your cluster:
kubectl apply -f forward-auth-post-mock-ic.yaml
Create a route with the forward-auth plugin as such:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "forward-auth-route",
"uri": "/post",
"methods": ["POST"],
"plugins": {
"forward-auth": {
"uri": "http://127.0.0.1:9080/auth",
"request_method": "GET",
"extra_headers": {"tenant_id": "$post_arg.tenant_id"}
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
❶ Set an extra header tenant_id using the value from the POST parameter tenant_id.
services:
- name: forward-auth-service
routes:
- name: forward-auth-route
uris:
- /post
methods:
- POST
plugins:
forward-auth:
uri: http://127.0.0.1:9080/auth
request_method: GET
extra_headers:
tenant_id: "$post_arg.tenant_id"
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ Set an extra header tenant_id using the value from the POST parameter tenant_id.
Synchronize the configuration to the gateway:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: forward-auth-post-plugin-config
spec:
plugins:
- name: forward-auth
config:
uri: http://apisix-gateway.aic.svc.cluster.local/auth
request_method: GET
extra_headers:
tenant_id: "$post_arg.tenant_id"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: forward-auth-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /post
method: POST
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: forward-auth-post-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
❶ Set an extra header tenant_id using the value from the POST parameter tenant_id.
Apply the configuration to your cluster:
kubectl apply -f forward-auth-post-ic.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: forward-auth-route
spec:
ingressClassName: apisix
http:
- name: forward-auth-route
match:
paths:
- /post
methods:
- POST
upstreams:
- name: httpbin-external-domain
plugins:
- name: forward-auth
enable: true
config:
uri: http://apisix-gateway.aic.svc.cluster.local/auth
request_method: GET
extra_headers:
tenant_id: "$post_arg.tenant_id"
❶ Set an extra header tenant_id using the value from the POST parameter tenant_id.
Apply the configuration to your cluster:
kubectl apply -f forward-auth-post-ic.yaml
Send a POST request with tenant_id in the body:
curl -i "http://127.0.0.1:9080/post" -X POST -d '
{
"tenant_id": "123"
}'
You should receive an HTTP/1.1 200 OK response.
Send a POST request with tenant_id in the body:
curl -i "http://127.0.0.1:9080/post" -X POST -d '
{
"tenant_id": "000"
}'
You should receive an HTTP/1.1 403 Forbidden response of the following:
tenant_id is 000 but expecting 123