Skip to content

Commit 0b8b5cd

Browse files
authored
change(openid-connect): when bearer_only is false, require the user to fill in session.secret (#12609)
1 parent 7cbf8ee commit 0b8b5cd

File tree

11 files changed

+163
-55
lines changed

11 files changed

+163
-55
lines changed

apisix/plugins/openid-connect.lua

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
local core = require("apisix.core")
1919
local ngx_re = require("ngx.re")
2020
local openidc = require("resty.openidc")
21-
local random = require("resty.random")
2221
local fetch_secrets = require("apisix.secret").fetch_secrets
2322
local jsonschema = require('jsonschema')
2423
local string = string
@@ -347,12 +346,7 @@ function _M.check_schema(conf)
347346
end
348347

349348
if not conf.bearer_only and not conf.session then
350-
core.log.warn("when bearer_only = false, " ..
351-
"you'd better complete the session configuration manually")
352-
conf.session = {
353-
-- generate a secret when bearer_only = false and no secret is configured
354-
secret = ngx_encode_base64(random.bytes(32, true) or random.bytes(32))
355-
}
349+
return false, "property \"session.secret\" is required when \"bearer_only\" is false"
356350
end
357351

358352
local check = {"discovery", "introspection_endpoint", "redirect_uri",

docs/en/latest/plugins/openid-connect.md

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ The `openid-connect` Plugin supports the integration with [OpenID Connect (OIDC)
6464
| set_userinfo_header | boolean | False | true | | If true and if user info data is available, set the value in the `X-Userinfo` request header. |
6565
| set_refresh_token_header | boolean | False | false | | If true and if the refresh token is available, set the value in the `X-Refresh-Token` request header. |
6666
| session | object | False | | | Session configuration used when `bearer_only` is `false` and the Plugin uses Authorization Code flow. |
67-
| session.secret | string | True | | 16 or more characters | Key used for session encryption and HMAC operation when `bearer_only` is `false`. It is automatically generated and saved to etcd if not configured. When using APISIX in the standalone mode where etcd is no longer the configuration center, the `secret` should be configured. |
67+
| session.secret | string | True | | 16 or more characters | Key used for session encryption and HMAC operation when `bearer_only` is `false`. |
6868
| session.cookie | object | False | | | Cookie configurations. |
6969
| session.cookie.lifetime | integer | False | 3600 | | Cookie lifetime in seconds. |
7070
| session_contents | object | False | | | Session content configurations. If unconfigured, all data will be stored in the session. |
@@ -231,17 +231,11 @@ To properly configure the redirection URI, make sure that the `redirect_uri` mat
231231

232232
You should also ensure that the `redirect_uri` include the scheme, such as `http` or `https`.
233233

234-
#### 2. Missing Session Secret
235-
236-
If you deploy APISIX in the [standalone mode](/apisix/production/deployment-modes#standalone-mode), make sure that `session.secret` is configured.
237-
238-
User sessions are stored in browser as cookies and encrypted with session secret. The secret is automatically generated and saved to etcd if no secret is configured through the `session.secret` attribute. However, in standalone mode, etcd is no longer the configuration center. Therefore, you should explicitly configure `session.secret` for this Plugin in the YAML configuration center `apisix.yaml`.
239-
240-
#### 3. Cookie Not Sent or Absent
234+
#### 2. Cookie Not Sent or Absent
241235

242236
Check if the [`SameSite`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value) cookie attribute is properly set (i.e. if your application needs to send the cookie cross sites) to see if this could be a factor that prevents the cookie being saved to the browser's cookie jar or being sent from the browser.
243237

244-
#### 4. Upstream Sent Too Big Header
238+
#### 3. Upstream Sent Too Big Header
245239

246240
If you have NGINX sitting in front of APISIX to proxy client traffic, see if you observe the following error in NGINX's `error.log`:
247241

@@ -253,11 +247,11 @@ If so, try adjusting `proxy_buffers`, `proxy_buffer_size`, and `proxy_busy_buffe
253247

254248
Another option is to configure the `session_content` attribute to adjust which data to store in session. For instance, you can set `session_content.access_token` to `true`.
255249

256-
#### 5. Invalid Client Secret
250+
#### 4. Invalid Client Secret
257251

258252
Verify if `client_secret` is valid and correct. An invalid `client_secret` would lead to an authentication failure and no token shall be returned and stored in session.
259253

260-
#### 6. PKCE IdP Configuration
254+
#### 5. PKCE IdP Configuration
261255

262256
If you are enabling PKCE with the authorization code flow, make sure you have configured the IdP client to use PKCE. For example, in Keycloak, you should configure the PKCE challenge method in the client's advanced settings:
263257

docs/en/latest/tutorials/keycloak-oidc.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
177177
"plugins": {
178178
"openid-connect": {
179179
"bearer_only": false,
180+
"session": {
181+
"secret": "change_to_whatever_secret_you_want"
182+
},
180183
"client_id": "'"$OIDC_CLIENT_ID"'",
181184
"client_secret": "'"$OIDC_CLIENT_SECRET"'",
182185
"discovery": "'"$OIDC_DISCOVERY"'",
@@ -203,6 +206,9 @@ curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
203206
"plugins": {
204207
"openid-connect": {
205208
"bearer_only": false,
209+
"session": {
210+
"secret": "change_to_whatever_secret_you_want"
211+
},
206212
"use_pkce": true,
207213
"client_id": "'"$OIDC_CLIENT_ID"'",
208214
"client_secret": "'"$OIDC_CLIENT_SECRET"'",

docs/zh/latest/plugins/openid-connect.md

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ description: openid-connect 插件支持与 OpenID Connect (OIDC) 身份提供
6464
| set_userinfo_header | boolean || true | | 如果为 true 并且用户信息数据可用,则在 `X-Userinfo` 请求标头中设置值。 |
6565
| set_refresh_token_header | boolean || false | | 如果为 true 并且刷新令牌可用,则在 `X-Refresh-Token` 请求标头中设置值。 |
6666
| session | object || | |`bearer_only``false` 且插件使用 Authorization Code 流程时使用的 Session 配置。 |
67-
| session.secret | string || | 16 个字符以上 |`bearer_only``false` 时,用于 session 加密和 HMAC 运算的密钥,若未配置则自动生成并保存到 etcd。当在独立模式下使用 APISIX 时,etcd 不再是配置中心,需要配置 `secret`|
67+
| session.secret | string || | 16 个字符以上 |`bearer_only``false` 时,用于 session 加密和 HMAC 运算的密钥|
6868
| session.cookie | object || | | Cookie 配置。 |
6969
| session.cookie.lifetime | integer || 3600 | | Cookie 生存时间(秒)。|
7070
| unauth_action | string || auth | ["auth","deny","pass"] | 未经身份验证的请求的操作。设置为 `auth` 时,重定向到 OpenID 提供程序的身份验证端点。设置为 `pass` 时,允许请求而无需身份验证。设置为 `deny` 时,返回 401 未经身份验证的响应,而不是启动授权代码授予流程。|
@@ -232,17 +232,11 @@ the error request to the redirect_uri path, but there's no session state found
232232

233233
您还应该确保 `redirect_uri` 包含 scheme,例如 `http``https`
234234

235-
#### 2. 缺少 Session Secret
236-
237-
如果您在[standalone 模式](../../../en/latest/deployment-modes.md#standalone)下部署 APISIX,请确保配置了 `session.secret`
238-
239-
用户 session 作为 cookie 存储在浏览器中,并使用 session 密钥进行加密。如果没有通过 `session.secret` 属性配置机密,则会自动生成机密并将其保存到 etcd。然而,在独立模式下,etcd 不再是配置中心。因此,您应该在 YAML 配置中心 `apisix.yaml` 中为此插件显式配置 `session.secret`
240-
241-
#### 3. Cookie 未发送或不存在
235+
#### 2. Cookie 未发送或不存在
242236

243237
检查 [`SameSite`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value) cookie 属性是否已正确设置(即您的应用程序是否需要跨站点发送 cookie),看看这是否会成为阻止 cookie 保存到浏览器的 cookie jar 或从浏览器发送的因素。
244238

245-
#### 4. 上游发送的标头太大
239+
#### 3. 上游发送的标头太大
246240

247241
如果您有 NGINX 位于 APISIX 前面来代理客户端流量,请查看 NGINX 的 `error.log` 中是否观察到以下错误:
248242

@@ -254,11 +248,11 @@ upstream sent too big header while reading response header from upstream
254248

255249
另一个选项是配置 `session_content` 属性来调整在会话中存储哪些数据。例如,你可以将 `session_content.access_token` 设置为 `true`
256250

257-
#### 5. 无效的客户端密钥
251+
#### 4. 无效的客户端密钥
258252

259253
验证 `client_secret` 是否有效且正确。无效的 `client_secret` 将导致身份验证失败,并且不会返回任何令牌并将其存储在 session 中。
260254

261-
#### 6. PKCE IdP 配置
255+
#### 5. PKCE IdP 配置
262256

263257
如果您使用授权码流程启用 PKCE,请确保您已将 IdP 客户端配置为使用 PKCE。例如,在 Keycloak 中,您应该在客户端的高级设置中配置 PKCE 质询方法:
264258

docs/zh/latest/tutorials/keycloak-oidc.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
177177
"plugins": {
178178
"openid-connect": {
179179
"bearer_only": false,
180+
"session": {
181+
"secret": "change_to_whatever_secret_you_want"
182+
},
180183
"client_id": "'"$OIDC_CLIENT_ID"'",
181184
"client_secret": "'"$OIDC_CLIENT_SECRET"'",
182185
"discovery": "'"$OIDC_DISCOVERY"'",
@@ -203,6 +206,9 @@ curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
203206
"plugins": {
204207
"openid-connect": {
205208
"bearer_only": false,
209+
"session": {
210+
"secret": "change_to_whatever_secret_you_want"
211+
},
206212
"use_pkce": true,
207213
"client_id": "'"$OIDC_CLIENT_ID"'",
208214
"client_secret": "'"$OIDC_CLIENT_SECRET"'",

t/plugin/openid-connect.t

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ __DATA__
4242
location /t {
4343
content_by_lua_block {
4444
local plugin = require("apisix.plugins.openid-connect")
45-
local ok, err = plugin.check_schema({client_id = "a", client_secret = "b", discovery = "c"})
45+
local ok, err = plugin.check_schema({
46+
client_id = "a",
47+
client_secret = "b",
48+
discovery = "c",
49+
session = {secret = "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"}
50+
})
4651
if not ok then
4752
ngx.say(err)
4853
end
@@ -60,7 +65,11 @@ done
6065
location /t {
6166
content_by_lua_block {
6267
local plugin = require("apisix.plugins.openid-connect")
63-
local ok, err = plugin.check_schema({client_secret = "b", discovery = "c"})
68+
local ok, err = plugin.check_schema({
69+
client_secret = "b",
70+
discovery = "c",
71+
session = {secret = "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"}
72+
})
6473
if not ok then
6574
ngx.say(err)
6675
end
@@ -79,7 +88,12 @@ done
7988
location /t {
8089
content_by_lua_block {
8190
local plugin = require("apisix.plugins.openid-connect")
82-
local ok, err = plugin.check_schema({client_id = 123, client_secret = "b", discovery = "c"})
91+
local ok, err = plugin.check_schema({
92+
client_id = 123,
93+
client_secret = "b",
94+
discovery = "c",
95+
session = {secret = "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"}
96+
})
8397
if not ok then
8498
ngx.say(err)
8599
end
@@ -111,7 +125,10 @@ done
111125
"ssl_verify": false,
112126
"timeout": 10,
113127
"scope": "apisix",
114-
"use_pkce": false
128+
"use_pkce": false,
129+
"session": {
130+
"secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"
131+
}
115132
}
116133
},
117134
"upstream": {
@@ -204,7 +221,10 @@ true
204221
"access_token_in_authorization_header": false,
205222
"set_id_token_header": true,
206223
"set_userinfo_header": true,
207-
"set_refresh_token_header": true
224+
"set_refresh_token_header": true,
225+
"session": {
226+
"secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"
227+
}
208228
}
209229
},
210230
"upstream": {
@@ -308,7 +328,10 @@ x-userinfo: ey.*
308328
"set_access_token_header": true,
309329
"access_token_in_authorization_header": true,
310330
"set_id_token_header": false,
311-
"set_userinfo_header": false
331+
"set_userinfo_header": false,
332+
"session": {
333+
"secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"
334+
}
312335
}
313336
},
314337
"upstream": {
@@ -901,22 +924,20 @@ OIDC introspection failed: invalid token
901924
client_id = "kbyuFDidLLm280LIwVFiazOqjO3ty8KH",
902925
client_secret = "60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa",
903926
discovery = "http://127.0.0.1:1980/.well-known/openid-configuration",
927+
session = {
928+
secret = "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"
929+
},
904930
}
905931
local ok, err = plugin.check_schema(s)
906932
if not ok then
907933
ngx.say(err)
908934
end
909935
910-
-- ensure session secret generated when bearer_only = false
911-
-- then remove it from table, because it's a random value that I cannot verify it by response body
912-
assert(s.session and s.session.secret, "no session secret generated")
913-
s.session = nil
914-
915936
ngx.say(json.encode(s))
916937
}
917938
}
918939
--- response_body
919-
{"accept_none_alg":false,"accept_unsupported_alg":true,"access_token_expires_leeway":0,"access_token_in_authorization_header":false,"bearer_only":false,"client_id":"kbyuFDidLLm280LIwVFiazOqjO3ty8KH","client_jwt_assertion_expires_in":60,"client_secret":"60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa","discovery":"http://127.0.0.1:1980/.well-known/openid-configuration","force_reauthorize":false,"iat_slack":120,"introspection_endpoint_auth_method":"client_secret_basic","introspection_interval":0,"jwk_expires_in":86400,"jwt_verification_cache_ignore":false,"logout_path":"/logout","realm":"apisix","renew_access_token_on_expiry":true,"revoke_tokens_on_logout":false,"scope":"openid","set_access_token_header":true,"set_id_token_header":true,"set_refresh_token_header":false,"set_userinfo_header":true,"ssl_verify":false,"timeout":3,"token_endpoint_auth_method":"client_secret_basic","unauth_action":"auth","use_nonce":false,"use_pkce":false}
940+
{"accept_none_alg":false,"accept_unsupported_alg":true,"access_token_expires_leeway":0,"access_token_in_authorization_header":false,"bearer_only":false,"client_id":"kbyuFDidLLm280LIwVFiazOqjO3ty8KH","client_jwt_assertion_expires_in":60,"client_secret":"60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa","discovery":"http://127.0.0.1:1980/.well-known/openid-configuration","force_reauthorize":false,"iat_slack":120,"introspection_endpoint_auth_method":"client_secret_basic","introspection_interval":0,"jwk_expires_in":86400,"jwt_verification_cache_ignore":false,"logout_path":"/logout","realm":"apisix","renew_access_token_on_expiry":true,"revoke_tokens_on_logout":false,"scope":"openid","session":{"secret":"jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"},"set_access_token_header":true,"set_id_token_header":true,"set_refresh_token_header":false,"set_userinfo_header":true,"ssl_verify":false,"timeout":3,"token_endpoint_auth_method":"client_secret_basic","unauth_action":"auth","use_nonce":false,"use_pkce":false}
920941
921942
922943
@@ -1076,7 +1097,10 @@ OIDC introspection failed: invalid jwt: invalid jwt string
10761097
"access_token_in_authorization_header": false,
10771098
"set_id_token_header": true,
10781099
"set_userinfo_header": true,
1079-
"post_logout_redirect_uri": "http://127.0.0.1:]] .. ngx.var.server_port .. [[/hello"
1100+
"post_logout_redirect_uri": "http://127.0.0.1:]] .. ngx.var.server_port .. [[/hello",
1101+
"session": {
1102+
"secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"
1103+
}
10801104
}
10811105
},
10821106
"upstream": {
@@ -1183,7 +1207,10 @@ http://127.0.0.1:.*/hello
11831207
"ssl_verify": false,
11841208
"timeout": 10,
11851209
"scope": "apisix",
1186-
"use_pkce": true
1210+
"use_pkce": true,
1211+
"session": {
1212+
"secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"
1213+
}
11871214
}
11881215
},
11891216
"upstream": {
@@ -1360,7 +1387,10 @@ x-userinfo: ey.*
13601387
"discovery": "https://samples.auth0.com/.well-known/openid-configuration",
13611388
"redirect_uri": "https://iresty.com",
13621389
"post_logout_redirect_uri": "https://iresty.com",
1363-
"scope": "openid profile"
1390+
"scope": "openid profile",
1391+
"session": {
1392+
"secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"
1393+
}
13641394
}
13651395
},
13661396
"upstream": {
@@ -1421,7 +1451,10 @@ true
14211451
"discovery": "https://accounts.google.com/.well-known/openid-configuration",
14221452
"redirect_uri": "https://iresty.com",
14231453
"post_logout_redirect_uri": "https://iresty.com",
1424-
"scope": "openid profile"
1454+
"scope": "openid profile",
1455+
"session": {
1456+
"secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"
1457+
}
14251458
}
14261459
},
14271460
"upstream": {
@@ -1485,6 +1518,9 @@ true
14851518
"ssl_verify": false,
14861519
"timeout": 10,
14871520
"bearer_only": false,
1521+
"session": {
1522+
"secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK"
1523+
},
14881524
"use_jwks": true,
14891525
"realm": "University",
14901526
"introspection_endpoint_auth_method": "client_secret_post",
@@ -1569,3 +1605,26 @@ true
15691605
qr/token validate successfully by \w+/
15701606
--- grep_error_log_out
15711607
token validate successfully by jwks
1608+
1609+
1610+
1611+
=== TEST 41: Missing `session.secret`.
1612+
--- config
1613+
location /t {
1614+
content_by_lua_block {
1615+
local plugin = require("apisix.plugins.openid-connect")
1616+
local ok, err = plugin.check_schema({
1617+
client_id = "a",
1618+
client_secret = "b",
1619+
discovery = "c",
1620+
})
1621+
if not ok then
1622+
ngx.say(err)
1623+
end
1624+
1625+
ngx.say("done")
1626+
}
1627+
}
1628+
--- response_body
1629+
property "session.secret" is required when "bearer_only" is false
1630+
done

0 commit comments

Comments
 (0)