You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by GitBox <gi...@apache.org> on 2022/01/07 02:17:54 UTC

[GitHub] [apisix] spacewander commented on a change in pull request #6037: feat: add forward-auth plugin

spacewander commented on a change in pull request #6037:
URL: https://github.com/apache/apisix/pull/6037#discussion_r779976644



##########
File path: apisix/core/request.lua
##########
@@ -278,6 +278,15 @@ function _M.get_path(ctx)
 end
 
 
+function _M.get_uri(ctx)

Review comment:
       We should not add a common method that is only used by a place. Actually, I think we should remove the `get_path` too.
   
   It is strange that `get_path` fetches `$uri` but `get_uri` fetches `$request_uri`. Look like it is a premature optimization that brings inconsistent names.

##########
File path: apisix/plugins/forward-auth.lua
##########
@@ -0,0 +1,143 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+local ipairs = ipairs
+local core   = require("apisix.core")
+local http   = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        host = {type = "string"},
+        ssl_verify = {
+            type = "boolean",
+            default = true,
+        },
+        request_headers = {
+            type = "array",
+            default = {},
+            items = {type = "string"},
+            description = "client request header that will be sent to the authorization"
+        },
+        upstream_headers = {
+            type = "array",
+            default = {},
+            items = {type = "string"},
+            description = "authorization response header that will be sent to the upstream"
+        },
+        client_headers = {
+            type = "array",
+            default = {},
+            items = {type = "string"},
+            description = "authorization response header that will be sent to"
+                           .. "the client when authorize failure"
+        },
+        timeout = {
+            type = "integer",
+            minimum = 1,
+            maximum = 60000,
+            default = 3000,
+            description = "timeout in milliseconds",
+        },
+        keepalive = {type = "boolean", default = true},
+        keepalive_timeout = {type = "integer", minimum = 1000, default = 60000},
+        keepalive_pool = {type = "integer", minimum = 1, default = 5},
+    },
+    required = {"host"}
+}
+
+
+local _M = {
+    version = 0.1,
+    priority = 2002,
+    name = "forward-auth",
+    schema = schema,
+}
+
+
+function _M.check_schema(conf)
+    return core.schema.check(schema, conf)
+end
+
+
+function _M.access(conf, ctx)
+    local auth_headers = {
+        ["X-Forwarded-Proto"] = core.request.get_scheme(ctx),
+        ["X-Forwarded-Method"] = core.request.get_method(),
+        ["X-Forwarded-Host"] = core.request.get_host(ctx),
+        ["X-Forwarded-Uri"] = core.request.get_uri(ctx),
+        ["X-Forwarded-For"] = core.request.get_remote_client_ip(ctx),
+    }
+
+    -- append headers that need to be get from the client request header
+    if #conf.request_headers > 0 then
+        for _, header in ipairs(conf.request_headers) do
+            auth_headers[header] = core.request.header(ctx, header)
+        end
+    else
+        auth_headers = core.table.merge(core.request.headers(), auth_headers)

Review comment:
       Ditto

##########
File path: docs/en/latest/plugins/forward-auth.md
##########
@@ -0,0 +1,135 @@
+---
+title: forward-auth
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+## Summary
+
+- [**Description**](#description)
+- [**Attributes**](#attributes)
+- [**Example**](#example)
+
+## Description
+
+The `forward-auth` plugin implement a classic external authentication model. We can implement a custom error return or user redirection to the authentication page if the authentication fails.
+
+Forward Auth cleverly moves the authentication and authorization logic to a dedicated external service, where the gateway forwards the user's request to the authentication service and blocks the original request and replaces the result when the authentication service responds with a non-20x status.
+
+## Attributes
+
+| Name | Type | Requirement | Default | Valid | Description |
+| -- | -- | -- | -- | -- | -- |
+| host | string | required |  |  | Authorization service host (eg. https://localhost:9188) |
+| ssl_verify | boolean | optional | true |   | Whether to verify the certificate |
+| request_headers | array[string] | optional |  |  | `client` request header that will be sent to the `authorization` service |
+| upstream_headers | array[string] | optional |  |  | `authorization` service response header that will be sent to the `upstream` |
+| client_headers | array[string] | optional |  |  | `authorization` response header that will be sent to the `client` when authorize failure |
+| timeout | integer | optional | 3000ms | [1, 60000]ms | Authorization service HTTP call timeout |
+| keepalive | boolean | optional | true |  | HTTP keepalive |
+| keepalive_timeout | integer | optional | 60000ms | [1000, ...]ms | keepalive idle timeout |
+| keepalive_pool | integer | optional | 5 | [1, ...]ms | Connection pool limit |
+
+## Example
+
+First, you need to setup an external authorization service. Here is an example of using Apache APISIX's serverless plugin to mock.
+
+```shell
+$ curl -X PUT 'http://127.0.0.1:9080/apisix/admin/routes/auth' \
+    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
+    -H 'Content-Type: application/json' \
+    -d '{
+    "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(\"Location\", \"http://example.com/auth\"); core.response.exit(403); end end"
+            ]
+        }
+    },
+    "upstream": {
+        "nodes": {},
+        "scheme": "https",
+        "type": "roundrobin"
+    }
+}'
+```
+
+Next, we create a route for testing.
+
+```shell
+$ curl -X PUT http://127.0.0.1:9080/apisix/admin/routes/1
+    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'
+    -d '{
+    "uri": "/headers",
+    "plugins": {
+        "forward-auth": {
+            "host": "http://127.0.0.1:9080/auth",
+            "request_headers": ["Authorization"],
+            "upstream_headers": ["X-User-ID"],
+            "client_headers": ["Location"]
+        }
+    },
+    "upstream": {
+        "nodes": {
+            "httpbin.org:80": 1
+        },
+        "type": "roundrobin"
+    }
+}'
+```
+
+We can perform the following three tests.
+
+1. **request_headers** Send Authorization header from `client` to `authorization` service
+
+```shell
+$ curl http://127.0.0.1:9080/headers -H 'Authorization: 123'
+{
+    "headers": {
+        "Authorization": "123",
+        "Next": "More-headers"
+    }
+}
+```
+
+2. **upstream_headers** Send `authorization` service response header to the `upstream`
+
+```shell
+$ curl http://127.0.0.1:9080/headers -H 'Authorization: 321'
+{
+    "headers": {
+        "Authorization": "321",
+        "X-User-ID": "i-am-user",
+        "Next": "More-headers"
+    }
+}
+```
+
+3. **client_headers** Send `authorization` service response header to `client` when authorize failure

Review comment:
       ```suggestion
   3. **client_headers** Send `authorization` service response header to `client` when authorizing failed
   ```

##########
File path: docs/en/latest/plugins/forward-auth.md
##########
@@ -0,0 +1,135 @@
+---
+title: forward-auth
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+## Summary
+
+- [**Description**](#description)
+- [**Attributes**](#attributes)
+- [**Example**](#example)
+
+## Description
+
+The `forward-auth` plugin implement a classic external authentication model. We can implement a custom error return or user redirection to the authentication page if the authentication fails.
+
+Forward Auth cleverly moves the authentication and authorization logic to a dedicated external service, where the gateway forwards the user's request to the authentication service and blocks the original request and replaces the result when the authentication service responds with a non-20x status.
+
+## Attributes
+
+| Name | Type | Requirement | Default | Valid | Description |
+| -- | -- | -- | -- | -- | -- |
+| host | string | required |  |  | Authorization service host (eg. https://localhost:9188) |
+| ssl_verify | boolean | optional | true |   | Whether to verify the certificate |
+| request_headers | array[string] | optional |  |  | `client` request header that will be sent to the `authorization` service |
+| upstream_headers | array[string] | optional |  |  | `authorization` service response header that will be sent to the `upstream` |

Review comment:
       Ditto

##########
File path: docs/en/latest/plugins/forward-auth.md
##########
@@ -0,0 +1,135 @@
+---
+title: forward-auth
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+## Summary
+
+- [**Description**](#description)
+- [**Attributes**](#attributes)
+- [**Example**](#example)
+
+## Description
+
+The `forward-auth` plugin implement a classic external authentication model. We can implement a custom error return or user redirection to the authentication page if the authentication fails.
+
+Forward Auth cleverly moves the authentication and authorization logic to a dedicated external service, where the gateway forwards the user's request to the authentication service and blocks the original request and replaces the result when the authentication service responds with a non-20x status.

Review comment:
       ```suggestion
   Forward Auth cleverly moves the authentication and authorization logic to a dedicated external service, where the gateway forwards the user's request to the authentication service and blocks the original request, and replaces the result when the authentication service responds with a non-2xx status.
   ```

##########
File path: apisix/plugins/forward-auth.lua
##########
@@ -0,0 +1,143 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+local ipairs = ipairs
+local core   = require("apisix.core")
+local http   = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        host = {type = "string"},
+        ssl_verify = {
+            type = "boolean",
+            default = true,
+        },
+        request_headers = {
+            type = "array",
+            default = {},
+            items = {type = "string"},
+            description = "client request header that will be sent to the authorization"

Review comment:
       ```suggestion
               description = "client request header that will be sent to the authorization service"
   ```

##########
File path: apisix/plugins/forward-auth.lua
##########
@@ -0,0 +1,143 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+local ipairs = ipairs
+local core   = require("apisix.core")
+local http   = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        host = {type = "string"},
+        ssl_verify = {
+            type = "boolean",
+            default = true,
+        },
+        request_headers = {
+            type = "array",
+            default = {},
+            items = {type = "string"},
+            description = "client request header that will be sent to the authorization"
+        },
+        upstream_headers = {
+            type = "array",
+            default = {},
+            items = {type = "string"},
+            description = "authorization response header that will be sent to the upstream"
+        },
+        client_headers = {
+            type = "array",
+            default = {},
+            items = {type = "string"},
+            description = "authorization response header that will be sent to"
+                           .. "the client when authorize failure"

Review comment:
       ```suggestion
                              .. "the client when authorizing failed"
   ```

##########
File path: apisix/plugins/forward-auth.lua
##########
@@ -0,0 +1,143 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+local ipairs = ipairs
+local core   = require("apisix.core")
+local http   = require("resty.http")
+
+local schema = {
+    type = "object",
+    properties = {
+        host = {type = "string"},
+        ssl_verify = {
+            type = "boolean",
+            default = true,
+        },
+        request_headers = {
+            type = "array",
+            default = {},
+            items = {type = "string"},
+            description = "client request header that will be sent to the authorization"
+        },
+        upstream_headers = {
+            type = "array",
+            default = {},
+            items = {type = "string"},
+            description = "authorization response header that will be sent to the upstream"
+        },
+        client_headers = {
+            type = "array",
+            default = {},
+            items = {type = "string"},
+            description = "authorization response header that will be sent to"
+                           .. "the client when authorize failure"
+        },
+        timeout = {
+            type = "integer",
+            minimum = 1,
+            maximum = 60000,
+            default = 3000,
+            description = "timeout in milliseconds",
+        },
+        keepalive = {type = "boolean", default = true},
+        keepalive_timeout = {type = "integer", minimum = 1000, default = 60000},
+        keepalive_pool = {type = "integer", minimum = 1, default = 5},
+    },
+    required = {"host"}
+}
+
+
+local _M = {
+    version = 0.1,
+    priority = 2002,
+    name = "forward-auth",
+    schema = schema,
+}
+
+
+function _M.check_schema(conf)
+    return core.schema.check(schema, conf)
+end
+
+
+function _M.access(conf, ctx)
+    local auth_headers = {
+        ["X-Forwarded-Proto"] = core.request.get_scheme(ctx),
+        ["X-Forwarded-Method"] = core.request.get_method(),
+        ["X-Forwarded-Host"] = core.request.get_host(ctx),
+        ["X-Forwarded-Uri"] = core.request.get_uri(ctx),
+        ["X-Forwarded-For"] = core.request.get_remote_client_ip(ctx),
+    }
+
+    -- append headers that need to be get from the client request header
+    if #conf.request_headers > 0 then
+        for _, header in ipairs(conf.request_headers) do
+            auth_headers[header] = core.request.header(ctx, header)

Review comment:
       Better not trust the client's `X-Forwarded-XX` by default

##########
File path: t/plugin/forward-auth.t
##########
@@ -0,0 +1,157 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
+        $block->set_value("no_error_log", "[error]");
+    }
+
+    if (!defined $block->request) {
+        $block->set_value("request", "GET /t");
+    }
+});
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local test_cases = {
+                {host = "http://127.0.0.1:8199"},
+                {request_headers = {"test"}},
+                {host = 3233},
+                {host = "http://127.0.0.1:8199", request_headers = "test"}
+            }
+            local plugin = require("apisix.plugins.forward-auth")
+
+            for _, case in ipairs(test_cases) do
+                local ok, err = plugin.check_schema(case)
+                ngx.say(ok and "done" or err)
+            end
+        }
+    }
+--- response_body
+done
+property "host" is required
+property "host" validation failed: wrong type: expected string, got number
+property "request_headers" validation failed: wrong type: expected array, got string
+
+
+
+=== TEST 2: setup route with plugin
+--- config
+    location /t {
+        content_by_lua_block {
+            local datas = {
+                {
+                    url = "/apisix/admin/routes/auth",
+                    data = [[{
+                        "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-an-user\"); core.response.exit(200); else core.response.set_header(\"Location\", \"http://example.com/auth\"); core.response.exit(403); end end"]
+                            }
+                        },
+                        "uri": "/auth"
+                    }]],
+                },
+                {
+                    url = "/apisix/admin/routes/echo",
+                    data = [[{
+                        "plugins": {
+                            "serverless-pre-function": {
+                                "phase": "rewrite",
+                                "functions": ["return function (conf, ctx) local core = require(\"apisix.core\"); core.response.exit(200, core.request.headers(ctx)); end"]
+                            }
+                        },
+                        "uri": "/echo"
+                    }]],
+                },
+                {
+                    url = "/apisix/admin/routes/1",
+                    data = [[{
+                        "plugins": {
+                            "forward-auth": {
+                                "host": "http://127.0.0.1:1984/auth",
+                                "request_headers": ["Authorization"],
+                                "upstream_headers": ["X-User-ID"],
+                                "client_headers": ["Location"]
+                            },
+                            "proxy-rewrite": {
+                                "uri": "/echo"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1984": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                    }]],
+                },
+            }
+
+            local t = require("lib.test_admin").test
+
+            for _, data in ipairs(datas) do
+                local code, body = t(data.url, ngx.HTTP_PUT, data.data)
+                ngx.say(code..body)
+            end
+        }
+    }
+--- response_body eval
+"201passed\n" x 3
+
+
+
+=== TEST 3: hit route (test request_headers)
+--- request
+GET /hello
+--- more_headers
+Authorization: 123
+--- response_body_like eval
+qr/\"authorization\":\"123\"/
+
+
+
+=== TEST 4: hit route (test upstream_headers)
+--- request
+GET /hello
+--- more_headers
+Authorization: 321
+--- response_body_like eval
+qr/\"x-user-id\":\"i-am-an-user\"/
+
+
+
+=== TEST 5: hit route (test client_headers)
+--- request
+GET /hello
+--- error_code: 403
+--- response_headers
+Location: http://example.com/auth

Review comment:
       Let's add a test that the client passes X-Forwarded-XX by itself

##########
File path: t/plugin/forward-auth.t
##########
@@ -0,0 +1,157 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
+        $block->set_value("no_error_log", "[error]");
+    }
+
+    if (!defined $block->request) {
+        $block->set_value("request", "GET /t");
+    }
+});
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local test_cases = {
+                {host = "http://127.0.0.1:8199"},
+                {request_headers = {"test"}},
+                {host = 3233},
+                {host = "http://127.0.0.1:8199", request_headers = "test"}
+            }
+            local plugin = require("apisix.plugins.forward-auth")
+
+            for _, case in ipairs(test_cases) do
+                local ok, err = plugin.check_schema(case)
+                ngx.say(ok and "done" or err)
+            end
+        }
+    }
+--- response_body
+done
+property "host" is required
+property "host" validation failed: wrong type: expected string, got number
+property "request_headers" validation failed: wrong type: expected array, got string
+
+
+
+=== TEST 2: setup route with plugin
+--- config
+    location /t {
+        content_by_lua_block {
+            local datas = {
+                {
+                    url = "/apisix/admin/routes/auth",
+                    data = [[{
+                        "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-an-user\"); core.response.exit(200); else core.response.set_header(\"Location\", \"http://example.com/auth\"); core.response.exit(403); end end"]

Review comment:
       Let's log down the X-Forwarded-XX received by the service. And better to break down the very long code.

##########
File path: docs/en/latest/plugins/forward-auth.md
##########
@@ -0,0 +1,135 @@
+---
+title: forward-auth
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+## Summary
+
+- [**Description**](#description)
+- [**Attributes**](#attributes)
+- [**Example**](#example)
+
+## Description
+
+The `forward-auth` plugin implement a classic external authentication model. We can implement a custom error return or user redirection to the authentication page if the authentication fails.
+
+Forward Auth cleverly moves the authentication and authorization logic to a dedicated external service, where the gateway forwards the user's request to the authentication service and blocks the original request and replaces the result when the authentication service responds with a non-20x status.
+
+## Attributes
+
+| Name | Type | Requirement | Default | Valid | Description |
+| -- | -- | -- | -- | -- | -- |
+| host | string | required |  |  | Authorization service host (eg. https://localhost:9188) |
+| ssl_verify | boolean | optional | true |   | Whether to verify the certificate |
+| request_headers | array[string] | optional |  |  | `client` request header that will be sent to the `authorization` service |
+| upstream_headers | array[string] | optional |  |  | `authorization` service response header that will be sent to the `upstream` |
+| client_headers | array[string] | optional |  |  | `authorization` response header that will be sent to the `client` when authorize failure |
+| timeout | integer | optional | 3000ms | [1, 60000]ms | Authorization service HTTP call timeout |
+| keepalive | boolean | optional | true |  | HTTP keepalive |
+| keepalive_timeout | integer | optional | 60000ms | [1000, ...]ms | keepalive idle timeout |
+| keepalive_pool | integer | optional | 5 | [1, ...]ms | Connection pool limit |
+
+## Example
+
+First, you need to setup an external authorization service. Here is an example of using Apache APISIX's serverless plugin to mock.
+
+```shell
+$ curl -X PUT 'http://127.0.0.1:9080/apisix/admin/routes/auth' \
+    -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
+    -H 'Content-Type: application/json' \
+    -d '{
+    "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(\"Location\", \"http://example.com/auth\"); core.response.exit(403); end end"
+            ]
+        }
+    },
+    "upstream": {
+        "nodes": {},
+        "scheme": "https",

Review comment:
       Let's remove useless field

##########
File path: docs/en/latest/plugins/forward-auth.md
##########
@@ -0,0 +1,135 @@
+---
+title: forward-auth
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+## Summary
+
+- [**Description**](#description)
+- [**Attributes**](#attributes)
+- [**Example**](#example)
+
+## Description
+
+The `forward-auth` plugin implement a classic external authentication model. We can implement a custom error return or user redirection to the authentication page if the authentication fails.

Review comment:
       ```suggestion
   The `forward-auth` plugin implements a classic external authentication model. We can implement a custom error return or user redirection to the authentication page if the authentication fails.
   ```

##########
File path: docs/en/latest/plugins/forward-auth.md
##########
@@ -0,0 +1,135 @@
+---
+title: forward-auth
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+## Summary
+
+- [**Description**](#description)
+- [**Attributes**](#attributes)
+- [**Example**](#example)
+
+## Description
+
+The `forward-auth` plugin implement a classic external authentication model. We can implement a custom error return or user redirection to the authentication page if the authentication fails.
+
+Forward Auth cleverly moves the authentication and authorization logic to a dedicated external service, where the gateway forwards the user's request to the authentication service and blocks the original request and replaces the result when the authentication service responds with a non-20x status.
+
+## Attributes
+
+| Name | Type | Requirement | Default | Valid | Description |
+| -- | -- | -- | -- | -- | -- |
+| host | string | required |  |  | Authorization service host (eg. https://localhost:9188) |
+| ssl_verify | boolean | optional | true |   | Whether to verify the certificate |
+| request_headers | array[string] | optional |  |  | `client` request header that will be sent to the `authorization` service |

Review comment:
       Let's doc the behavior when this field is empty




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org