Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(openid-connect): add session.cookie configuration #10919

Merged
merged 8 commits into from
Mar 4, 2024
9 changes: 9 additions & 0 deletions apisix/plugins/openid-connect.lua
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ local schema = {
description = "the key used for the encrypt and HMAC calculation",
minLength = 16,
},
cookie = {
type = "object",
properties = {
lifetime = {
type = "integer",
description = "it holds the cookie lifetime in seconds in the future",
}
}
}
},
required = {"secret"},
additionalProperties = false,
Expand Down
2 changes: 2 additions & 0 deletions docs/en/latest/plugins/openid-connect.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ description: OpenID Connect allows the client to obtain user information from th
| set_refresh_token_header | boolean | False | false | | When set to true and a refresh token object is available, sets it in the `X-Refresh-Token` request header. |
| session | object | False | | | When bearer_only is set to false, openid-connect will use Authorization Code flow to authenticate on the IDP, so you need to set the session-related configuration. |
| session.secret | string | True | Automatic generation | 16 or more characters | The key used for session encrypt and HMAC operation. |
| session.cookie | integer | False | | | |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing description for session.cookie

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a map to hold the lifetime, does it need some description?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing description for session.cookie
As @monkeyDluffy6017 said, it is a map. I fixed its type to object, does it still need some description?

| session.cookie.lifetime | object | False | 3600 | | it holds the cookie lifetime in seconds in the future. By default this is set to 3,600 seconds. This can be configured with Nginx set $session_cookie_lifetime 3600;. This does not set cookie's expiration time on session only (by default) cookies, but it is used if the cookies are configured persistent with session.cookie.persistent == true. |
| unauth_action | string | False | "auth" | ["auth","deny","pass"] | Specify the response type on unauthenticated requests. "auth" redirects to identity provider, "deny" results in a 401 response, "pass" will allow the request without authentication. |
| proxy_opts | object | False | | | HTTP proxy that the OpenID provider is behind. |
| proxy_opts.http_proxy | string | False | | http://proxy-server:port | HTTP proxy server address. |
Expand Down
2 changes: 2 additions & 0 deletions docs/zh/latest/plugins/openid-connect.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ description: OpenID Connect(OIDC)是基于 OAuth 2.0 的身份认证协议
| set_refresh_token_header | boolean | 否 | false | | 当设置为 `true` 并且刷新令牌可用时,则会将该属性设置在`X-Refresh-Token`请求头中。 |
| session | object | 否 | | | 当设置 bearer_only 为 false 时,openid-connect 插件将使用 Authorization Code 在 IDP 上进行认证,因此你必须设置 session 相关设置。 |
| session.secret | string | 是 | 自动生成 | 16 个以上字符 | 用于 session 加密和 HMAC 计算的密钥。 |
| session.cookie | integer | False | | | |
| session.cookie.lifetime | object | False | 3600 | | 用于设置cookie的生命周期,以秒为单位。默认设置为3600秒。这可以通过Nginx `set $session_cookie_lifetime 3600;`来配置。在默认情况下不会影响会话 cookie 的过期时间,但如果通过 session.cookie.persistent == true 配置了持久化的 cookie,则会使用该值。另请参阅有关 ssl_session_timeout 的说明。 |
| unauth_action | string | False | "auth" | ["auth","deny","pass"] | 指定未经身份验证的请求的响应类型。 `auth` 重定向到身份提供者,`deny` 导致 401 响应,`pass` 将允许请求而无需身份验证。 |
| proxy_opts | object | 否 | | | OpenID 服务器前面的 HTTP 代理服务器。 |
| proxy_opts | object | 否 | | | 用来访问身份认证服务器的代理服务器。 |
Expand Down
154 changes: 154 additions & 0 deletions t/plugin/openid-connect6.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#
# 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';

log_level('debug');
repeat_each(1);
no_long_string();
no_root_location();
# no_shuffle();

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: Check configuration of cookie
--- config
location /t {
content_by_lua_block {
local test_cases = {
{
client_id = "course_management",
client_secret = "tbsmDOpsHwdgIqYl2NltGRTKzjIzvEmT",
discovery = "http://127.0.0.1:8080/realms/University/.well-known/openid-configuration",
session = {
secret = "6S8IO+Pydgb33LIor8T9ClER0T/sglFAjClFeAF3RsY=",
cookie = {
lifetime = 100
}
}
},
}
local plugin = require("apisix.plugins.openid-connect")
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

monkeyDluffy6017 marked this conversation as resolved.
Show resolved Hide resolved
=== TEST 2: Set up new route access the auth server
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"openid-connect": {
"discovery": "http://127.0.0.1:8080/realms/University/.well-known/openid-configuration",
"realm": "University",
"client_id": "course_management",
"client_secret": "d1ec69e9-55d2-4109-a3ea-befa071579d5",
"redirect_uri": "http://127.0.0.1:]] .. ngx.var.server_port .. [[/authenticated",
"ssl_verify": false,
"bearer_only" : false,
"timeout": 10,
"introspection_endpoint_auth_method": "client_secret_post",
"required_scopes": ["profile"],
"introspection_endpoint_auth_method": "client_secret_post",
"introspection_endpoint": "http://127.0.0.1:8080/realms/University/protocol/openid-connect/token/introspect",
"set_access_token_header": true,
"access_token_in_authorization_header": false,
"set_id_token_header": true,
"set_userinfo_header": true,
"set_refresh_token_header": true,
"session": {
"secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK",
"cookie": {
"lifetime": 100
}
}
}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/*"
}]]
)

if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed

=== TEST 3: Call to route to get session
--- config
location /t {
content_by_lua_block {
local http = require "resty.http"
local login_keycloak = require("lib.keycloak").login_keycloak
local concatenate_cookies = require("lib.keycloak").concatenate_cookies

local current_time = os.time()
local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello"

local res, err = login_keycloak(uri, "teacher@gmail.com", "123456")
if err then
ngx.status = 500
ngx.say(err)
return
end

local cookie_str = concatenate_cookies(res.headers['Set-Cookie'])
local parts = {}
for part in string.gmatch(cookie_str, "[^|]+") do
table.insert(parts, part)
end
local target_number = tonumber(parts[2], 10) - 100
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't set the cookie.lifetime, what will the target_number be?
If we want to use the condition if target_number >= current_time then, the default value of cookie.lifetime should be less than 100

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value of cookie.lifetime is 3600.
Yes, you are right. 100 is not the right condition, I will change it to a larger number than the default value.

-- ngx.say(target_number, current_time)
-- It takes time to execute the request, and there may be delays. So subtract one second.
if target_number == current_time or target_number -1 == current_time then
monkeyDluffy6017 marked this conversation as resolved.
Show resolved Hide resolved
ngx.say("passed")
end
}
}
--- response_body
passed