From cda3caf7373848d4f23166c4cb2871a10422092e Mon Sep 17 00:00:00 2001 From: Diego Rojas Date: Tue, 20 Oct 2020 13:55:56 -0300 Subject: [PATCH 01/77] Update Dockerfile image --- tests/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Dockerfile b/tests/Dockerfile index caaa3856..e433be9a 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -1,4 +1,4 @@ -FROM openresty/openresty:xenial +FROM openresty/openresty:bionic # install dependencies RUN ["luarocks", "install", "lua-resty-session"] From df75c6e26cb7dbc9d78927d989e98be04173f10b Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Tue, 20 Oct 2020 19:47:29 +0200 Subject: [PATCH 02/77] redirect kill's stderr as it causes too much noise --- tests/spec/test_support.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/spec/test_support.lua b/tests/spec/test_support.lua index 475c0ee7..e074facd 100644 --- a/tests/spec/test_support.lua +++ b/tests/spec/test_support.lua @@ -537,7 +537,7 @@ local function kill(pid, signal) else signal = "-" .. signal .. " " end - return os.execute("/bin/kill " .. signal .. pid) + return os.execute("/bin/sh -c '/bin/kill " .. signal .. pid .. "' 2>/dev/null") end local function is_running(pid) From 68504deaafa9f5381345d6643c2e0b66041f6161 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Tue, 17 Nov 2020 17:14:00 +0100 Subject: [PATCH 03/77] don't fix lua-resty-jwt version to 0.2.0 closes #362 closes #363 closes #366 Signed-off-by: Stefan Bodewig --- ChangeLog | 5 +++++ lua-resty-openidc-1.7.3-1.rockspec | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 2d064f89..094b2247 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +11/17/2020 +- changed dependency on lua-resty-jwt to allow newer versions in + luarocks packaging; see #363, #366, #362; + thanks to @Darguelles and @kayano + 09/20/2020 - release 1.7.3 diff --git a/lua-resty-openidc-1.7.3-1.rockspec b/lua-resty-openidc-1.7.3-1.rockspec index 3e431a51..f2d63ceb 100644 --- a/lua-resty-openidc-1.7.3-1.rockspec +++ b/lua-resty-openidc-1.7.3-1.rockspec @@ -25,7 +25,7 @@ dependencies = { "lua >= 5.1", "lua-resty-http >= 0.08", "lua-resty-session >= 2.8", - "lua-resty-jwt == 0.2.0" + "lua-resty-jwt >= 0.2.0" } build = { type = "builtin", From 0c75741b41bc9a8b5dbe0b27f81a2851a6c68b60 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Tue, 17 Nov 2020 17:42:16 +0100 Subject: [PATCH 04/77] release 1.7.4 Signed-off-by: Hans Zandbelt --- .github/issue_template.md | 2 +- .travis.yml | 2 +- ChangeLog | 1 + lib/resty/openidc.lua | 2 +- ...idc-1.7.3-1.rockspec => lua-resty-openidc-1.7.4-1.rockspec | 4 ++-- 5 files changed, 6 insertions(+), 5 deletions(-) rename lua-resty-openidc-1.7.3-1.rockspec => lua-resty-openidc-1.7.4-1.rockspec (97%) diff --git a/.github/issue_template.md b/.github/issue_template.md index 63c9a494..11efd3ee 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -7,7 +7,7 @@ A new issue about a bug should be verified with a minimized example. ###### Environment -- lua-resty-openidc version (e.g. 1.7.3) +- lua-resty-openidc version (e.g. 1.7.4) - OpenID Connect provider (e.g. Keycloak, Azure AD) ###### Expected behaviour diff --git a/.travis.yml b/.travis.yml index faa95426..df5536e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ services: env: global: - - VERSION=1.7.3-1 + - VERSION=1.7.4-1 - NAME=lua-resty-openidc - ROCKSPEC=$NAME-$VERSION.rockspec - LUAROCKS=2.3.0 diff --git a/ChangeLog b/ChangeLog index 094b2247..e10f54b7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ - changed dependency on lua-resty-jwt to allow newer versions in luarocks packaging; see #363, #366, #362; thanks to @Darguelles and @kayano +- release 1.7.4 09/20/2020 - release 1.7.3 diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 9bd4551e..2e794e07 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -84,7 +84,7 @@ local supported_token_auth_methods = { } local openidc = { - _VERSION = "1.7.3" + _VERSION = "1.7.4" } openidc.__index = openidc diff --git a/lua-resty-openidc-1.7.3-1.rockspec b/lua-resty-openidc-1.7.4-1.rockspec similarity index 97% rename from lua-resty-openidc-1.7.3-1.rockspec rename to lua-resty-openidc-1.7.4-1.rockspec index f2d63ceb..b36bc630 100644 --- a/lua-resty-openidc-1.7.3-1.rockspec +++ b/lua-resty-openidc-1.7.4-1.rockspec @@ -1,8 +1,8 @@ package = "lua-resty-openidc" -version = "1.7.3-1" +version = "1.7.4-1" source = { url = "git://github.com/zmartzone/lua-resty-openidc", - tag = "v1.7.3", + tag = "v1.7.4", dir = "lua-resty-openidc" } description = { From 49be8033b7a3dc0557c133f8616645ca19485950 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Tue, 17 Nov 2020 17:48:49 +0100 Subject: [PATCH 05/77] upgrade Travis luarocks Signed-off-by: Hans Zandbelt --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index df5536e8..d668980c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ env: - VERSION=1.7.4-1 - NAME=lua-resty-openidc - ROCKSPEC=$NAME-$VERSION.rockspec - - LUAROCKS=2.3.0 + - LUAROCKS=3.4.0 matrix: - LUA=lua5.1 From f1886fa4553516c193961467062c0ae26bb39108 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sat, 5 Dec 2020 09:51:51 +0100 Subject: [PATCH 06/77] close session in access_token() and a certain path in authenticate() closes #374 Signed-off-by: Stefan Bodewig --- ChangeLog | 8 ++++++++ lib/resty/openidc.lua | 12 +++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index e10f54b7..ee03d78d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +12/05/2020 +- fixed a session leak in access_token() and for a very unlikely + code-path in authenticate(); authenticate will still normally not + close the session as users may want to use it after the method + returns; see + https://github.com/zmartzone/lua-resty-openidc#sessions-and-locking + see #374 + 11/17/2020 - changed dependency on lua-resty-jwt to allow newer versions in luarocks packaging; see #363, #366, #362; diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 2e794e07..83d0aa2a 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -265,7 +265,7 @@ local function get_host_name(headers) end -- assemble the redirect_uri -local function openidc_get_redirect_uri(opts) +local function openidc_get_redirect_uri(opts, session) local path = opts.redirect_uri_path if opts.redirect_uri then if opts.redirect_uri:sub(1, 1) == '/' then @@ -279,6 +279,7 @@ local function openidc_get_redirect_uri(opts) local host = get_host_name(headers) if not host then -- possibly HTTP 1.0 and no Host header + if session then session:close() end ngx.exit(ngx.HTTP_BAD_REQUEST) end return scheme .. "://" .. host .. path @@ -338,7 +339,7 @@ local function openidc_authorize(opts, session, target_url, prompt) client_id = opts.client_id, response_type = "code", scope = opts.scope and opts.scope or "openid email profile", - redirect_uri = openidc_get_redirect_uri(opts), + redirect_uri = openidc_get_redirect_uri(opts, session), state = state, } @@ -1115,7 +1116,7 @@ local function openidc_authorization_response(opts, session) local body = { grant_type = "authorization_code", code = args.code, - redirect_uri = openidc_get_redirect_uri(opts), + redirect_uri = openidc_get_redirect_uri(opts, session), state = session.data.state, code_verifier = session.data.code_verifier } @@ -1514,8 +1515,9 @@ end function openidc.access_token(opts, session_opts) local session = r_session.start(session_opts) - - return openidc_access_token(opts, session, true) + local token, err = openidc_access_token(opts, session, true) + session:close() + return token, err end From e35fa162176382846a1d11018aff46c63fa8e87c Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Wed, 9 Dec 2020 20:58:17 +0100 Subject: [PATCH 07/77] typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5a5e83c5..eb79cf71 100644 --- a/README.md +++ b/README.md @@ -300,9 +300,9 @@ local res, err = require("resty.openidc").authenticate(opts, nil, "deny") ## Sessions and Locking -The `authenicate` function returns the current session object as its +The `authenticate` function returns the current session object as its forth return argument. If you have configured lua-resty-session to use -a server side storade backend that uses locking, the session may still +a server side storage backend that uses locking, the session may still be locked when it is returned. In this case you may want to close it explicitly From 6534df04b339a289f0a0ff260249dc0927b04f12 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Tue, 23 Mar 2021 21:41:12 +0100 Subject: [PATCH 08/77] update support and disclaimer text Signed-off-by: Hans Zandbelt --- DISCLAIMER | 6 ++++++ README.md | 13 ++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/DISCLAIMER b/DISCLAIMER index 44407015..144142b5 100644 --- a/DISCLAIMER +++ b/DISCLAIMER @@ -1,4 +1,10 @@ /*************************************************************************** + * Copyright (C) 2017-2021 ZmartZone IAM + * All rights reserved. + * + * ZmartZone IAM + * https://www.zmartzone.eu + * * Copyright (C) 2014-2017 Ping Identity Corporation * All rights reserved. * diff --git a/README.md b/README.md index 5a5e83c5..3a01be0f 100644 --- a/README.md +++ b/README.md @@ -63,16 +63,10 @@ If you are using [OpenResty](http://openresty.org/), the default location would ## Support -#### Community Support - For generic questions, see the Wiki pages with Frequently Asked Questions at: [https://github.com/zmartzone/lua-resty-openidc/wiki](https://github.com/zmartzone/lua-resty-openidc/wiki) -Any questions/issues should go to issues tracker. - -#### Commercial Services +Any questions/issues should go to the Github Discussons or Issues tracker. -For commercial Support contracts, Professional Services, Training and use-case specific support you can contact: -[sales@zmartzone.eu](mailto:sales@zmartzone.eu) ## Sample Configuration for Google+ Signin @@ -618,5 +612,6 @@ as the second command Disclaimer ---------- -*This software is open sourced by ZmartZone IAM. For commercial support -you can contact [ZmartZone IAM](https://www.zmartzone.eu) as described above in the [Support](#support) section.* +*This software is open sourced by ZmartZone IAM but not supported commercially as such. +Any questions/issues should go to the Github Discussions or Issues tracker. +See also the DISCLAIMER file in this directory.* From 59d01573d7d8eeed4cd60666af1cdc90ff8be80b Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Tue, 23 Mar 2021 21:44:36 +0100 Subject: [PATCH 09/77] move support text to end of README Signed-off-by: Hans Zandbelt --- README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c55bccff..00d67176 100644 --- a/README.md +++ b/README.md @@ -61,13 +61,6 @@ Otherwise copy `openidc.lua` somewhere in your `lua_package_path` under a direct If you are using [OpenResty](http://openresty.org/), the default location would be `/usr/local/openresty/lualib/resty`. -## Support - -For generic questions, see the Wiki pages with Frequently Asked Questions at: -[https://github.com/zmartzone/lua-resty-openidc/wiki](https://github.com/zmartzone/lua-resty-openidc/wiki) -Any questions/issues should go to the Github Discussons or Issues tracker. - - ## Sample Configuration for Google+ Signin Sample `nginx.conf` configuration for authenticating users against Google+ Signin, protecting a reverse-proxied path. @@ -609,8 +602,14 @@ $ docker run -it --rm -e coverage=t lua-resty-openidc/test:latest as the second command -Disclaimer ----------- +## Support + +For generic questions, see the Wiki pages with Frequently Asked Questions at: +[https://github.com/zmartzone/lua-resty-openidc/wiki](https://github.com/zmartzone/lua-resty-openidc/wiki) +Any questions/issues should go to the Github Discussons or Issues tracker. + + +## Disclaimer *This software is open sourced by ZmartZone IAM but not supported commercially as such. Any questions/issues should go to the Github Discussions or Issues tracker. From e63cde57b0a4d8a85af9bc0c9972023a3e4268b0 Mon Sep 17 00:00:00 2001 From: Arcadiy Ivanov Date: Mon, 3 May 2021 16:20:36 -0400 Subject: [PATCH 10/77] Allow lifecycle handlers to fail, altering the course of request processing --- lib/resty/openidc.lua | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 83d0aa2a..705c157a 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -327,6 +327,7 @@ end local function openidc_authorize(opts, session, target_url, prompt) local resty_random = require("resty.random") local resty_string = require("resty.string") + local err -- generate state and nonce local state = resty_string.to_hex(resty_random.bytes(16)) @@ -373,7 +374,11 @@ local function openidc_authorize(opts, session, target_url, prompt) session.data.last_authenticated = ngx.time() if opts.lifecycle and opts.lifecycle.on_created then - opts.lifecycle.on_created(session) + err = opts.lifecycle.on_created(session) + if err then + log(WARN, "failed in `on_created` handler: " .. err) + return err + end end session:save() @@ -1178,7 +1183,11 @@ local function openidc_authorization_response(opts, session) end if opts.lifecycle and opts.lifecycle.on_authenticated then - opts.lifecycle.on_authenticated(session) + err = opts.lifecycle.on_authenticated(session) + if err then + log(WARN, "failed in `on_authenticated` handler: " .. err) + return nil, err, session.data.original_url, session + end end -- save the session with the obtained id_token @@ -1235,9 +1244,14 @@ local function openidc_logout(opts, session) local session_token = session.data.enc_id_token local access_token = session.data.access_token local refresh_token = session.data.refresh_token + local err if opts.lifecycle and opts.lifecycle.on_logout then - opts.lifecycle.on_logout(session) + err = opts.lifecycle.on_logout(session) + if err then + log(WARN, "failed in `on_logout` handler: " .. err) + return err + end end session:destroy() @@ -1367,7 +1381,11 @@ local function openidc_access_token(opts, session, try_to_renew) return nil, err end if opts.lifecycle and opts.lifecycle.on_regenerated then - opts.lifecycle.on_regenerated(session) + err = opts.lifecycle.on_regenerated(session) + if err then + log(WARN, "failed in `on_regenerated` handler: " .. err) + return nil, err + end end return session.data.access_token, err From 56ea9b5e3473627b845d8108b2530913d10f2df7 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sun, 19 Sep 2021 13:37:36 +0200 Subject: [PATCH 11/77] honor introspection_cache_ignore in jwt_verify functions see #399 --- ChangeLog | 4 ++++ README.md | 3 +++ lib/resty/openidc.lua | 12 +++++++++--- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index ee03d78d..3d5b0e15 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +09/19/2021 +- made jwt_verify() and bearer_jwt_verify() honor + opts.introspection_cache_ignore as well. + 12/05/2020 - fixed a session leak in access_token() and for a very unlikely code-path in authenticate(); authenticate will still normally not diff --git a/README.md b/README.md index 00d67176..1c0ce026 100644 --- a/README.md +++ b/README.md @@ -375,6 +375,9 @@ lAc5Csj0o5Q+oEhPUAVBIF07m4rd0OvAVPOCQ2NJhQSL1oWASbf+fg== -- the expiration time in seconds for jwk cache, default is 1 day. --jwk_expires_in = 24 * 60 * 60 + -- It may be necessary to force an introspection call for a bearer token and ignore the existing cached + -- introspection results. If so you need to set set the introspection_cache_ignore option to true. + -- introspection_cache_ignore = true } -- call bearer_jwt_verify for OAuth 2.0 JWT validation diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 83d0aa2a..c4dfddf4 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1688,10 +1688,14 @@ end function openidc.jwt_verify(access_token, opts, ...) local err local json + local v + local introspection_cache_ignore = opts.introspection_cache_ignore or false local slack = opts.iat_slack and opts.iat_slack or 120 -- see if we've previously cached the validation result for this access token - local v = openidc_cache_get("introspection", access_token) + if not introspection_cache_ignore then + v = openidc_cache_get("introspection", access_token) + end if not v then local jwt_obj jwt_obj, err = openidc_load_jwt_and_verify_crypto(opts, access_token, opts.public_key, opts.symmetric_key, @@ -1700,8 +1704,10 @@ function openidc.jwt_verify(access_token, opts, ...) json = jwt_obj.payload log(DEBUG, "jwt: ", cjson.encode(json)) - local ttl = json.exp and json.exp - ngx.time() or 120 - openidc_cache_set("introspection", access_token, cjson.encode(json), ttl) + if not introspection_cache_ignore then + local ttl = json.exp and json.exp - ngx.time() or 120 + openidc_cache_set("introspection", access_token, cjson.encode(json), ttl) + end end else From 6ba3a5f658c4e541a9c9209ca17a0b6496533b6c Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Thu, 23 Sep 2021 21:15:14 +0200 Subject: [PATCH 12/77] record and document #384 --- ChangeLog | 4 ++++ README.md | 1 + 2 files changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 3d5b0e15..4c764d31 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +09/23/2021 +- if lifecyle handlers return truthy values they cause the operation + they are handlers of to fail; see #384; thanks to @arcivanov + 09/19/2021 - made jwt_verify() and bearer_jwt_verify() honor opts.introspection_cache_ignore as well. diff --git a/README.md b/README.md index 1c0ce026..6258e3c1 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,7 @@ h2JHukolz9xf6qN61QMLSd83+kwoBr2drp6xg3eGDLIkQCQLrkY= -- `openidc_logout` -- -- Any, all or none of the hooks may be used. Empty `lifecycle` does nothing. + -- A hook that returns a truthy value causes the lifecycle action they are taking part of to fail. -- Optional : add decorator for HTTP request that is -- applied when lua-resty-openidc talks to the OpenID Connect From a73ebd9f40475cb2c953e69159a30c36f884c8a4 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Wed, 22 Sep 2021 22:01:35 +0200 Subject: [PATCH 13/77] split caches of token introspection and JWT verification see #399 Signed-off-by: Stefan Bodewig --- ChangeLog | 7 +++--- README.md | 8 +++--- lib/resty/openidc.lua | 58 ++++++++++++++++++++++++++++--------------- 3 files changed, 46 insertions(+), 27 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4c764d31..cbeb423b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,9 +2,10 @@ - if lifecyle handlers return truthy values they cause the operation they are handlers of to fail; see #384; thanks to @arcivanov -09/19/2021 -- made jwt_verify() and bearer_jwt_verify() honor - opts.introspection_cache_ignore as well. +09/22/2021 +- made jwt_verify() and bearer_jwt_verify() use a separate cache named + "jwt_verification" and introduced opts.jwt_verification_cache_ignore + to disable caching completely 12/05/2020 - fixed a session leak in access_token() and for a very unlikely diff --git a/README.md b/README.md index 6258e3c1..7f902f0a 100644 --- a/README.md +++ b/README.md @@ -316,7 +316,7 @@ http { resolver 8.8.8.8; # cache for JWT verification results - lua_shared_dict introspection 10m; + lua_shared_dict jwt_verification 10m; server { listen 8080; @@ -376,9 +376,9 @@ lAc5Csj0o5Q+oEhPUAVBIF07m4rd0OvAVPOCQ2NJhQSL1oWASbf+fg== -- the expiration time in seconds for jwk cache, default is 1 day. --jwk_expires_in = 24 * 60 * 60 - -- It may be necessary to force an introspection call for a bearer token and ignore the existing cached - -- introspection results. If so you need to set set the introspection_cache_ignore option to true. - -- introspection_cache_ignore = true + -- It may be necessary to force verification for a bearer token and ignore the existing cached + -- verification results. If so you need to set set the jwt_verification_cache_ignore option to true. + -- jwt_verification_cache_ignore = true } -- call bearer_jwt_verify for OAuth 2.0 JWT validation diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index b4dfbfd9..435e363b 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -132,6 +132,7 @@ function openidc.invalidate_caches() openidc_cache_invalidate("discovery") openidc_cache_invalidate("jwks") openidc_cache_invalidate("introspection") + openidc_cache_invalidate("jwt_verification") end -- validate the contents of and id_token @@ -1612,6 +1613,20 @@ local function openidc_get_bearer_access_token(opts) return access_token, err end +local function get_cached_introspection(opts, access_token) + local introspection_cache_ignore = opts.introspection_cache_ignore or false + if not introspection_cache_ignore then + return openidc_cache_get("introspection", access_token) + end +end + +local function set_cached_introspection(opts, access_token, encoded_json, ttl) + local introspection_cache_ignore = opts.introspection_cache_ignore or false + if not introspection_cache_ignore then + openidc_cache_set("introspection", access_token, encoded_json, ttl) + end +end + -- main routine for OAuth 2.0 token introspection function openidc.introspect(opts) @@ -1623,12 +1638,7 @@ function openidc.introspect(opts) -- see if we've previously cached the introspection result for this access token local json - local v - local introspection_cache_ignore = opts.introspection_cache_ignore or false - - if not introspection_cache_ignore then - v = openidc_cache_get("introspection", access_token) - end + local v = get_cached_introspection(opts, access_token) if v then json = cjson.decode(v) @@ -1679,10 +1689,11 @@ function openidc.introspect(opts) end -- cache the results + local introspection_cache_ignore = opts.introspection_cache_ignore or false local expiry_claim = opts.introspection_expiry_claim or "exp" - local introspection_interval = opts.introspection_interval or 0 if not introspection_cache_ignore and json[expiry_claim] then + local introspection_interval = opts.introspection_interval or 0 local ttl = json[expiry_claim] if expiry_claim == "exp" then --https://tools.ietf.org/html/rfc7662#section-2.2 ttl = ttl - ngx.time() @@ -1693,39 +1704,46 @@ function openidc.introspect(opts) end end log(DEBUG, "cache token ttl: " .. ttl) - openidc_cache_set("introspection", access_token, cjson.encode(json), ttl) - + set_cached_introspection(opts, access_token, cjson.encode(json), ttl) end return json, err end +local function get_cached_jwt_verification(opts, access_token) + local jwt_verification_cache_ignore = opts.jwt_verification_cache_ignore or false + if not jwt_verification_cache_ignore then + return openidc_cache_get("jwt_verification", access_token) + end +end + +local function set_cached_jwt_verification(opts, access_token, encoded_json, ttl) + local jwt_verification_cache_ignore = opts.jwt_verification_cache_ignore or false + if not jwt_verification_cache_ignore then + openidc_cache_set("jwt_verification", access_token, encoded_json, ttl) + end +end + -- main routine for OAuth 2.0 JWT token validation -- optional args are claim specs, see jwt-validators in resty.jwt function openidc.jwt_verify(access_token, opts, ...) local err local json - local v - local introspection_cache_ignore = opts.introspection_cache_ignore or false + local v = get_cached_jwt_verification(opts, access_token) local slack = opts.iat_slack and opts.iat_slack or 120 - -- see if we've previously cached the validation result for this access token - if not introspection_cache_ignore then - v = openidc_cache_get("introspection", access_token) - end if not v then local jwt_obj jwt_obj, err = openidc_load_jwt_and_verify_crypto(opts, access_token, opts.public_key, opts.symmetric_key, opts.token_signing_alg_values_expected, ...) if not err then json = jwt_obj.payload - log(DEBUG, "jwt: ", cjson.encode(json)) + local encoded_json = cjson.encode(json) + log(DEBUG, "jwt: ", encoded_json) - if not introspection_cache_ignore then - local ttl = json.exp and json.exp - ngx.time() or 120 - openidc_cache_set("introspection", access_token, cjson.encode(json), ttl) - end + set_cached_jwt_verification(opts, access_token, encoded_json, + json.exp and json.exp - ngx.time() or 120) end else From d387399b411eac3a8596bf4e1bae7d5584ae822e Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Wed, 22 Sep 2021 22:26:16 +0200 Subject: [PATCH 14/77] take some configuration options into account for cache keys see #399 Signed-off-by: Stefan Bodewig --- lib/resty/openidc.lua | 63 +++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 435e363b..f3999807 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1613,17 +1613,41 @@ local function openidc_get_bearer_access_token(opts) return access_token, err end +local function get_introspection_endpoint(opts) + local introspection_endpoint = opts.introspection_endpoint + if not introspection_endpoint then + local err = openidc_ensure_discovered_data(opts) + if err then + return nil, "opts.introspection_endpoint not said and " .. err + end + local endpoint = opts.discovery and opts.discovery.introspection_endpoint + if endpoint then + return endpoint + end + end + return introspection_endpoint +end + +local function get_introspection_cache_prefix(opts) + return (get_introspection_endpoint(opts) or 'nil-endpoint') .. ',' + .. (opts.client_id or 'no-client_id') .. ',' + .. (opts.client_secret and 'secret' or 'no-client_secret') .. ':' +end + local function get_cached_introspection(opts, access_token) local introspection_cache_ignore = opts.introspection_cache_ignore or false if not introspection_cache_ignore then - return openidc_cache_get("introspection", access_token) + return openidc_cache_get("introspection", + get_introspection_cache_prefix(opts) .. access_token) end end local function set_cached_introspection(opts, access_token, encoded_json, ttl) local introspection_cache_ignore = opts.introspection_cache_ignore or false if not introspection_cache_ignore then - openidc_cache_set("introspection", access_token, encoded_json, ttl) + openidc_cache_set("introspection", + get_introspection_cache_prefix(opts) .. access_token, + encoded_json, ttl) end end @@ -1665,16 +1689,10 @@ function openidc.introspect(opts) end -- call the introspection endpoint - local introspection_endpoint = opts.introspection_endpoint - if not introspection_endpoint then - err = openidc_ensure_discovered_data(opts) - if err then - return nil, "opts.introspection_endpoint not said and " .. err - end - local endpoint = opts.discovery and opts.discovery.introspection_endpoint - if endpoint then - introspection_endpoint = endpoint - end + local introspection_endpoint + introspection_endpoint, err = get_introspection_endpoint(opts) + if err then + return nil, err end json, err = openidc.call_token_endpoint(opts, introspection_endpoint, body, opts.introspection_endpoint_auth_method, "introspection") @@ -1711,17 +1729,34 @@ function openidc.introspect(opts) end +local function get_jwt_verification_cache_prefix(opts) + local signing_alg_values_expected = (opts.accept_none_alg and 'none' or 'no-none') + local expected_algs = opts.token_signing_alg_values_expected or {} + if type(expected_algs) == 'string' then + expected_algs = { expected_algs } + end + for _, alg in ipairs(expected_algs) do + signing_alg_values_expected = signing_alg_values_expected .. ',' .. alg + end + return (opts.public_key or 'no-pubkey') .. ',' + .. (opts.symmetric_key or 'no-symkey') .. ',' + .. signing_alg_values_expected .. ':' +end + local function get_cached_jwt_verification(opts, access_token) local jwt_verification_cache_ignore = opts.jwt_verification_cache_ignore or false if not jwt_verification_cache_ignore then - return openidc_cache_get("jwt_verification", access_token) + return openidc_cache_get("jwt_verification", + get_jwt_verification_cache_prefix(opts) .. access_token) end end local function set_cached_jwt_verification(opts, access_token, encoded_json, ttl) local jwt_verification_cache_ignore = opts.jwt_verification_cache_ignore or false if not jwt_verification_cache_ignore then - openidc_cache_set("jwt_verification", access_token, encoded_json, ttl) + openidc_cache_set("jwt_verification", + get_jwt_verification_cache_prefix(opts) .. access_token, + encoded_json, ttl) end end From 1e1e66f99aed2ccd46a9f67486f7208955039835 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Thu, 23 Sep 2021 21:01:26 +0200 Subject: [PATCH 15/77] add a new option that allows caches to be segregated see #399 Signed-off-by: Stefan Bodewig --- ChangeLog | 5 ++++- README.md | 22 ++++++++++++++++++++++ lib/resty/openidc.lua | 6 ++++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index cbeb423b..ad8850f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,10 +2,13 @@ - if lifecyle handlers return truthy values they cause the operation they are handlers of to fail; see #384; thanks to @arcivanov +- added opts.cache_segment as option to shard the cache used by token + introspection or JWT verification; see #399 + 09/22/2021 - made jwt_verify() and bearer_jwt_verify() use a separate cache named "jwt_verification" and introduced opts.jwt_verification_cache_ignore - to disable caching completely + to disable caching completely; see #399 12/05/2020 - fixed a session leak in access_token() and for a very unlikely diff --git a/README.md b/README.md index 7f902f0a..b6ef3005 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,16 @@ local res, err, target, session = require("resty.openidc").authenticate(opts) session:close() ``` +## Caching of Introspection and JWT Verification Results + +Note the `jwt_verification` and `introspection` caches are shared +between all configured locations. If you are using locations with +different `opts` configuration the shared cache may allow a token that +is valid for only one location to be accepted by another if it is read +from the cache. In order to avoid cache confusion it is recommended to +set `opts.cache_segment` to unique strings for each set of related +locations. + ## Sample Configuration for OAuth 2.0 JWT Token Validation Sample `nginx.conf` configuration for verifying Bearer JWT Access Tokens against a pre-configured secret/key. @@ -379,6 +389,10 @@ lAc5Csj0o5Q+oEhPUAVBIF07m4rd0OvAVPOCQ2NJhQSL1oWASbf+fg== -- It may be necessary to force verification for a bearer token and ignore the existing cached -- verification results. If so you need to set set the jwt_verification_cache_ignore option to true. -- jwt_verification_cache_ignore = true + + -- optional name of a cache-segment if you need separate + -- caches for differently configured locations + -- cache_segment = 'api' } -- call bearer_jwt_verify for OAuth 2.0 JWT validation @@ -447,6 +461,10 @@ http { -- Defaults to "exp" - Controls the TTL of the introspection cache -- https://tools.ietf.org/html/rfc7662#section-2.2 -- introspection_expiry_claim = "exp" + + -- optional name of a cache-segment if you need separate + -- caches for differently configured locations + -- cache_segment = 'api' } -- call introspect for OAuth 2.0 Bearer Access Token validation @@ -547,6 +565,10 @@ http { -- It may be necessary to force an introspection call for an access_token and ignore the existing cached -- introspection results. If so you need to set set the introspection_cache_ignore option to true. -- introspection_cache_ignore = true + + -- optional name of a cache-segment if you need separate + -- caches for differently configured locations + -- cache_segment = 'api' } -- call introspect for OAuth 2.0 Bearer Access Token validation diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index f3999807..51d1447b 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1629,7 +1629,8 @@ local function get_introspection_endpoint(opts) end local function get_introspection_cache_prefix(opts) - return (get_introspection_endpoint(opts) or 'nil-endpoint') .. ',' + return (opts.cache_segment and opts.cache_segment.gsub(',', '_') or 'DEFAULT') .. ',' + .. (get_introspection_endpoint(opts) or 'nil-endpoint') .. ',' .. (opts.client_id or 'no-client_id') .. ',' .. (opts.client_secret and 'secret' or 'no-client_secret') .. ':' end @@ -1738,7 +1739,8 @@ local function get_jwt_verification_cache_prefix(opts) for _, alg in ipairs(expected_algs) do signing_alg_values_expected = signing_alg_values_expected .. ',' .. alg end - return (opts.public_key or 'no-pubkey') .. ',' + return (opts.cache_segment and opts.cache_segment.gsub(',', '_') or 'DEFAULT') .. ',' + .. (opts.public_key or 'no-pubkey') .. ',' .. (opts.symmetric_key or 'no-symkey') .. ',' .. signing_alg_values_expected .. ':' end From c2eb2d7768b14a11dd9a8cc4cd07ef04d5aaf1fa Mon Sep 17 00:00:00 2001 From: thorstenfleischmann Date: Wed, 13 Oct 2021 13:56:11 +0200 Subject: [PATCH 16/77] Make revoke_token() accessible New function revoke_token() and revoke_tokens() --- lib/resty/openidc.lua | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 51d1447b..5171b1d7 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1234,6 +1234,36 @@ local function openidc_revoke_token(opts, token_type_hint, token) end end +function openidc.revoke_token(opts, token_type_hint, token) + local err = openidc_ensure_discovered_data(opts) + if err then + log(ERROR, "revocation of " .. token_type_hint .. " unsuccessful: " .. err) + return false + end + + return openidc_revoke_token(opts, token_type_hint, token) +end + +function openidc.revoke_tokens(opts, session) + local err = openidc_ensure_discovered_data(opts) + if err then + log(ERROR, "revocation of tokens unsuccessful: " .. err) + return false + end + + local access_token = session.data.access_token + local refresh_token = session.data.refresh_token + + local access_token_revoke, refresh_token_revoke + if refresh_token then + access_token_revoke = openidc_revoke_token(opts, "refresh_token", refresh_token) + end + if access_token then + refresh_token_revoke = openidc_revoke_token(opts, "access_token", access_token) + end + return access_token_revoke and refresh_token_revoke +end + local openidc_transparent_pixel = "\137\080\078\071\013\010\026\010\000\000\000\013\073\072\068\082" .. "\000\000\000\001\000\000\000\001\008\004\000\000\000\181\028\012" .. "\002\000\000\000\011\073\068\065\084\120\156\099\250\207\000\000" .. From 8c75bc1fec534a7e6f0412ec27ba37063b330dfe Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Thu, 28 Oct 2021 16:33:42 +0200 Subject: [PATCH 17/77] fix potential null dereference in openidc_revoke_token's logs --- lib/resty/openidc.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 51d1447b..a9be4013 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1214,11 +1214,12 @@ local function openidc_revoke_token(opts, token_type_hint, token) if token_type_hint then body['token_type_hint'] = token_type_hint end + local token_type_log = token_type_hint or 'token' -- ensure revocation endpoint auth method is properly discovered local err = ensure_config(opts) if err then - log(ERROR, "revocation of " .. token_type_hint .. " unsuccessful: " .. err) + log(ERROR, "revocation of " .. token_type_log .. " unsuccessful: " .. err) return false end @@ -1226,10 +1227,10 @@ local function openidc_revoke_token(opts, token_type_hint, token) local _ _, err = openidc.call_token_endpoint(opts, opts.discovery.revocation_endpoint, body, opts.token_endpoint_auth_method, "revocation", true) if err then - log(ERROR, "revocation of " .. token_type_hint .. " unsuccessful: " .. err) + log(ERROR, "revocation of " .. token_type_log .. " unsuccessful: " .. err) return false else - log(DEBUG, "revocation of " .. token_type_hint .. " successful") + log(DEBUG, "revocation of " .. token_type_log .. " successful") return true end end From c4ac86cc30133ae03c138c5285b04568a440bc68 Mon Sep 17 00:00:00 2001 From: thorstenfleischmann Date: Tue, 2 Nov 2021 08:27:19 +0100 Subject: [PATCH 18/77] AUTHORS and README --- AUTHORS | 1 + README.md | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/AUTHORS b/AUTHORS index a848049b..15507a38 100644 --- a/AUTHORS +++ b/AUTHORS @@ -34,3 +34,4 @@ reporting bugs, providing fixes, suggesting useful features or other: Joshua Erney Nick Wiedenbrueck Eduardo Gonçalves + Thorsten Fleischmann diff --git a/README.md b/README.md index b6ef3005..2263ffd3 100644 --- a/README.md +++ b/README.md @@ -309,6 +309,12 @@ from the cache. In order to avoid cache confusion it is recommended to set `opts.cache_segment` to unique strings for each set of related locations. +## Revoke tokens + +The `revoke_tokens(opts, session)` function revokes the current refresh and access token. In contrast to a full logout, the session cookie will not be destroyed and the endsession endpoint will not be called. The function returns `true` if both tokens were revoked successfully. This function might be helpful in scenarios where you want to destroy/remove a session from the server side. + +With `revoke_token(opts, token_type_hint, token)` it is also possible to revoke a specific token. `token_type_hint` can usually be `refresh_token` or `access_token`. + ## Sample Configuration for OAuth 2.0 JWT Token Validation Sample `nginx.conf` configuration for verifying Bearer JWT Access Tokens against a pre-configured secret/key. From e001314268f619315b01686aac9bd136851ca8e5 Mon Sep 17 00:00:00 2001 From: thorstenfleischmann Date: Tue, 2 Nov 2021 08:42:05 +0100 Subject: [PATCH 19/77] fix null dereference in openidc.revoke_token log --- lib/resty/openidc.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 910ebbdb..61807a3b 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1238,7 +1238,7 @@ end function openidc.revoke_token(opts, token_type_hint, token) local err = openidc_ensure_discovered_data(opts) if err then - log(ERROR, "revocation of " .. token_type_hint .. " unsuccessful: " .. err) + log(ERROR, "revocation of " .. (token_type_hint or "token (no type specified)") .. " unsuccessful: " .. err) return false end From 94c384f6e1b3e2c07b25d14d43bd04f62ea94e5a Mon Sep 17 00:00:00 2001 From: Thorsten Fleischmann Date: Tue, 2 Nov 2021 12:54:53 +0100 Subject: [PATCH 20/77] basic revoke_tokens test --- tests/spec/revoke_tokens_spec.lua | 31 +++++++++++++++++++++++++++++++ tests/spec/test_support.lua | 10 ++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/spec/revoke_tokens_spec.lua diff --git a/tests/spec/revoke_tokens_spec.lua b/tests/spec/revoke_tokens_spec.lua new file mode 100644 index 00000000..5706e344 --- /dev/null +++ b/tests/spec/revoke_tokens_spec.lua @@ -0,0 +1,31 @@ +local http = require('socket.http') +local test_support = require('test_support') +require 'busted.runner'() + +describe('when revoke_tokens is successful', function() + test_support.start_server({ + oidc_opts = { + discovery = { + revocation_endpoint = "http://127.0.0.1/revocation", + } + } + }) + teardown(test_support.stop_server) + local _, _, cookies = test_support.login() + local content_table = {} + http.request({ + url = "http://localhost/revoke_tokens", + headers = { cookie = cookies }, + sink = ltn12.sink.table(content_table) + }) + + it('should return true', function() + assert.are.equals("revoke-result: true\n", table.concat(content_table)) + end) + + it('should have logged the revocation', function() + assert.error_log_contains("revocation of refresh_token successful") + assert.error_log_contains("revocation of access_token successful") + end) + +end) diff --git a/tests/spec/test_support.lua b/tests/spec/test_support.lua index e074facd..84aee710 100644 --- a/tests/spec/test_support.lua +++ b/tests/spec/test_support.lua @@ -372,6 +372,16 @@ JWT_SIGN_SECRET]=] } } + location /revoke_tokens { + content_by_lua_block { + local opts = OIDC_CONFIG + local res, err, target, session = oidc.authenticate(opts, nil, UNAUTH_ACTION) + local r = oidc.revoke_tokens(opts, session) + ngx.header.content_type = 'text/plain' + ngx.say('revoke-result: ' .. tostring(r)) + } + } + location /revocation { content_by_lua_block { ngx.req.read_body() From a50d7679a43c407b703d158dc670b5d00a07bf80 Mon Sep 17 00:00:00 2001 From: thorstenfleischmann Date: Thu, 4 Nov 2021 11:11:37 +0100 Subject: [PATCH 21/77] #404 Possibilty to pass in a existing session --- lib/resty/openidc.lua | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index a9be4013..6066977a 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1402,8 +1402,12 @@ local function openidc_get_redirect_uri_path(opts) return opts.redirect_uri and openidc_get_path(opts.redirect_uri) or opts.redirect_uri_path end +local function is_session(o) + return o ~= nil and o.start and type(o.start) == "function" +end + -- main routine for OpenID Connect user authentication -function openidc.authenticate(opts, target_url, unauth_action, session_opts) +function openidc.authenticate(opts, target_url, unauth_action, session_or_opts) if opts.redirect_uri_path then log(WARN, "using deprecated option `opts.redirect_uri_path`; switch to using an absolute URI and `opts.redirect_uri` instead") @@ -1411,10 +1415,16 @@ function openidc.authenticate(opts, target_url, unauth_action, session_opts) local err - local session, session_error = r_session.start(session_opts) - if session == nil then - log(ERROR, "Error starting session: " .. session_error) - return nil, session_error, target_url, session + local session + if is_session(session_or_opts) then + session = session_or_opts + else + local session_error + session, session_error = r_session.start(session_or_opts) + if session == nil then + log(ERROR, "Error starting session: " .. session_error) + return nil, session_error, target_url, session + end end target_url = target_url or ngx.var.request_uri From d2b31974cda5b6c22119057488cd68c84a5cf462 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sat, 6 Nov 2021 14:02:39 +0100 Subject: [PATCH 22/77] record #402 --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index ad8850f9..232f525e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +11/06/2021 +- added public functions that allow tokens to be revoked without + destroying the current session; see #402; thanks to + @thorstenfleischmann + 09/23/2021 - if lifecyle handlers return truthy values they cause the operation they are handlers of to fail; see #384; thanks to @arcivanov From d52fc6c38161a71f32d8232eee77f80428d5d7b2 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sat, 6 Nov 2021 14:25:24 +0100 Subject: [PATCH 23/77] treat empty x5c claim in JWK like a missing x5c claim closes #406 Signed-off-by: Stefan Bodewig --- ChangeLog | 3 +++ lib/resty/openidc.lua | 12 ++++++++---- tests/spec/bearer_token_verification_spec.lua | 13 +++++++++++++ .../rsa_key_jwk_with_n_and_e_and_empty_x5c.json | 12 ++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 tests/spec/rsa_key_jwk_with_n_and_e_and_empty_x5c.json diff --git a/ChangeLog b/ChangeLog index 232f525e..60398316 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,9 @@ destroying the current session; see #402; thanks to @thorstenfleischmann +- when the x5c claim of a JWK is an empty array it will be ignored + rather than cause an error; see #406 + 09/23/2021 - if lifecyle handlers return truthy values they cause the operation they are handlers of to fail; see #384; thanks to @arcivanov diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 61807a3b..8770a98c 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -847,7 +847,6 @@ local function encode_bit_string(array) end local function openidc_pem_from_x5c(x5c) - -- TODO check x5c length log(DEBUG, "Found x5c, getting PEM public key from x5c entry of json public key") local chunks = split_by_chunk(b64(openidc_base64_url_decode(x5c[1])), 64) local pem = "-----BEGIN CERTIFICATE-----\n" .. @@ -912,10 +911,15 @@ local function openidc_pem_from_jwk(opts, kid) return nil, err end + local x5c = jwk.x5c + if x5c and #(jwk.x5c) == 0 then + log(WARN, "Found invalid JWK with empty x5c array, ignoring x5c claim") + x5c = nil + end + local pem - -- TODO check x5c length - if jwk.x5c then - pem = openidc_pem_from_x5c(jwk.x5c) + if x5c then + pem = openidc_pem_from_x5c(x5c) elseif jwk.kty == "RSA" and jwk.n and jwk.e then pem = openidc_pem_from_rsa_n_and_e(jwk.n, jwk.e) else diff --git a/tests/spec/bearer_token_verification_spec.lua b/tests/spec/bearer_token_verification_spec.lua index 57370f97..0be18b13 100644 --- a/tests/spec/bearer_token_verification_spec.lua +++ b/tests/spec/bearer_token_verification_spec.lua @@ -147,6 +147,19 @@ describe("when using a RSA key from a JWK that doesn't contain the x5c claim", f base_checks() end) +describe("when using a RSA key from a JWK that contains an empty x5c claim but n and e", function() + test_support.start_server({ + verify_opts = { + discovery = { + jwks_uri = "http://127.0.0.1/jwk", + } + }, + jwk = test_support.load("/spec/rsa_key_jwk_with_n_and_e_and_empty_x5c.json") + }) + teardown(test_support.stop_server) + base_checks() +end) + describe("when the JWK specifies a kid and the JWKS contains multiple keys", function() test_support.start_server({ verify_opts = { diff --git a/tests/spec/rsa_key_jwk_with_n_and_e_and_empty_x5c.json b/tests/spec/rsa_key_jwk_with_n_and_e_and_empty_x5c.json new file mode 100644 index 00000000..607db767 --- /dev/null +++ b/tests/spec/rsa_key_jwk_with_n_and_e_and_empty_x5c.json @@ -0,0 +1,12 @@ +{ + "keys": [ + { + "kty": "RSA", + "use": "sig", + "kid": "abcd", + "x5c": [], + "e": "AQAB", + "n": "AMLGOeOLW3uZr6jbrkE4i-Cp9LcvfWhOcff8D68qmcWTwfYSDWTGKPbME83EuFwrpVzBZqIV2VaR7L5uX3-5MZij2DeG52Gdv-QnP0JlyHAxRWnNWSYu-ZURlwhDBi7g-rxTq_3_8DeFZQJtf6_pcWxLidwe6fQpQK0XkbRfxi3os0-YAEDedSyVzsIP9buz2KkjtHJ-RWxTGC61J_vJY7ruSVBDkPLBbMHFkTRUqATZ76B_DDtn5lyctbTUZKUUGljiwvu8zK2thp5CjMnP8DcCP0e8ZT5IUBN73VYksvf3Die8-axKUuFAyYRjv8mKbWXRFwJyUelC72R6pvcTBzc" + } + ] +} From bc4186164b422cc14e89927b09ae618741e0d746 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sat, 6 Nov 2021 14:30:44 +0100 Subject: [PATCH 24/77] track #405 Signed-off-by: Stefan Bodewig --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index 60398316..a347d90f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,10 @@ - when the x5c claim of a JWK is an empty array it will be ignored rather than cause an error; see #406 +- `authenticate`'s last parameter can now be an existing session + rather than options for starting a new one: see #405; thanks to + @thorstenfleischmann + 09/23/2021 - if lifecyle handlers return truthy values they cause the operation they are handlers of to fail; see #384; thanks to @arcivanov From 2680c968ddbac624003f7a403ee04b5c1d1332e2 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Fri, 19 Nov 2021 20:09:49 +0100 Subject: [PATCH 25/77] allow discovery cache to be controlled Signed-off-by: Stefan Bodewig --- ChangeLog | 4 ++++ lib/resty/openidc.lua | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index a347d90f..bc5e47a7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +11/19/2021 +- added opts.discovery_expires_in in order to make cache expiry of + OpenID Connect Discovery responses configurable. + 11/06/2021 - added public functions that allow tokens to be revoked without destroying the current session; see #402; thanks to diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 25f14ec8..dff0f7fa 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -598,7 +598,7 @@ local function openidc_ensure_discovered_data(opts) local err if type(opts.discovery) == "string" then local discovery - discovery, err = openidc_discover(opts.discovery, opts.ssl_verify, opts.keepalive, opts.timeout, opts.jwk_expires_in, opts.proxy_opts, + discovery, err = openidc_discover(opts.discovery, opts.ssl_verify, opts.keepalive, opts.timeout, opts.discovery_expires_in, opts.proxy_opts, opts.http_request_decorator) if not err then opts.discovery = discovery From c35b40a971ef87e163e63b8b45152a454d5374d2 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Fri, 19 Nov 2021 20:10:11 +0100 Subject: [PATCH 26/77] provide some documentation about caches see #407 Signed-off-by: Stefan Bodewig --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 2263ffd3..27c4c5a4 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,39 @@ local res, err, target, session = require("resty.openidc").authenticate(opts) session:close() ``` +## Caching + +lua-resty-openidc can use [shared memory +caches](https://github.com/openresty/lua-nginx-module/#lua_shared_dict) +for several things. If you want it to use the caches, you must use +`lua_shared_dict` in your `nginx.conf` file. + +Currently up to four caches are used + +* the cache named `discovery` stores the OpenID Connect Disovery + metadata of your OpenID Connect Provider. Cache items expire after + 24 hours unless overriden by `opts.discovery_expires_in` (a value + given in seconds) . This cache will store one item per issuer URI + and you can look up the discovery document yourself to get an + estimate for the size required - usually a few kB per OpenID Connect + Provider. +* the cache named `jwks` stores the key material of your OpenID + Connect Provider if it is provided via the JWKS endpoint. Cache + items expire after 24 hours unless overriden by + `opts.jwks_expires_in`. This cache will store one item per JWKS URI + and you can look up the jwks yourself to get an estimate for the + size required - usually a few kB per OpenID Connect Provider. +* the cache named `introspection` stores the result of OAuth2 token + introspection. Cache items expire when the corresponding token + expires. Tokens with unknown expiry are not cached at all. This + cache will contain one entry per introspected access token - usually + this will be a few kB per token. +* the cache named `jwt_verification` stores the result of JWT + verification. Cache items expire when the corresponding token + expires. Tokens with unknown expiry are not cached for two + minutes. This cache will contain one entry per verified JWT - + usually this will be a few kB per token. + ## Caching of Introspection and JWT Verification Results Note the `jwt_verification` and `introspection` caches are shared From 84629795d18e51f6af2331d05eb54490ab2776f4 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Fri, 17 Dec 2021 18:00:22 +0100 Subject: [PATCH 27/77] add additional arguments to on_authenticated lifecycle hook helps with #413 Signed-off-by: Stefan Bodewig --- ChangeLog | 4 ++++ README.md | 1 + lib/resty/openidc.lua | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index bc5e47a7..af72966b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +12/17/2021 +- added id_token and the token endpoint response as additional + arguments to the on_authenticated lifecycle hook; see #413 + 11/19/2021 - added opts.discovery_expires_in in order to make cache expiry of OpenID Connect Discovery responses configurable. diff --git a/README.md b/README.md index 27c4c5a4..ed582b35 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,7 @@ h2JHukolz9xf6qN61QMLSd83+kwoBr2drp6xg3eGDLIkQCQLrkY= -- `openidc_authorize` immediately prior to saving the session -- -- `on_authenticated` hook is invoked *after* receiving authorization response in -- `openidc_authorization_response` immediately prior to saving the session + -- Starting with lua-resty-openidc 1.7.5 this receives the decoded id_token as second and the response of the token endpoint as third argument -- -- `on_regenerated` is invoked immediately after the a new access token has been obtained via token refresh and is called with the regenerated session table diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index dff0f7fa..ce38a708 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1188,7 +1188,7 @@ local function openidc_authorization_response(opts, session) end if opts.lifecycle and opts.lifecycle.on_authenticated then - err = opts.lifecycle.on_authenticated(session) + err = opts.lifecycle.on_authenticated(session, id_token, json) if err then log(WARN, "failed in `on_authenticated` handler: " .. err) return nil, err, session.data.original_url, session From 85e30a13e08156ef3d782ecf7236758334234c6c Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Tue, 21 Dec 2021 16:23:45 +0100 Subject: [PATCH 28/77] release 1.7.5 Signed-off-by: Hans Zandbelt --- .github/issue_template.md | 2 +- .travis.yml | 2 +- ChangeLog | 3 +++ lib/resty/openidc.lua | 2 +- ...idc-1.7.4-1.rockspec => lua-resty-openidc-1.7.5-1.rockspec | 4 ++-- 5 files changed, 8 insertions(+), 5 deletions(-) rename lua-resty-openidc-1.7.4-1.rockspec => lua-resty-openidc-1.7.5-1.rockspec (97%) diff --git a/.github/issue_template.md b/.github/issue_template.md index 11efd3ee..f97fcf40 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -7,7 +7,7 @@ A new issue about a bug should be verified with a minimized example. ###### Environment -- lua-resty-openidc version (e.g. 1.7.4) +- lua-resty-openidc version (e.g. 1.7.5) - OpenID Connect provider (e.g. Keycloak, Azure AD) ###### Expected behaviour diff --git a/.travis.yml b/.travis.yml index d668980c..a5241491 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ services: env: global: - - VERSION=1.7.4-1 + - VERSION=1.7.5-1 - NAME=lua-resty-openidc - ROCKSPEC=$NAME-$VERSION.rockspec - LUAROCKS=3.4.0 diff --git a/ChangeLog b/ChangeLog index af72966b..4fcd61ed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +12/21/2021 +- release 1.7.5 + 12/17/2021 - added id_token and the token endpoint response as additional arguments to the on_authenticated lifecycle hook; see #413 diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index ce38a708..246414ef 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -84,7 +84,7 @@ local supported_token_auth_methods = { } local openidc = { - _VERSION = "1.7.4" + _VERSION = "1.7.5" } openidc.__index = openidc diff --git a/lua-resty-openidc-1.7.4-1.rockspec b/lua-resty-openidc-1.7.5-1.rockspec similarity index 97% rename from lua-resty-openidc-1.7.4-1.rockspec rename to lua-resty-openidc-1.7.5-1.rockspec index b36bc630..10c152a7 100644 --- a/lua-resty-openidc-1.7.4-1.rockspec +++ b/lua-resty-openidc-1.7.5-1.rockspec @@ -1,8 +1,8 @@ package = "lua-resty-openidc" -version = "1.7.4-1" +version = "1.7.5-1" source = { url = "git://github.com/zmartzone/lua-resty-openidc", - tag = "v1.7.4", + tag = "v1.7.5", dir = "lua-resty-openidc" } description = { From e017d4cad3e73f376d060a9d297e71631789e526 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Tue, 21 Dec 2021 16:48:40 +0100 Subject: [PATCH 29/77] add release process notes Signed-off-by: Hans Zandbelt --- RELEASE.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..970868dc --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,10 @@ +1. add release entry to ChangeLog +2. modify lua-resty-openidc-*.rockspec filename and version/tag contents +3. modify .travis.yml to update VERSION +4. (optional) modify .github/issue_template.md to point to the latest version +5. modify lib/resty/openidc.lua to update _VERSION +6. commit and push to Github +7. create a new release on the Github project, summarizing the ChangeLog +8. run "luarocks build" and "luarocks upload lua-resty-openidc-1.7.5-1.rockspec" + (make sure to get a luarocks.org upload key and configure ~/.luarocks/upload_config.lua) +9. run "opm build" and "opm upload" (possibly after modifying dist.ini) and "opm clean dist" From 5ff02a65c5752c38b30d4d17b350ec97c52a2471 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Thu, 23 Dec 2021 15:18:39 +0100 Subject: [PATCH 30/77] migrate CI from Travis to Github Actions publish luarocks and opm packages manually using RELEASE.md Signed-off-by: Hans Zandbelt --- .github/workflows/docker-ci.yml | 16 +++++ .travis.yml | 37 ---------- .travis/platform.sh | 15 ---- .travis/setenv_lua.sh | 3 - .travis/setup_lua.sh | 122 -------------------------------- ChangeLog | 3 + README.md | 2 +- RELEASE.md | 13 ++-- 8 files changed, 26 insertions(+), 185 deletions(-) create mode 100644 .github/workflows/docker-ci.yml delete mode 100644 .travis.yml delete mode 100644 .travis/platform.sh delete mode 100644 .travis/setenv_lua.sh delete mode 100644 .travis/setup_lua.sh diff --git a/.github/workflows/docker-ci.yml b/.github/workflows/docker-ci.yml new file mode 100644 index 00000000..1d20581d --- /dev/null +++ b/.github/workflows/docker-ci.yml @@ -0,0 +1,16 @@ +name: CI + +on: [push, pull_request] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build + run: docker build . -f tests/Dockerfile -t lua-resty-openidc/test + - name: Run + run: docker run -t --rm lua-resty-openidc/test:latest diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a5241491..00000000 --- a/.travis.yml +++ /dev/null @@ -1,37 +0,0 @@ -language: c - -sudo: required - -services: - - docker - -env: - global: - - VERSION=1.7.5-1 - - NAME=lua-resty-openidc - - ROCKSPEC=$NAME-$VERSION.rockspec - - LUAROCKS=3.4.0 - matrix: - - LUA=lua5.1 - -before_install: - - docker build -f tests/Dockerfile . -t lua-resty-openidc/test - - source .travis/setenv_lua.sh - -install: - - luarocks install Lua-cURL --server=https://luarocks.org/dev - - luarocks install lunitx - - luarocks install JSON4Lua - -script: - - luarocks make --pack-binary-rock $ROCKSPEC CFLAGS="-O2 -fPIC -fprofile-arcs" LIBFLAG="-shared" - - docker run -it --rm lua-resty-openidc/test:latest - -# The API key comes from an environment variable configured on https://travis-ci.org -after_success: - - luarocks upload --api-key=$API_KEY $ROCKSPEC - -notifications: - email: - on_success: change - on_failure: always diff --git a/.travis/platform.sh b/.travis/platform.sh deleted file mode 100644 index 0ade2010..00000000 --- a/.travis/platform.sh +++ /dev/null @@ -1,15 +0,0 @@ -if [ -z "${PLATFORM:-}" ]; then - PLATFORM=$TRAVIS_OS_NAME; -fi - -if [ "$PLATFORM" == "osx" ]; then - PLATFORM="macosx"; -fi - -if [ -z "$PLATFORM" ]; then - if [ "$(uname)" == "Linux" ]; then - PLATFORM="linux"; - else - PLATFORM="macosx"; - fi; -fi \ No newline at end of file diff --git a/.travis/setenv_lua.sh b/.travis/setenv_lua.sh deleted file mode 100644 index a5a3e73f..00000000 --- a/.travis/setenv_lua.sh +++ /dev/null @@ -1,3 +0,0 @@ -export PATH=${PATH}:$HOME/.lua:$HOME/.local/bin:${TRAVIS_BUILD_DIR}/install/luarocks/bin -bash .travis/setup_lua.sh -eval `$HOME/.lua/luarocks path` \ No newline at end of file diff --git a/.travis/setup_lua.sh b/.travis/setup_lua.sh deleted file mode 100644 index 389c4276..00000000 --- a/.travis/setup_lua.sh +++ /dev/null @@ -1,122 +0,0 @@ -#! /bin/bash - -# A script for setting up environment for travis-ci testing. -# Sets up Lua and Luarocks. -# LUA must be "lua5.1", "lua5.2" or "luajit". -# luajit2.0 - master v2.0 -# luajit2.1 - master v2.1 - -set -eufo pipefail - -LUAJIT_VERSION="2.0.4" -LUAJIT_BASE="LuaJIT-$LUAJIT_VERSION" - -source .travis/platform.sh - -LUA_HOME_DIR=$TRAVIS_BUILD_DIR/install/lua - -LR_HOME_DIR=$TRAVIS_BUILD_DIR/install/luarocks - -mkdir $HOME/.lua - -LUAJIT="no" - -if [ "$PLATFORM" == "macosx" ]; then - if [ "$LUA" == "luajit" ]; then - LUAJIT="yes"; - fi - if [ "$LUA" == "luajit2.0" ]; then - LUAJIT="yes"; - fi - if [ "$LUA" == "luajit2.1" ]; then - LUAJIT="yes"; - fi; -elif [ "$(expr substr $LUA 1 6)" == "luajit" ]; then - LUAJIT="yes"; -fi - -mkdir -p "$LUA_HOME_DIR" - -if [ "$LUAJIT" == "yes" ]; then - - if [ "$LUA" == "luajit" ]; then - curl --location https://github.com/LuaJIT/LuaJIT/archive/v$LUAJIT_VERSION.tar.gz | tar xz; - else - git clone https://github.com/LuaJIT/LuaJIT.git $LUAJIT_BASE; - fi - - cd $LUAJIT_BASE - - if [ "$LUA" == "luajit2.1" ]; then - git checkout v2.1; - # force the INSTALL_TNAME to be luajit - perl -i -pe 's/INSTALL_TNAME=.+/INSTALL_TNAME= luajit/' Makefile - fi - - make && make install PREFIX="$LUA_HOME_DIR" - - ln -s $LUA_HOME_DIR/bin/luajit $HOME/.lua/luajit - ln -s $LUA_HOME_DIR/bin/luajit $HOME/.lua/lua; - -else - - if [ "$LUA" == "lua5.1" ]; then - curl http://www.lua.org/ftp/lua-5.1.5.tar.gz | tar xz - cd lua-5.1.5; - elif [ "$LUA" == "lua5.2" ]; then - curl http://www.lua.org/ftp/lua-5.2.4.tar.gz | tar xz - cd lua-5.2.4; - elif [ "$LUA" == "lua5.3" ]; then - curl http://www.lua.org/ftp/lua-5.3.2.tar.gz | tar xz - cd lua-5.3.2; - fi - - # Build Lua without backwards compatibility for testing - perl -i -pe 's/-DLUA_COMPAT_(ALL|5_2)//' src/Makefile - make $PLATFORM - make INSTALL_TOP="$LUA_HOME_DIR" install; - - ln -s $LUA_HOME_DIR/bin/lua $HOME/.lua/lua - ln -s $LUA_HOME_DIR/bin/luac $HOME/.lua/luac; - -fi - -cd $TRAVIS_BUILD_DIR - -lua -v - -LUAROCKS_BASE=luarocks-$LUAROCKS - -curl --location http://luarocks.org/releases/$LUAROCKS_BASE.tar.gz | tar xz - -cd $LUAROCKS_BASE - -if [ "$LUA" == "luajit" ]; then - ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.0" --prefix="$LR_HOME_DIR"; -elif [ "$LUA" == "luajit2.0" ]; then - ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.0" --prefix="$LR_HOME_DIR"; -elif [ "$LUA" == "luajit2.1" ]; then - ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.1" --prefix="$LR_HOME_DIR"; -else - ./configure --with-lua="$LUA_HOME_DIR" --prefix="$LR_HOME_DIR" -fi - -make build && make install - -ln -s $LR_HOME_DIR/bin/luarocks $HOME/.lua/luarocks - -cd $TRAVIS_BUILD_DIR - -luarocks --version - -rm -rf $LUAROCKS_BASE - -if [ "$LUAJIT" == "yes" ]; then - rm -rf $LUAJIT_BASE; -elif [ "$LUA" == "lua5.1" ]; then - rm -rf lua-5.1.5; -elif [ "$LUA" == "lua5.2" ]; then - rm -rf lua-5.2.4; -elif [ "$LUA" == "lua5.3" ]; then - rm -rf lua-5.3.2; -fi \ No newline at end of file diff --git a/ChangeLog b/ChangeLog index 4fcd61ed..1a19de46 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +12/23/2021 +- use Github actions for docker-based CI; delete Travis files + 12/21/2021 - release 1.7.5 diff --git a/README.md b/README.md index ed582b35..60d0c726 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.org/zmartzone/lua-resty-openidc.svg?branch=master)](https://travis-ci.org/zmartzone/lua-resty-openidc) +[![CI Status](https://github.com/zmartzone/lua-resty-openidc/actions/workflows/docker-ci.yml/badge.svg)](https://github.com/zmartzone/lua-resty-openidc/actions/workflows/docker-ci.yml) [OpenID Certification](https://openid.net/certification) # lua-resty-openidc diff --git a/RELEASE.md b/RELEASE.md index 970868dc..cee386aa 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,10 +1,9 @@ 1. add release entry to ChangeLog 2. modify lua-resty-openidc-*.rockspec filename and version/tag contents -3. modify .travis.yml to update VERSION -4. (optional) modify .github/issue_template.md to point to the latest version -5. modify lib/resty/openidc.lua to update _VERSION -6. commit and push to Github -7. create a new release on the Github project, summarizing the ChangeLog -8. run "luarocks build" and "luarocks upload lua-resty-openidc-1.7.5-1.rockspec" +3. (optional) modify .github/issue_template.md to point to the latest version +4. modify lib/resty/openidc.lua to update _VERSION +5. commit and push to Github +6. create a new release on the Github project, summarizing the ChangeLog +7. run "luarocks build" and "luarocks upload lua-resty-openidc-1.7.5-1.rockspec" (make sure to get a luarocks.org upload key and configure ~/.luarocks/upload_config.lua) -9. run "opm build" and "opm upload" (possibly after modifying dist.ini) and "opm clean dist" +8. run "opm build" and "opm upload" (possibly after modifying dist.ini) and "opm clean dist" From f93f8748c23a33a3b667929cfca69b8a0c85d299 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Mon, 3 Jan 2022 16:04:29 +0100 Subject: [PATCH 31/77] update copyright year to 2022 Signed-off-by: Hans Zandbelt --- DISCLAIMER | 9 +-------- lib/resty/openidc.lua | 10 +--------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/DISCLAIMER b/DISCLAIMER index 144142b5..5b1d5e54 100644 --- a/DISCLAIMER +++ b/DISCLAIMER @@ -1,18 +1,11 @@ /*************************************************************************** - * Copyright (C) 2017-2021 ZmartZone IAM + * Copyright (C) 2017-2022 ZmartZone Holding B.V. * All rights reserved. * * ZmartZone IAM * https://www.zmartzone.eu * * Copyright (C) 2014-2017 Ping Identity Corporation - * All rights reserved. - * - * Ping Identity Corporation - * 1099 18th St Suite 2950 - * Denver, CO 80202 - * 303.468.2900 - * http://www.pingidentity.com * * DISCLAIMER OF WARRANTIES: * diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 246414ef..753ab227 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -17,18 +17,10 @@ specific language governing permissions and limitations under the License. *************************************************************************** -Copyright (C) 2017-2019 ZmartZone IAM +Copyright (C) 2017-2022 ZmartZone Holding B.V. Copyright (C) 2015-2017 Ping Identity Corporation All rights reserved. -For further information please contact: - - Ping Identity Corporation - 1099 18th St Suite 2950 - Denver, CO 80202 - 303.468.2900 - http://www.pingidentity.com - DISCLAIMER OF WARRANTIES: THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT From 789cf8ac48086b95c8522cb7b34bf63252341968 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Wed, 13 Dec 2017 14:41:03 +0100 Subject: [PATCH 32/77] support for response_mode form_post Signed-off-by: Hans Zandbelt whitespace --- lib/resty/openidc.lua | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 753ab227..415ad29d 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -354,6 +354,10 @@ local function openidc_authorize(opts, session, target_url, prompt) params.code_challenge = openidc_s256(code_verifier) end + if opts.response_mode then + params.response_mode = opts.response_mode + end + -- merge any provided extra parameters if opts.authorization_params then for k, v in pairs(opts.authorization_params) do params[k] = v end @@ -1076,8 +1080,14 @@ end -- handle a "code" authorization response from the OP local function openidc_authorization_response(opts, session) - local args = ngx.req.get_uri_args() - local err, log_err, client_err + local args, err, log_err, client_err + + if opts.response_mode and opts.response_mode == "form_post" then + ngx.req.read_body() + args = ngx.req.get_post_args() + else + args = ngx.req.get_uri_args() + end if not args.code or not args.state then err = "unhandled request to the redirect_uri: " .. ngx.var.request_uri From 493eefcbe963cecd2c3b0c3f243016eac0aa0e10 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Wed, 3 Jan 2018 13:55:37 +0100 Subject: [PATCH 33/77] spec for form_post support --- tests/spec/form_post_spec.lua | 94 +++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 tests/spec/form_post_spec.lua diff --git a/tests/spec/form_post_spec.lua b/tests/spec/form_post_spec.lua new file mode 100644 index 00000000..a539c4d7 --- /dev/null +++ b/tests/spec/form_post_spec.lua @@ -0,0 +1,94 @@ +local http = require("socket.http") +local test_support = require("test_support") +local ltn12 = require("ltn12") +require 'busted.runner'() + +describe("when response_mode is form_post", function() + test_support.start_server({oidc_opts = {response_mode = "form_post"}}) + teardown(test_support.stop_server) + local _, status, headers = http.request({ + url = "http://127.0.0.1/default/t", + redirect = false + }) + it("then it is included", function() + assert.truthy(string.match(headers["location"], ".*response_mode=form_post.*")) + end) +end) + +local function do_post(cookie_header, body) + local x, y, z = http.request({ + method = "POST", + url = "http://localhost/default/redirect_uri", + headers = { + ["Content-Type"] = "application/x-www-form-urlencoded", + ["Content-Length"] = string.len(body), + cookie = cookie_header, + }, + source = ltn12.source.string(body), + redirect = false + }) + return x, y, z +end + +describe("when a form_post is received", function() + test_support.start_server({oidc_opts = {response_mode = "form_post"}}) + teardown(test_support.stop_server) + local _, _, headers = http.request({ + url = "http://localhost/default/t", + redirect = false + }) + local state = test_support.grab(headers, 'state') + test_support.register_nonce(headers) + local cookie_header = test_support.extract_cookies(headers) + describe("without an active user session", function() + local _, redirStatus = http.request({ + method = 'POST', + url = "http://localhost/default/redirect_uri", + headers = { + ["Content-Type"] = "application/x-www-form-urlencoded", + }, + source = ltn12.source.string("code=foo&state=" .. state) + }) + it("should be rejected", function() + assert.are.equals(401, redirStatus) + end) + it("will log an error message", function() + assert.error_log_contains("but there's no session state found") + end) + end) + describe("with bad state", function() + local _, redirStatus = do_post(cookie_header, "code=foo&state=X" .. state) + it("should be rejected", function() + assert.are.equals(401, redirStatus) + end) + it("will log an error message", function() + assert.error_log_contains("does not match state restored from session") + end) + end) + describe("without state", function() + local _, redirStatus = do_post(cookie_header, "code=foo") + it("should be rejected", function() + assert.are.equals(401, redirStatus) + end) + it("will log an error message", function() + assert.error_log_contains("unhandled request to the redirect_uri") + end) + end) + describe("without code", function() + local _, redirStatus = do_post(cookie_header, "state=" .. state) + it("should be rejected", function() + assert.are.equals(401, redirStatus) + end) + it("will log an error message", function() + assert.error_log_contains("unhandled request to the redirect_uri") + end) + end) + describe("with all things set", function() + local _, redirStatus, h = do_post(cookie_header, "code=foo&state=" .. state) + it("redirects to the original URI", function() + assert.are.equals(302, redirStatus) + assert.are.equals("/default/t", h.location) + end) + end) +end) + From 3ba8fe14db0356426a043d37c3ba929f6f4c1995 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Wed, 3 Jan 2018 14:05:01 +0100 Subject: [PATCH 34/77] make sure request is rejected for the correct reason --- tests/spec/form_post_spec.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/spec/form_post_spec.lua b/tests/spec/form_post_spec.lua index a539c4d7..d9989b0d 100644 --- a/tests/spec/form_post_spec.lua +++ b/tests/spec/form_post_spec.lua @@ -41,13 +41,15 @@ describe("when a form_post is received", function() test_support.register_nonce(headers) local cookie_header = test_support.extract_cookies(headers) describe("without an active user session", function() + local body = "code=foo&state=" .. state local _, redirStatus = http.request({ method = 'POST', url = "http://localhost/default/redirect_uri", headers = { ["Content-Type"] = "application/x-www-form-urlencoded", + ["Content-Length"] = string.len(body), }, - source = ltn12.source.string("code=foo&state=" .. state) + source = ltn12.source.string(body) }) it("should be rejected", function() assert.are.equals(401, redirStatus) From 23ca9e1cd6c47264329b7816a4939774320f95e1 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Tue, 4 Jan 2022 19:38:56 +0100 Subject: [PATCH 35/77] document response_mode=form_post --- ChangeLog | 3 +++ README.md | 1 + 2 files changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index 1a19de46..1150f53b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +01/04/2022 +- added support for OAuth 2.0 Form Post Response Mode. + 12/23/2021 - use Github actions for docker-based CI; delete Travis files diff --git a/README.md b/README.md index 60d0c726..91738c53 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,7 @@ h2JHukolz9xf6qN61QMLSd83+kwoBr2drp6xg3eGDLIkQCQLrkY= -- Connection keepalive with the OP can be enabled ("yes") or disabled ("no"). --keepalive = "no", + --response_mode=form_post can be used to make lua-resty-openidc use the [OAuth 2.0 Form Post Response Mode](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html). *Note* for modern browsers you will need to set [`$session_cookie_samesite`](https://github.com/bungle/lua-resty-session#string-sessioncookiesamesite) to `None` with form_post unless your OpenID Connect Provider and Relying Party share the same domain. --authorization_params = { hd="zmartzone.eu" }, --scope = "openid email profile", -- Refresh the users id_token after 900 seconds without requiring re-authentication From a468d13283d62ef5e3d14f02404294f7777c053d Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sat, 5 Mar 2022 09:45:56 +0100 Subject: [PATCH 36/77] improve error message when bearer token header is malformed closes #421 Signed-off-by: Stefan Bodewig --- ChangeLog | 4 ++++ lib/resty/openidc.lua | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1150f53b..5b718d3f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +03/05/2022 +- improved error message when expecting a Bearer token header and the + header doesn't contain a space character; see #421 + 01/04/2022 - added support for OAuth 2.0 Form Post Response Mode. diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 415ad29d..41c31977 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1637,14 +1637,14 @@ local function openidc_get_bearer_access_token(opts) local header_name = opts.auth_accept_token_as_header_name or "Authorization" local header = get_first(headers[header_name]) - if header == nil or header:find(" ") == nil then + if header == nil then err = "no Authorization header found" log(ERROR, err) return nil, err end local divider = header:find(' ') - if string.lower(header:sub(0, divider - 1)) ~= string.lower("Bearer") then + if divider == 0 or string.lower(header:sub(0, divider - 1)) ~= string.lower("Bearer") then err = "no Bearer authorization header value found" log(ERROR, err) return nil, err From 08c3b10a9f74d5fcd89f69b33d029567469e282a Mon Sep 17 00:00:00 2001 From: usysrc Date: Fri, 22 Apr 2022 09:25:10 +0200 Subject: [PATCH 37/77] fixed gsub call --- lib/resty/openidc.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 41c31977..6597cb28 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1676,7 +1676,7 @@ local function get_introspection_endpoint(opts) end local function get_introspection_cache_prefix(opts) - return (opts.cache_segment and opts.cache_segment.gsub(',', '_') or 'DEFAULT') .. ',' + return (opts.cache_segment and opts.cache_segment:gsub(',', '_') or 'DEFAULT') .. ',' .. (get_introspection_endpoint(opts) or 'nil-endpoint') .. ',' .. (opts.client_id or 'no-client_id') .. ',' .. (opts.client_secret and 'secret' or 'no-client_secret') .. ':' @@ -1786,7 +1786,7 @@ local function get_jwt_verification_cache_prefix(opts) for _, alg in ipairs(expected_algs) do signing_alg_values_expected = signing_alg_values_expected .. ',' .. alg end - return (opts.cache_segment and opts.cache_segment.gsub(',', '_') or 'DEFAULT') .. ',' + return (opts.cache_segment and opts.cache_segment:gsub(',', '_') or 'DEFAULT') .. ',' .. (opts.public_key or 'no-pubkey') .. ',' .. (opts.symmetric_key or 'no-symkey') .. ',' .. signing_alg_values_expected .. ':' From 961af4c7fc6e8e674bd850273719717f03c025a8 Mon Sep 17 00:00:00 2001 From: usysrc Date: Sat, 23 Apr 2022 00:33:05 +0200 Subject: [PATCH 38/77] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 15507a38..e2fd56d4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -35,3 +35,4 @@ reporting bugs, providing fixes, suggesting useful features or other: Nick Wiedenbrueck Eduardo Gonçalves Thorsten Fleischmann + Tilmann Hars From a69b488ab00ca4f9052f621ff53e5f90f69ac05a Mon Sep 17 00:00:00 2001 From: usysrc Date: Wed, 4 May 2022 17:32:16 +0200 Subject: [PATCH 39/77] fixed serpent version to 0.28 --- tests/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Dockerfile b/tests/Dockerfile index e433be9a..21c37095 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -10,7 +10,7 @@ RUN ["apt-get", "update"] RUN ["apt-get", "install", "-y", "git"] RUN ["luarocks", "install", "busted"] RUN ["luarocks", "install", "LuaSocket"] -RUN ["luarocks", "install", "serpent"] +RUN ["luarocks", "install", "serpent", "0.28"] RUN ["luarocks", "install", "dkjson"] RUN ["luarocks", "install", "luacov"] From 7d72f45117c1da472664ff9d10de573343d56d7b Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Tue, 31 May 2022 21:53:58 +0200 Subject: [PATCH 40/77] recent serpent rockspec finally seems to work --- tests/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Dockerfile b/tests/Dockerfile index 21c37095..e433be9a 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -10,7 +10,7 @@ RUN ["apt-get", "update"] RUN ["apt-get", "install", "-y", "git"] RUN ["luarocks", "install", "busted"] RUN ["luarocks", "install", "LuaSocket"] -RUN ["luarocks", "install", "serpent", "0.28"] +RUN ["luarocks", "install", "serpent"] RUN ["luarocks", "install", "dkjson"] RUN ["luarocks", "install", "luacov"] From b723124bdd751bd9f5b58bd1dc5584c897aa2d38 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sun, 26 Jun 2022 14:58:04 +0200 Subject: [PATCH 41/77] make tests independent of nginx config file buffer size Signed-off-by: Stefan Bodewig --- tests/spec/test_support.lua | 164 +++++++++++++++++++----------------- 1 file changed, 87 insertions(+), 77 deletions(-) diff --git a/tests/spec/test_support.lua b/tests/spec/test_support.lua index 84aee710..9ebc65f2 100644 --- a/tests/spec/test_support.lua +++ b/tests/spec/test_support.lua @@ -90,6 +90,54 @@ local DEFAULT_UNAUTH_ACTION = "nil" local DEFAULT_DELAY_RESPONSE = "0" +local DEFAULT_INIT_TEMPLATE = [[ +local test_globals = {} +local sign_secret = [=[ +JWT_SIGN_SECRET]=] + +if os.getenv('coverage') then + require("luacov.runner")("/spec/luacov/settings.luacov") +end +test_globals.oidc = require "resty.openidc" +test_globals.cjson = require "cjson" +test_globals.delay = function(delay_response) + if delay_response > 0 then + ngx.sleep(delay_response / 1000) + end +end +test_globals.b64url = function(s) + return ngx.encode_base64(test_globals.cjson.encode(s)):gsub('+','-'):gsub('/','_') +end +test_globals.create_jwt = function(payload, fake_signature) + if not fake_signature then + local jwt_content = { + header = TOKEN_HEADER, + payload = payload + } + local jwt = require "resty.jwt" + return jwt:sign(sign_secret, jwt_content) + else + local header = test_globals.b64url({ + typ = "JWT", + alg = "AB256" + }) + return header .. "." .. test_globals.b64url(payload) .. ".NOT_A_VALID_SIGNATURE" + end +end +test_globals.query_decorator = function(req) + req.query = "foo=bar" + return req +end +test_globals.body_decorator = function(req) + local body = ngx.decode_args(req.body) + body.foo = "bar" + req.body = ngx.encode_args(body) + return req +end +test_globals.jwks = [=[JWK]=] +return test_globals +]] + local DEFAULT_CONFIG_TEMPLATE = [[ worker_processes 1; pid /tmp/server/logs/nginx.pid; @@ -101,51 +149,10 @@ events { http { access_log /tmp/server/logs/access.log; - lua_package_path '~/lua/?.lua;;'; + lua_package_path '~/lua/?.lua;/tmp/server/conf/?.lua;;'; lua_shared_dict discovery 1m; init_by_lua_block { - sign_secret = [=[ -JWT_SIGN_SECRET]=] - if os.getenv('coverage') then - require("luacov.runner")("/spec/luacov/settings.luacov") - end - oidc = require "resty.openidc" - cjson = require "cjson" - delay = function(delay_response) - if delay_response > 0 then - ngx.sleep(delay_response / 1000) - end - end - b64url = function(s) - return ngx.encode_base64(cjson.encode(s)):gsub('+','-'):gsub('/','_') - end - create_jwt = function(payload, fake_signature) - if not fake_signature then - local jwt_content = { - header = TOKEN_HEADER, - payload = payload - } - local jwt = require "resty.jwt" - return jwt:sign(sign_secret, jwt_content) - else - local header = b64url({ - typ = "JWT", - alg = "AB256" - }) - return header .. "." .. b64url(payload) .. ".NOT_A_VALID_SIGNATURE" - end - end - query_decorator = function(req) - req.query = "foo=bar" - return req - end - body_decorator = function(req) - local body = ngx.decode_args(req.body) - body.foo = "bar" - req.body = ngx.encode_args(body) - return req - end - jwks = [=[JWK]=] + test_globals = require("test_globals") } resolver 8.8.8.8; @@ -160,7 +167,7 @@ JWT_SIGN_SECRET]=] location /jwt { content_by_lua_block { - local jwt_token = create_jwt(ACCESS_TOKEN, FAKE_ACCESS_TOKEN_SIGNATURE) + local jwt_token = test_globals.create_jwt(ACCESS_TOKEN, FAKE_ACCESS_TOKEN_SIGNATURE) ngx.header.content_type = 'text/plain' ngx.say(jwt_token) } @@ -168,10 +175,10 @@ JWT_SIGN_SECRET]=] location /jwk { content_by_lua_block { - ngx.log(ngx.ERR, "jwk uri_args: " .. cjson.encode(ngx.req.get_uri_args())) + ngx.log(ngx.ERR, "jwk uri_args: " .. test_globals.cjson.encode(ngx.req.get_uri_args())) ngx.header.content_type = 'application/json;charset=UTF-8' - delay(JWK_DELAY_RESPONSE) - ngx.say(jwks) + test_globals.delay(JWK_DELAY_RESPONSE) + ngx.say(test_globals.jwks) } } @@ -183,9 +190,9 @@ JWT_SIGN_SECRET]=] access_by_lua_block { local opts = OIDC_CONFIG if opts.decorate then - opts.http_request_decorator = opts.decorate == "body" and body_decorator or query_decorator + opts.http_request_decorator = opts.decorate == "body" and test_globals.body_decorator or test_globals.query_decorator end - local res, err, target, session = oidc.authenticate(opts, nil, UNAUTH_ACTION) + local res, err, target, session = test_globals.oidc.authenticate(opts, nil, UNAUTH_ACTION) if err then ngx.status = 401 ngx.log(ngx.ERR, "authenticate failed: " .. err) @@ -204,10 +211,10 @@ JWT_SIGN_SECRET]=] access_by_lua_block { local opts = OIDC_CONFIG if opts.decorate then - opts.http_request_decorator = opts.decorate == "body" and body_decorator or query_decorator + opts.http_request_decorator = opts.decorate == "body" and test_globals.body_decorator or test_globals.query_decorator end local uri = ngx.var.scheme .. "://" .. ngx.var.host .. ngx.var.request_uri - local res, err, target, session = oidc.authenticate(opts, uri, UNAUTH_ACTION) + local res, err, target, session = test_globals.oidc.authenticate(opts, uri, UNAUTH_ACTION) if err then ngx.status = 401 ngx.log(ngx.ERR, "authenticate failed: " .. err) @@ -253,13 +260,13 @@ JWT_SIGN_SECRET]=] end local jwt_token if NONE_ALG_ID_TOKEN_SIGNATURE then - local header = b64url({ + local header = test_globals.b64url({ typ = "JWT", alg = "none" }) - jwt_token = header .. "." .. b64url(id_token) .. "." + jwt_token = header .. "." .. test_globals.b64url(id_token) .. "." else - jwt_token = create_jwt(id_token, FAKE_ID_TOKEN_SIGNATURE) + jwt_token = test_globals.create_jwt(id_token, FAKE_ID_TOKEN_SIGNATURE) if BREAK_ID_TOKEN_SIGNATURE then jwt_token = jwt_token:sub(1, -6) .. "XXXXX" end @@ -272,8 +279,8 @@ JWT_SIGN_SECRET]=] if args.grant_type == "authorization_code" or REFRESH_RESPONSE_CONTAINS_ID_TOKEN then token_response.id_token = jwt_token end - delay(TOKEN_DELAY_RESPONSE) - ngx.say(cjson.encode(token_response)) + test_globals.delay(TOKEN_DELAY_RESPONSE) + ngx.say(test_globals.cjson.encode(token_response)) } } @@ -281,24 +288,24 @@ JWT_SIGN_SECRET]=] content_by_lua_block { local opts = VERIFY_OPTS if opts.decorate then - opts.http_request_decorator = query_decorator + opts.http_request_decorator = test_globals.query_decorator end - local json, err, token = oidc.bearer_jwt_verify(opts) + local json, err, token = test_globals.oidc.bearer_jwt_verify(opts) if err then ngx.status = 401 ngx.log(ngx.ERR, "Invalid token: " .. err) else ngx.status = 204 - ngx.log(ngx.ERR, "Valid token: " .. cjson.encode(json)) + ngx.log(ngx.ERR, "Valid token: " .. test_globals.cjson.encode(json)) end } } location /discovery { content_by_lua_block { - ngx.log(ngx.ERR, "discovery uri_args: " .. cjson.encode(ngx.req.get_uri_args())) + ngx.log(ngx.ERR, "discovery uri_args: " .. test_globals.cjson.encode(ngx.req.get_uri_args())) ngx.header.content_type = 'application/json;charset=UTF-8' - delay(DISCOVERY_DELAY_RESPONSE) + test_globals.delay(DISCOVERY_DELAY_RESPONSE) ngx.say([=[{ "authorization_endpoint": "http://127.0.0.1/authorize", "token_endpoint": "http://127.0.0.1/token", @@ -311,11 +318,11 @@ JWT_SIGN_SECRET]=] location /user-info { content_by_lua_block { - delay(USERINFO_DELAY_RESPONSE) + test_globals.delay(USERINFO_DELAY_RESPONSE) local auth = ngx.req.get_headers()["Authorization"] ngx.log(ngx.ERR, "userinfo authorization header: " .. (auth and auth or "")) ngx.header.content_type = 'application/json;charset=UTF-8' - ngx.say(cjson.encode(USERINFO)) + ngx.say(test_globals.cjson.encode(USERINFO)) } } @@ -337,8 +344,8 @@ JWT_SIGN_SECRET]=] ngx.log(ngx.ERR, "no cookie in introspection call") end ngx.header.content_type = 'application/json;charset=UTF-8' - delay(INTROSPECTION_DELAY_RESPONSE) - ngx.say(cjson.encode(INTROSPECTION_RESPONSE)) + test_globals.delay(INTROSPECTION_DELAY_RESPONSE) + ngx.say(test_globals.cjson.encode(INTROSPECTION_RESPONSE)) } } @@ -346,22 +353,22 @@ JWT_SIGN_SECRET]=] content_by_lua_block { local opts = INTROSPECTION_OPTS if opts.decorate then - opts.http_request_decorator = body_decorator + opts.http_request_decorator = test_globals.body_decorator end - local json, err = oidc.introspect(opts) + local json, err = test_globals.oidc.introspect(opts) if err then ngx.status = 401 ngx.log(ngx.ERR, "Introspection error: " .. err) else ngx.header.content_type = 'application/json;charset=UTF-8' - ngx.say(cjson.encode(json)) + ngx.say(test_globals.cjson.encode(json)) end } } location /access_token { content_by_lua_block { - local access_token, err = oidc.access_token(ACCESS_TOKEN_OPTS) + local access_token, err = test_globals.oidc.access_token(ACCESS_TOKEN_OPTS) if not access_token then ngx.status = 401 ngx.log(ngx.ERR, "access_token error: " .. (err or 'no message')) @@ -375,8 +382,8 @@ JWT_SIGN_SECRET]=] location /revoke_tokens { content_by_lua_block { local opts = OIDC_CONFIG - local res, err, target, session = oidc.authenticate(opts, nil, UNAUTH_ACTION) - local r = oidc.revoke_tokens(opts, session) + local res, err, target, session = test_globals.oidc.authenticate(opts, nil, UNAUTH_ACTION) + local r = test_globals.oidc.revoke_tokens(opts, session) ngx.header.content_type = 'text/plain' ngx.say('revoke-result: ' .. tostring(r)) } @@ -393,7 +400,7 @@ JWT_SIGN_SECRET]=] ngx.log(ngx.ERR, "no cookie in introspection call") end ngx.header.content_type = 'application/json;charset=UTF-8' - delay(REVOCATION_DELAY_RESPONSE) + test_globals.delay(REVOCATION_DELAY_RESPONSE) ngx.status = 200 ngx.say('INVALID JSON.') } @@ -424,7 +431,7 @@ end local DEFAULT_INTROSPECTION_RESPONSE = merge({active=true}, DEFAULT_ACCESS_TOKEN) -local function write_config(out, custom_config) +local function write_template(out, template, custom_config) custom_config = custom_config or {} local oidc_config = merge(merge({}, DEFAULT_OIDC_CONFIG), custom_config["oidc_opts"] or {}) local id_token = merge(merge({}, DEFAULT_ID_TOKEN), custom_config["id_token"] or {}) @@ -464,7 +471,7 @@ local function write_config(out, custom_config) for _, k in ipairs(custom_config["remove_introspection_config_keys"] or {}) do introspection_opts[k] = nil end - local config = DEFAULT_CONFIG_TEMPLATE + local content = template :gsub("OIDC_CONFIG", serpent.block(oidc_config, {comment = false })) :gsub("TOKEN_HEADER", serpent.block(token_header, {comment = false })) :gsub("JWT_SIGN_SECRET", custom_config["jwt_sign_secret"] or DEFAULT_JWT_SIGN_SECRET) @@ -492,7 +499,7 @@ local function write_config(out, custom_config) :gsub("ID_TOKEN", serpent.block(id_token, {comment = false })) :gsub("ACCESS_TOKEN", serpent.block(access_token, {comment = false })) :gsub("UNAUTH_ACTION", custom_config["unauth_action"] and ('"' .. custom_config["unauth_action"] .. '"') or DEFAULT_UNAUTH_ACTION) - out:write(config) + out:write(content) end -- starts a server instance with some customizations of the configuration. @@ -535,8 +542,11 @@ function test_support.start_server(custom_config) assert(os.execute("rm -rf /tmp/server"), "failed to remove old server dir") assert(os.execute("mkdir -p /tmp/server/conf"), "failed to create server dir") assert(os.execute("mkdir -p /tmp/server/logs"), "failed to create log dir") - local out = assert(io.open("/tmp/server/conf/nginx.conf", "w")) - write_config(out, custom_config) + local out = assert(io.open("/tmp/server/conf/test_globals.lua", "w")) + write_template(out, DEFAULT_INIT_TEMPLATE, custom_config) + assert(out:close()) + out = assert(io.open("/tmp/server/conf/nginx.conf", "w")) + write_template(out, DEFAULT_CONFIG_TEMPLATE, custom_config) assert(out:close()) assert(os.execute("openresty -c /tmp/server/conf/nginx.conf > /dev/null"), "failed to start nginx") end From 6eaa5e309f1ec41463c57dbd46a8c79f364809d5 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sun, 26 Jun 2022 14:58:43 +0200 Subject: [PATCH 42/77] modernize test image a bit Signed-off-by: Stefan Bodewig --- tests/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Dockerfile b/tests/Dockerfile index e433be9a..1e35a28d 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -1,4 +1,4 @@ -FROM openresty/openresty:bionic +FROM openresty/openresty:focal # install dependencies RUN ["luarocks", "install", "lua-resty-session"] From 78a75c76d58a88fca758e782ec787bfd481cce84 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sun, 26 Jun 2022 15:21:35 +0200 Subject: [PATCH 43/77] avoid nil refernece when there are no headers Signed-off-by: Stefan Bodewig --- tests/spec/test_support.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/spec/test_support.lua b/tests/spec/test_support.lua index 9ebc65f2..fb1d2299 100644 --- a/tests/spec/test_support.lua +++ b/tests/spec/test_support.lua @@ -602,7 +602,8 @@ end -- returns a Cookie header value based on all cookies requested via -- Set-Cookie inside headers function test_support.extract_cookies(headers) - local pair = headers["set-cookie"] or '' + local h = headers or {} + local pair = h["set-cookie"] or '' local semi = pair:find(";") if semi then pair = pair:sub(1, semi - 1) From 0fdbb4c8c86d2ddd616e85722dc274e9bc9f1f47 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sun, 26 Jun 2022 15:52:00 +0200 Subject: [PATCH 44/77] looks as if github runners didn't reject TESTNET-1 IPs immediately Signed-off-by: Stefan Bodewig --- tests/spec/redirect_to_op_spec.lua | 1 + tests/spec/token_request_spec.lua | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/spec/redirect_to_op_spec.lua b/tests/spec/redirect_to_op_spec.lua index 97fc3986..efadd723 100644 --- a/tests/spec/redirect_to_op_spec.lua +++ b/tests/spec/redirect_to_op_spec.lua @@ -135,6 +135,7 @@ end) describe("when discovery endpoint is not reachable", function() test_support.start_server({ oidc_opts = { + timeout = 40000, discovery = "http://192.0.2.1/" }, }) diff --git a/tests/spec/token_request_spec.lua b/tests/spec/token_request_spec.lua index 320f4109..a7dfd50e 100644 --- a/tests/spec/token_request_spec.lua +++ b/tests/spec/token_request_spec.lua @@ -151,6 +151,7 @@ end) describe("if token endpoint is not reachable", function() test_support.start_server({ oidc_opts = { + timeout = 40000, discovery = { token_endpoint = "http://192.0.2.1/" } From b07330120ffe54dd3fbeac247726b76d0f9dc793 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sun, 26 Jun 2022 16:02:54 +0200 Subject: [PATCH 45/77] it really looks as if github runners didn't reject TESTNET-1 IPs immediately Signed-off-by: Stefan Bodewig --- tests/spec/access_token_access_spec.lua | 1 + tests/spec/bearer_token_verification_spec.lua | 1 + tests/spec/introspection_spec.lua | 1 + tests/spec/userinfo_spec.lua | 1 + 4 files changed, 4 insertions(+) diff --git a/tests/spec/access_token_access_spec.lua b/tests/spec/access_token_access_spec.lua index 738a3aec..87128e31 100644 --- a/tests/spec/access_token_access_spec.lua +++ b/tests/spec/access_token_access_spec.lua @@ -200,6 +200,7 @@ end) describe("when token endpoint is not reachable", function() test_support.start_server({ access_token_opts = { + timeout = 40000, discovery = { token_endpoint = "http://192.0.2.1/" } diff --git a/tests/spec/bearer_token_verification_spec.lua b/tests/spec/bearer_token_verification_spec.lua index 0be18b13..8baca8da 100644 --- a/tests/spec/bearer_token_verification_spec.lua +++ b/tests/spec/bearer_token_verification_spec.lua @@ -438,6 +438,7 @@ end) describe("when jwks endpoint is not reachable", function() test_support.start_server({ verify_opts = { + timeout = 40000, discovery = { jwks_uri = "http://192.0.2.1/" } diff --git a/tests/spec/introspection_spec.lua b/tests/spec/introspection_spec.lua index a9efd8b4..3b5437b7 100644 --- a/tests/spec/introspection_spec.lua +++ b/tests/spec/introspection_spec.lua @@ -395,6 +395,7 @@ end) describe("when introspection endpoint is not reachable", function() test_support.start_server({ introspection_opts = { + timeout = 40000, introspection_endpoint = "http://192.0.2.1/" }, }) diff --git a/tests/spec/userinfo_spec.lua b/tests/spec/userinfo_spec.lua index da30b887..800a84a7 100644 --- a/tests/spec/userinfo_spec.lua +++ b/tests/spec/userinfo_spec.lua @@ -73,6 +73,7 @@ end) describe("when userinfo endpoint is not reachable", function() test_support.start_server({ oidc_opts = { + timeout = 40000, discovery = { userinfo_endpoint = "http://192.0.2.1/" } From 6f0f1e07bde5f132fedf09601292f6215b0fe0a1 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sun, 6 Nov 2022 18:30:33 +0100 Subject: [PATCH 46/77] add new cofig option for local redirect_uri path see #453 Signed-off-by: Stefan Bodewig --- ChangeLog | 8 ++++++ README.md | 39 ++++++++++++++++++++++++++++ lib/resty/openidc.lua | 3 +++ tests/spec/redirect_from_op_spec.lua | 28 ++++++++++++++++++++ 4 files changed, 78 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5b718d3f..b413072d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +11/06/2022 +- a new option local_redirect_path can be used is situations where the + redirect_uri as is visible to lua-resty-openidc is not simply the path + segment of the configured redirect_uri but something more + complex. This is needed for example if a reverse proxy in front of + your server adds a prefix of rewrites URIs in a more complex way; + see #453 + 03/05/2022 - improved error message when expecting a Bearer token header and the header doesn't contain a space character; see #421 diff --git a/README.md b/README.md index 91738c53..eb1efef8 100644 --- a/README.md +++ b/README.md @@ -274,6 +274,45 @@ h2JHukolz9xf6qN61QMLSd83+kwoBr2drp6xg3eGDLIkQCQLrkY= } ``` +## About `redirect_uri` + +The so called `redirect_uri` is an URI that is part of the OpenID +Connect protocoll. The redirect URI is registered with your OpenID +Connect provider and is the URI your provider will redirect the users +to after successful login. This URI then is handelled by +lua-resty-openidc where it obtains tokens and performs some checks and +only after that the browser is redirected to where your user wanted to +go initially. + +The `redirect_uri` is not expected to be handelled by your appication +code at all. It must be an URI wthat lua-resty-openidc is responsible +for so it must be in a `location` protected by lua-resty-openidc. + +You configure the `redirect_uri` on the lua-resty-openidc side via the +`opts.redirect_uri` parameter (which defaults to `/redirect_uri`). If +it starts with a `/` then lua-resty-openidc will prepend the protocoll +and current hostname to it when sending the URI to the OpenID Connect +provider (taking `Forwarded` and `X-Forwarded-*` HTTP headers into +account). But you can also specify an absolute URI containing host and +protocoll yourself. + +Before version 1.6.1 `opts.redirect_uri_path` has been the way to +configure the `redirect_uri` without any option to take control over +the protocoll and host parts. + +Whenever lua-resty-openidc "sees" a local path navigated that matches +the path of `opts.redirect_uri` (or `opts.redirect_uri_path`) it will +intercept the request and handle it itself. + +This works for most cases but sometimes the externally visible +`redirect_uri` has a different path than the one locally visible to +the server. This may happen if a reverse proxy in front of your server +rewrites URIs before forwarding the requests. Therefore version 1.7.6 +introduced a new option `opts.local_redirect_uri_path`. If it is set +lua-resty-opendic will intercepts requests to this path rather than +the path of `opts.redirect_uri`. + + ## Check authentication only ```lua diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 6597cb28..cb78c052 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1435,6 +1435,9 @@ local function openidc_get_path(uri) end local function openidc_get_redirect_uri_path(opts) + if opts.local_redirect_uri_path then + return opts.local_redirect_uri_path + end return opts.redirect_uri and openidc_get_path(opts.redirect_uri) or opts.redirect_uri_path end diff --git a/tests/spec/redirect_from_op_spec.lua b/tests/spec/redirect_from_op_spec.lua index 8da7158f..126a488d 100644 --- a/tests/spec/redirect_from_op_spec.lua +++ b/tests/spec/redirect_from_op_spec.lua @@ -179,3 +179,31 @@ describe("when the redirect_uri and target-uri are specified as absolute URIs", end) end) end) + +describe("when redirect_uri and local_redirect_uri_path are specified", function() + test_support.start_server({ + oidc_opts = { + redirect_uri = 'https://example.com/foo/default-absolute/redirect_uri', + local_redirect_uri_path = '/default-absolute/redirect_uri', + }, + }) + teardown(test_support.stop_server) + local _, _, headers = http.request({ + url = "http://localhost/default-absolute/t", + redirect = false + }) + local state = test_support.grab(headers, 'state') + test_support.register_nonce(headers) + local cookie_header = test_support.extract_cookies(headers) + describe("accessing the redirect_uri path with good parameters", function() + local _, redirStatus, h = http.request({ + url = "http://localhost/default-absolute/redirect_uri?code=foo&state=" .. state, + headers = { cookie = cookie_header }, + redirect = false + }) + it("redirects to the original URI", function() + assert.are.equals(302, redirStatus) + assert.are.equals("http://localhost/default-absolute/t", h.location) + end) + end) +end) From a488e08065810b17414647f450d3830e92e63027 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Wed, 11 Jan 2023 13:47:39 +0100 Subject: [PATCH 47/77] verify x5c claim in JWK actually holds an array see #459 Signed-off-by: Stefan Bodewig --- lib/resty/openidc.lua | 4 ++++ tests/spec/bearer_token_verification_spec.lua | 24 +++++++++++++++++++ tests/spec/rsa_key_jwk_with_broken_x5c.json | 10 ++++++++ 3 files changed, 38 insertions(+) create mode 100644 tests/spec/rsa_key_jwk_with_broken_x5c.json diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index cb78c052..a73b7143 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -908,6 +908,10 @@ local function openidc_pem_from_jwk(opts, kid) end local x5c = jwk.x5c + if x5c and type(x5c) ~= 'table' then + log(WARN, "Found invalid JWK with x5c claim not being an array but a " .. type(x5c)) + x5c = nil + end if x5c and #(jwk.x5c) == 0 then log(WARN, "Found invalid JWK with empty x5c array, ignoring x5c claim") x5c = nil diff --git a/tests/spec/bearer_token_verification_spec.lua b/tests/spec/bearer_token_verification_spec.lua index 8baca8da..7834acaf 100644 --- a/tests/spec/bearer_token_verification_spec.lua +++ b/tests/spec/bearer_token_verification_spec.lua @@ -203,6 +203,30 @@ describe("when the JWK specifies a kid and the JWKS does not contain a key with end) +describe("when the JWKS contains a broken x5c which is not an array", function() + test_support.start_server({ + verify_opts = { + discovery = { + jwks_uri = "http://127.0.0.1/jwk", + } + }, + jwk = test_support.load("/spec/rsa_key_jwk_with_broken_x5c.json"), + }) + teardown(test_support.stop_server) + local jwt = test_support.trim(http.request("http://127.0.0.1/jwt")) + local _, status = http.request({ + url = "http://127.0.0.1/verify_bearer_token", + headers = { authorization = "Bearer " .. jwt } + }) + it("the token is invalid", function() + assert.are.equals(401, status) + end) + it("an error is logged", function() + assert.error_log_contains("Found invalid JWK with x5c claim not being an array but a string") + end) + +end) + describe("when the JWK specifies no kid and the JWKS contains multiple keys", function() test_support.start_server({ verify_opts = { diff --git a/tests/spec/rsa_key_jwk_with_broken_x5c.json b/tests/spec/rsa_key_jwk_with_broken_x5c.json new file mode 100644 index 00000000..f7863b5e --- /dev/null +++ b/tests/spec/rsa_key_jwk_with_broken_x5c.json @@ -0,0 +1,10 @@ +{ + "keys": [ + { + "kty": "RSA", + "use": "sig", + "kid": "abcd", + "x5c": "MIIC+zCCAeOgAwIBAgIJAOlUwkUgtiAjMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0xNzEwMjAwODAyMzBaFw0yNzEwMTgwODAyMzBaMBQxEjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMLGOeOLW3uZr6jbrkE4i+Cp9LcvfWhOcff8D68qmcWTwfYSDWTGKPbME83EuFwrpVzBZqIV2VaR7L5uX3+5MZij2DeG52Gdv+QnP0JlyHAxRWnNWSYu+ZURlwhDBi7g+rxTq/3/8DeFZQJtf6/pcWxLidwe6fQpQK0XkbRfxi3os0+YAEDedSyVzsIP9buz2KkjtHJ+RWxTGC61J/vJY7ruSVBDkPLBbMHFkTRUqATZ76B/DDtn5lyctbTUZKUUGljiwvu8zK2thp5CjMnP8DcCP0e8ZT5IUBN73VYksvf3Die8+axKUuFAyYRjv8mKbWXRFwJyUelC72R6pvcTBzcCAwEAAaNQME4wHQYDVR0OBBYEFEp61+QOEp6ZR/GpTood068poIf3MB8GA1UdIwQYMBaAFEp61+QOEp6ZR/GpTood068poIf3MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADqM/3yygJOF+k8AXPk2AGOfF7EMtm8GYVETUBwunYqgPHMoZj/+/IfHYg0BNx4T5Uh7yABvICO8KCpIrog0boopCNBlrkKs1L8WGnK9L8EQinA2JM+rL1UBAuPVFLGdT9LEjhwoJsRrRet9Pt79+iHP4kS2priuwKHOzsGwmj7Ptstx4lf6dOwElgGlxNDI8YnG4a4NlOQ0HMGtaS/bvB8hMPGRb8uCmnlrPVOQFOkUCwTTA5D1DjWmXg+aCwGf313MyH8y0TQ8aNkxJkcHHGTZELXmS+t9BnYvpNm+sQkqfVkwxgnP/R8wPH36rnID6QSH6/mFhz6S21qK5p3vVys=" + } + ] +} From 6fd9aa345d64f4145eaed067b8ebc648e5d9e32c Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Fri, 13 Jan 2023 07:00:07 +0100 Subject: [PATCH 48/77] x5c is not base64url encoded, see RFC7517 Section 4.7 closes #460 Signed-off-by: Stefan Bodewig --- ChangeLog | 5 +++++ lib/resty/openidc.lua | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index b413072d..23abbe11 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +01/13/2023 +- when parsing JWKs with an x5c claim the claim was wronly assumed to + be base64url encoded instead of base64 encoded; + see #460 + 11/06/2022 - a new option local_redirect_path can be used is situations where the redirect_uri as is visible to lua-resty-openidc is not simply the path diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index a73b7143..0ab97452 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -844,7 +844,7 @@ end local function openidc_pem_from_x5c(x5c) log(DEBUG, "Found x5c, getting PEM public key from x5c entry of json public key") - local chunks = split_by_chunk(b64(openidc_base64_url_decode(x5c[1])), 64) + local chunks = split_by_chunk(x5c[1], 64) local pem = "-----BEGIN CERTIFICATE-----\n" .. table.concat(chunks, "\n") .. "\n-----END CERTIFICATE-----" From 5a7b9e2cfe4f5aab7c60032e6ca94d2d534f7d19 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Mon, 30 Jan 2023 19:06:51 +0100 Subject: [PATCH 49/77] release 1.7.6 Signed-off-by: Stefan Bodewig --- .github/issue_template.md | 2 +- ChangeLog | 3 +++ lib/resty/openidc.lua | 2 +- ...idc-1.7.5-1.rockspec => lua-resty-openidc-1.7.6-1.rockspec | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) rename lua-resty-openidc-1.7.5-1.rockspec => lua-resty-openidc-1.7.6-1.rockspec (97%) diff --git a/.github/issue_template.md b/.github/issue_template.md index f97fcf40..b692773a 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -7,7 +7,7 @@ A new issue about a bug should be verified with a minimized example. ###### Environment -- lua-resty-openidc version (e.g. 1.7.5) +- lua-resty-openidc version (e.g. 1.7.6) - OpenID Connect provider (e.g. Keycloak, Azure AD) ###### Expected behaviour diff --git a/ChangeLog b/ChangeLog index 23abbe11..fe3839c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +30/01/2023 +- release 1.7.6 + 01/13/2023 - when parsing JWKs with an x5c claim the claim was wronly assumed to be base64url encoded instead of base64 encoded; diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 0ab97452..0b0c2ad9 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -76,7 +76,7 @@ local supported_token_auth_methods = { } local openidc = { - _VERSION = "1.7.5" + _VERSION = "1.7.6" } openidc.__index = openidc diff --git a/lua-resty-openidc-1.7.5-1.rockspec b/lua-resty-openidc-1.7.6-1.rockspec similarity index 97% rename from lua-resty-openidc-1.7.5-1.rockspec rename to lua-resty-openidc-1.7.6-1.rockspec index 10c152a7..2e6f0e16 100644 --- a/lua-resty-openidc-1.7.5-1.rockspec +++ b/lua-resty-openidc-1.7.6-1.rockspec @@ -1,8 +1,8 @@ package = "lua-resty-openidc" -version = "1.7.5-1" +version = "1.7.6-1" source = { url = "git://github.com/zmartzone/lua-resty-openidc", - tag = "v1.7.5", + tag = "v1.7.6", dir = "lua-resty-openidc" } description = { From 18daee9994d73d66b41330c44f6d611d73abd048 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Mon, 30 Jan 2023 19:58:31 +0100 Subject: [PATCH 50/77] switch to https --- lua-resty-openidc-1.7.6-1.rockspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua-resty-openidc-1.7.6-1.rockspec b/lua-resty-openidc-1.7.6-1.rockspec index 2e6f0e16..9008753c 100644 --- a/lua-resty-openidc-1.7.6-1.rockspec +++ b/lua-resty-openidc-1.7.6-1.rockspec @@ -1,7 +1,7 @@ package = "lua-resty-openidc" version = "1.7.6-1" source = { - url = "git://github.com/zmartzone/lua-resty-openidc", + url = "https://github.com/zmartzone/lua-resty-openidc", tag = "v1.7.6", dir = "lua-resty-openidc" } From a3b1198a79a87c67b29f3af8e21d2d29a762b2b7 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Mon, 30 Jan 2023 19:58:42 +0100 Subject: [PATCH 51/77] add myself as author --- dist.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist.ini b/dist.ini index 592dc39b..d85b0ecf 100644 --- a/dist.ini +++ b/dist.ini @@ -1,6 +1,6 @@ name = lua-resty-openidc abstract = A library for NGINX implementing the OpenID Connect Relying Party (RP) and the OAuth 2.0 Resource Server (RS) functionality -author = Hans Zandbelt (@zandbelt) +author = Hans Zandbelt (@zandbelt), Stefan Bodewig (@bodewig) is_original = yes license = apache2 repo_link = https://github.com/zmartzone/lua-resty-openidc From 9533d6c50b0568a5e76349ed6c59a35443df9be3 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Mon, 30 Jan 2023 20:50:46 +0100 Subject: [PATCH 52/77] match second upload by Hans --- ...idc-1.7.6-1.rockspec => lua-resty-openidc-1.7.6-2.rockspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename lua-resty-openidc-1.7.6-1.rockspec => lua-resty-openidc-1.7.6-2.rockspec (95%) diff --git a/lua-resty-openidc-1.7.6-1.rockspec b/lua-resty-openidc-1.7.6-2.rockspec similarity index 95% rename from lua-resty-openidc-1.7.6-1.rockspec rename to lua-resty-openidc-1.7.6-2.rockspec index 9008753c..de7c9d84 100644 --- a/lua-resty-openidc-1.7.6-1.rockspec +++ b/lua-resty-openidc-1.7.6-2.rockspec @@ -1,7 +1,7 @@ package = "lua-resty-openidc" -version = "1.7.6-1" +version = "1.7.6-2" source = { - url = "https://github.com/zmartzone/lua-resty-openidc", + url = "git+https://github.com/zmartzone/lua-resty-openidc", tag = "v1.7.6", dir = "lua-resty-openidc" } From 66d285071de5c7749cd2dfd051e9bf0fa37732aa Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Mon, 30 Jan 2023 20:51:03 +0100 Subject: [PATCH 53/77] stop advertizing opm support --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index eb1efef8..0179b40e 100644 --- a/README.md +++ b/README.md @@ -49,17 +49,16 @@ introspection for access token validation. ## Installation -If you're using `opm` execute the following: - - opm install zmartzone/lua-resty-openidc - -If you're using `luarocks` execute the following: +Using `luarocks` execute the following: luarocks install lua-resty-openidc Otherwise copy `openidc.lua` somewhere in your `lua_package_path` under a directory named `resty`. If you are using [OpenResty](http://openresty.org/), the default location would be `/usr/local/openresty/lualib/resty`. +Older versions of lua-resty-openidc could also be installed using opm +but this is no longer supported. + ## Sample Configuration for Google+ Signin From 649ebba54a3201651f9f2b91a2e19ecd3f3dd0f8 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Fri, 3 Feb 2023 07:46:56 +0100 Subject: [PATCH 54/77] pin lua-resty-session to 3.x Signed-off-by: Hans Zandbelt --- lua-resty-openidc-1.7.6-2.rockspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua-resty-openidc-1.7.6-2.rockspec b/lua-resty-openidc-1.7.6-2.rockspec index de7c9d84..23e436c3 100644 --- a/lua-resty-openidc-1.7.6-2.rockspec +++ b/lua-resty-openidc-1.7.6-2.rockspec @@ -24,7 +24,7 @@ description = { dependencies = { "lua >= 5.1", "lua-resty-http >= 0.08", - "lua-resty-session >= 2.8", + "lua-resty-session >= 2.8, < 4.0.0" "lua-resty-jwt >= 0.2.0" } build = { From 81b6e25037d88c109c5b1076e1985501e02d3ae5 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Fri, 3 Feb 2023 07:50:43 +0100 Subject: [PATCH 55/77] typo Signed-off-by: Hans Zandbelt --- lua-resty-openidc-1.7.6-2.rockspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua-resty-openidc-1.7.6-2.rockspec b/lua-resty-openidc-1.7.6-2.rockspec index 23e436c3..c6df7ca0 100644 --- a/lua-resty-openidc-1.7.6-2.rockspec +++ b/lua-resty-openidc-1.7.6-2.rockspec @@ -24,7 +24,7 @@ description = { dependencies = { "lua >= 5.1", "lua-resty-http >= 0.08", - "lua-resty-session >= 2.8, < 4.0.0" + "lua-resty-session >= 2.8, < 4.0.0", "lua-resty-jwt >= 0.2.0" } build = { From ed8e269698a761897fff6d4bc1fb9ee3a300c59d Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Fri, 3 Feb 2023 07:58:48 +0100 Subject: [PATCH 56/77] using 2 specs did not seem to work, now try with <= 3.10 Signed-off-by: Hans Zandbelt --- lua-resty-openidc-1.7.6-2.rockspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua-resty-openidc-1.7.6-2.rockspec b/lua-resty-openidc-1.7.6-2.rockspec index c6df7ca0..6ac64389 100644 --- a/lua-resty-openidc-1.7.6-2.rockspec +++ b/lua-resty-openidc-1.7.6-2.rockspec @@ -24,7 +24,7 @@ description = { dependencies = { "lua >= 5.1", "lua-resty-http >= 0.08", - "lua-resty-session >= 2.8, < 4.0.0", + "lua-resty-session <= 3.10", "lua-resty-jwt >= 0.2.0" } build = { From fbebba8db912adf0f74f68d5d21eb86573f3e469 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Fri, 3 Feb 2023 08:02:09 +0100 Subject: [PATCH 57/77] CI: need to pin lua-resty-session to 3.10 in Dockerfile Signed-off-by: Hans Zandbelt --- tests/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Dockerfile b/tests/Dockerfile index 1e35a28d..cc3345d1 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -1,7 +1,7 @@ FROM openresty/openresty:focal # install dependencies -RUN ["luarocks", "install", "lua-resty-session"] +RUN ["luarocks", "install", "lua-resty-session", "3.10"] RUN ["luarocks", "install", "lua-resty-http"] RUN ["luarocks", "install", "lua-resty-jwt"] From 3f590f63cebbb25244ed8b21b9bfb9d3d6669ff0 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Fri, 3 Feb 2023 08:03:56 +0100 Subject: [PATCH 58/77] rockspec: revert to lua-resty-session > 2.8, < 4.0.0 Signed-off-by: Hans Zandbelt --- lua-resty-openidc-1.7.6-2.rockspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua-resty-openidc-1.7.6-2.rockspec b/lua-resty-openidc-1.7.6-2.rockspec index 6ac64389..14c398b1 100644 --- a/lua-resty-openidc-1.7.6-2.rockspec +++ b/lua-resty-openidc-1.7.6-2.rockspec @@ -24,7 +24,7 @@ description = { dependencies = { "lua >= 5.1", "lua-resty-http >= 0.08", - "lua-resty-session <= 3.10", + "lua-resty-session > 2.8, < 4.0.0", "lua-resty-jwt >= 0.2.0" } build = { From 4b9316403e1d6a162aecea86c466f50fe78232e8 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Fri, 3 Feb 2023 14:57:31 +0100 Subject: [PATCH 59/77] 1.7.6-3: lua-resty-session >= 2.8, <= 3.10 Signed-off-by: Hans Zandbelt --- ...enidc-1.7.6-2.rockspec => lua-resty-openidc-1.7.6-3.rockspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename lua-resty-openidc-1.7.6-2.rockspec => lua-resty-openidc-1.7.6-3.rockspec (97%) diff --git a/lua-resty-openidc-1.7.6-2.rockspec b/lua-resty-openidc-1.7.6-3.rockspec similarity index 97% rename from lua-resty-openidc-1.7.6-2.rockspec rename to lua-resty-openidc-1.7.6-3.rockspec index 14c398b1..3fb85721 100644 --- a/lua-resty-openidc-1.7.6-2.rockspec +++ b/lua-resty-openidc-1.7.6-3.rockspec @@ -24,7 +24,7 @@ description = { dependencies = { "lua >= 5.1", "lua-resty-http >= 0.08", - "lua-resty-session > 2.8, < 4.0.0", + "lua-resty-session >= 2.8, <= 3.10", "lua-resty-jwt >= 0.2.0" } build = { From cad2915240885fe247f657129c6e1020e4fe932f Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Fri, 3 Feb 2023 14:58:57 +0100 Subject: [PATCH 60/77] fix build nr in rockspec Signed-off-by: Hans Zandbelt --- lua-resty-openidc-1.7.6-3.rockspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua-resty-openidc-1.7.6-3.rockspec b/lua-resty-openidc-1.7.6-3.rockspec index 3fb85721..1114c621 100644 --- a/lua-resty-openidc-1.7.6-3.rockspec +++ b/lua-resty-openidc-1.7.6-3.rockspec @@ -1,5 +1,5 @@ package = "lua-resty-openidc" -version = "1.7.6-2" +version = "1.7.6-3" source = { url = "git+https://github.com/zmartzone/lua-resty-openidc", tag = "v1.7.6", From 9ee1f6aeaf17ab56ff96a29d30df8d213b618e71 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sun, 5 Feb 2023 21:43:08 +0100 Subject: [PATCH 61/77] bump copyright years --- DISCLAIMER | 2 +- lib/resty/openidc.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DISCLAIMER b/DISCLAIMER index 5b1d5e54..d67cda09 100644 --- a/DISCLAIMER +++ b/DISCLAIMER @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2017-2022 ZmartZone Holding B.V. + * Copyright (C) 2017-2023 ZmartZone Holding B.V. * All rights reserved. * * ZmartZone IAM diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 0b0c2ad9..a1be3da7 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -17,7 +17,7 @@ specific language governing permissions and limitations under the License. *************************************************************************** -Copyright (C) 2017-2022 ZmartZone Holding B.V. +Copyright (C) 2017-2023 ZmartZone Holding B.V. Copyright (C) 2015-2017 Ping Identity Corporation All rights reserved. From 432d45a1d0c34d467c5b5d0407b8b5c0df0e77b4 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sun, 5 Feb 2023 21:44:37 +0100 Subject: [PATCH 62/77] record newer luarocks release Signed-off-by: Stefan Bodewig --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index fe3839c6..2a9b9242 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +03/02/2023 +- release 1.7.6-3 of luarock pinning lua-resty-session dependency to + not go beyond 3.1ß + 30/01/2023 - release 1.7.6 From 4ff418922465689ab64427edf3835c239c929f40 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Thu, 16 Feb 2023 07:22:02 +0100 Subject: [PATCH 63/77] properly base64url-encode n in test key Signed-off-by: Stefan Bodewig --- tests/spec/longer_rsa_key_jwk_with_n_and_e.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/spec/longer_rsa_key_jwk_with_n_and_e.json b/tests/spec/longer_rsa_key_jwk_with_n_and_e.json index f5b4b376..4e4d71ed 100644 --- a/tests/spec/longer_rsa_key_jwk_with_n_and_e.json +++ b/tests/spec/longer_rsa_key_jwk_with_n_and_e.json @@ -5,7 +5,7 @@ "use": "sig", "kid": "abcd", "e": "AQAB", - "n":"xRycpPkv96kQol6yONWtwwN9GQwEDtt9/Tb+U2hMWwRDfWlvBxXDM80D1yil8DQydQMeTgfoGaMqVLsZhTROluVDvopz308uq0/LbshyockOTRYI+ennVRAKMFnr64/OY/cVkLfgZxGJ9OZknulf/uRuVHhPnSl/2ZSBHXcuWxVwnfsA5CBMJq2JwUPTlay7QVEzXQyo7kUEjP+gkiccvl91Jl5Zk8khEVUg07rxPK8viRORC1DQQY3NAsKn+/BOcoOc7LwHPiE8/5vna5wWe+jllTkuwKOi+W/lMZH/ZlBwWKsnCgZx+N+r8vAstMsdIgkif0kV6Egjb2H15J4s1iaEyavq+QwpyEjOSIVsllXopHKgxNuVUvPSsMsrIhl36ruoGRBHcWlKeYOPD5mIgcH0fm9/Og+gQPx8UqpGsj6EiuWOuwFRJJQfFIFSKzsAkixuqwzIv2s4OgxAYqXaQHflSN4kKBPgWNS+Q86S98vsaekAkuXRfL83NlNZwsBk9gpGuZkgJLWWx3H5I5q39jpG9/v82SrPvTdhZh2UKHfKqBCEeonRu49s5bQswW3WlpZuBW/jx4YO34aKf3HHBeFn3PZDjuUNdxbUmUrRaNB/hqBT0HKmTJ/cX2pV4liZ3nJ0ScwChiuBUogc4Ix/FyZx1ssk+mmyKMznpt1Mnyk=" + "n":"xRycpPkv96kQol6yONWtwwN9GQwEDtt9_Tb-U2hMWwRDfWlvBxXDM80D1yil8DQydQMeTgfoGaMqVLsZhTROluVDvopz308uq0_LbshyockOTRYI-ennVRAKMFnr64_OY_cVkLfgZxGJ9OZknulf_uRuVHhPnSl_2ZSBHXcuWxVwnfsA5CBMJq2JwUPTlay7QVEzXQyo7kUEjP-gkiccvl91Jl5Zk8khEVUg07rxPK8viRORC1DQQY3NAsKn-_BOcoOc7LwHPiE8_5vna5wWe-jllTkuwKOi-W_lMZH_ZlBwWKsnCgZx-N-r8vAstMsdIgkif0kV6Egjb2H15J4s1iaEyavq-QwpyEjOSIVsllXopHKgxNuVUvPSsMsrIhl36ruoGRBHcWlKeYOPD5mIgcH0fm9_Og-gQPx8UqpGsj6EiuWOuwFRJJQfFIFSKzsAkixuqwzIv2s4OgxAYqXaQHflSN4kKBPgWNS-Q86S98vsaekAkuXRfL83NlNZwsBk9gpGuZkgJLWWx3H5I5q39jpG9_v82SrPvTdhZh2UKHfKqBCEeonRu49s5bQswW3WlpZuBW_jx4YO34aKf3HHBeFn3PZDjuUNdxbUmUrRaNB_hqBT0HKmTJ_cX2pV4liZ3nJ0ScwChiuBUogc4Ix_FyZx1ssk-mmyKMznpt1Mnyk" } ] } From ae3bc543e01160ec986843e0305e2f4d97edf3dc Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Fri, 10 Mar 2023 18:59:51 +0100 Subject: [PATCH 64/77] deal with Authorization headers without blank closes #473 Signed-off-by: Stefan Bodewig --- ChangeLog | 7 ++++++- lib/resty/openidc.lua | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2a9b9242..ef5f63f7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,9 @@ -03/02/2023 +03/10/2023 +- when looking for a bearer token an exception occured if the + Authorization header didn't contain any space character; + see #473 + +02/03/2023 - release 1.7.6-3 of luarock pinning lua-resty-session dependency to not go beyond 3.1ß diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index a1be3da7..4edc73bf 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1651,7 +1651,7 @@ local function openidc_get_bearer_access_token(opts) end local divider = header:find(' ') - if divider == 0 or string.lower(header:sub(0, divider - 1)) ~= string.lower("Bearer") then + if divider == nil or divider == 0 or string.lower(header:sub(0, divider - 1)) ~= string.lower("Bearer") then err = "no Bearer authorization header value found" log(ERROR, err) return nil, err From 40a3f8ec9dc0f36dd4999464ca107206046481c7 Mon Sep 17 00:00:00 2001 From: lijunlong Date: Sun, 12 Feb 2023 21:29:42 +0800 Subject: [PATCH 65/77] optimization: some small optimizations. --- AUTHORS | 2 ++ lib/resty/openidc.lua | 36 ++++++++++++------------------------ 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/AUTHORS b/AUTHORS index e2fd56d4..2c39bbc6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -36,3 +36,5 @@ reporting bugs, providing fixes, suggesting useful features or other: Eduardo Gonçalves Thorsten Fleischmann Tilmann Hars + Junlong Li + diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 4edc73bf..1f3854e7 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -52,6 +52,8 @@ local type = type local ngx = ngx local b64 = ngx.encode_base64 local unb64 = ngx.decode_base64 +local b64url = require("ngx.base64").encode_base64url +local unb64url = require("ngx.base64").decode_base64url local log = ngx.log local DEBUG = ngx.DEBUG @@ -278,23 +280,6 @@ local function openidc_get_redirect_uri(opts, session) return scheme .. "://" .. host .. path end --- perform base64url decoding -local function openidc_base64_url_decode(input) - local reminder = #input % 4 - if reminder > 0 then - local padlen = 4 - reminder - input = input .. string.rep('=', padlen) - end - input = input:gsub('%-', '+'):gsub('_', '/') - return unb64(input) -end - --- perform base64url encoding -local function openidc_base64_url_encode(input) - local output = b64(input, true) - return output:gsub('%+', '-'):gsub('/', '_') -end - local function openidc_combine_uri(uri, params) if params == nil or next(params) == nil then return uri @@ -310,10 +295,12 @@ local function decorate_request(http_request_decorator, req) return http_request_decorator and http_request_decorator(req) or req end +local sha256 = (require 'resty.sha256'):new() local function openidc_s256(verifier) - local sha256 = (require 'resty.sha256'):new() sha256:update(verifier) - return openidc_base64_url_encode(sha256:final()) + local s256 = b64url(sha256:final()) + sha256:reset() + return s256 end -- send the browser of to the OP's authorization endpoint @@ -326,7 +313,7 @@ local function openidc_authorize(opts, session, target_url, prompt) local state = resty_string.to_hex(resty_random.bytes(16)) local nonce = (opts.use_nonce == nil or opts.use_nonce) and resty_string.to_hex(resty_random.bytes(16)) - local code_verifier = opts.use_pkce and openidc_base64_url_encode(resty_random.bytes(32)) + local code_verifier = opts.use_pkce and b64url(resty_random.bytes(32)) -- assemble the parameters to the authentication request local params = { @@ -537,8 +524,8 @@ local function openidc_access_token_expires_in(opts, expires_in) end local function openidc_load_jwt_none_alg(enc_hdr, enc_payload) - local header = cjson_s.decode(openidc_base64_url_decode(enc_hdr)) - local payload = cjson_s.decode(openidc_base64_url_decode(enc_payload)) + local header = cjson_s.decode(unb64url(enc_hdr)) + local payload = cjson_s.decode(unb64url(enc_payload)) if header and payload and header.alg == "none" then return { raw_header = enc_hdr, @@ -856,7 +843,7 @@ local function openidc_pem_from_rsa_n_and_e(n, e) log(DEBUG, "getting PEM public key from n and e parameters of json public key") local der_key = { - openidc_base64_url_decode(n), openidc_base64_url_decode(e) + unb64url(n), unb64url(e) } local encoded_key = encode_sequence_of_integer(der_key) local pem = der2pem(encode_sequence({ @@ -949,8 +936,9 @@ local function is_algorithm_expected(jwt_header, expected_algs) return true end if type(expected_algs) == 'string' then - expected_algs = { expected_algs } + return expected_algs == jwt_header.alg end + for _, alg in ipairs(expected_algs) do if alg == jwt_header.alg then return true From 65e4794d579d38ffcb5f28aeabafb0f207fc3c29 Mon Sep 17 00:00:00 2001 From: Nate Plumm Date: Thu, 18 Jan 2024 17:11:53 -0700 Subject: [PATCH 66/77] #345 handle the userinfo response as JWT --- lib/resty/openidc.lua | 11 +++++++++++ tests/spec/test_support.lua | 9 +++++++++ tests/spec/userinfo_spec.lua | 23 +++++++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 1f3854e7..e7b8872e 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -623,6 +623,17 @@ function openidc.call_userinfo_endpoint(opts, access_token) log(DEBUG, "userinfo response: ", res.body) + -- handle if the response type is a jwt/signed payload + local responseType = string.lower(res.headers["Content-Type"]) + if string.find(responseType, "application/jwt") then + local json, err = openidc.jwt_verify(res.body, opts) + if err then + err = "userinfo jwt could not be verified: " .. err + return nil, err + end + return json + end + -- parse the response from the user info endpoint return openidc_parse_json_response(res) end diff --git a/tests/spec/test_support.lua b/tests/spec/test_support.lua index fb1d2299..cca73e24 100644 --- a/tests/spec/test_support.lua +++ b/tests/spec/test_support.lua @@ -326,6 +326,15 @@ http { } } + location /user-info-signed { + content_by_lua_block { + local auth = ngx.req.get_headers()["Authorization"] + ngx.header.content_type = 'application/jwt;charset=UTF-8' + local signed_userinfo = test_globals.create_jwt(USERINFO) + ngx.print(signed_userinfo) + } + } + location /introspection { content_by_lua_block { ngx.req.read_body() diff --git a/tests/spec/userinfo_spec.lua b/tests/spec/userinfo_spec.lua index 800a84a7..856a2378 100644 --- a/tests/spec/userinfo_spec.lua +++ b/tests/spec/userinfo_spec.lua @@ -169,3 +169,26 @@ describe("when userinfo endpoint doesn't return proper JSON", function() assert.error_log_contains("JSON decoding failed") end) end) + +describe("when userinfo endpoint returns a JWT", function() + test_support.start_server({ + oidc_opts = { + discovery = { + userinfo_endpoint = "http://127.0.0.1/user-info-signed", + token_endpoint_auth_methods_supported = { "private_key_jwt" }, + }, + token_endpoint_auth_method = "private_key_jwt", + client_rsa_private_key = test_support.load("/spec/private_rsa_key.pem"), + public_key = test_support.load("/spec/public_rsa_key.pem"), + }, + }) + teardown(test_support.stop_server) + local _, status = test_support.login() + it("login succeeds", function() + assert.are.equals(302, status) + end) + it("an error has not been logged", function() + assert.is_not.error_log_contains("JSON decoding failed") + assert.is_not.error_log_contains("userinfo jwt could not be verified") + end) +end) From d284d715062bdce33d0482a9adbb04396b92cd91 Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sun, 25 Aug 2024 13:25:54 +0200 Subject: [PATCH 67/77] restrict zero-pixel image logout to actual image requests should fix #521 --- ChangeLog | 4 ++++ lib/resty/openidc.lua | 37 ++++++++++++++++++++++++++++++++++--- tests/spec/logout_spec.lua | 22 +++++++++++++++++++++- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index ef5f63f7..1daa7276 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +08/25/2024 +- don't return a zero-pixel image in logout for Firefox 128 and later + see #521 + 03/10/2023 - when looking for a bearer token an exception occured if the Authorization header didn't contain any space character; diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index e7b8872e..011f8923 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1280,6 +1280,38 @@ local openidc_transparent_pixel = "\137\080\078\071\013\010\026\010\000\000\000\ "\002\007\001\002\154\028\049\113\000\000\000\000\073\069\078\068" .. "\174\066\096\130" +local function request_prefers_png_over_html() + local headers = ngx.req.get_headers() + local header = get_first(headers['Accept']) + if not header then return false end + + -- https://httpwg.org/specs/rfc9110.html#field.accept + local accepted = {} + local function append_accepted_type(media_range_and_quality) + local media_range, quality = media_range_and_quality:match("(.+)%s*;%s*q=%s*([^%s]+)") + if media_range and quality then + accepted[#accepted + 1] = {media_range=media_range, quality=tonumber(quality)} + else + accepted[#accepted + 1] = {media_range=media_range_and_quality, quality=1} + end + end + header:gsub("[^,]+", append_accepted_type) + + table.sort(accepted, function(a1, a2) + return a1.quality > a2.quality + end) + + for _, a in ipairs(accepted) do + if a.media_range:find("text/html") then + return false + end + if a.media_range:find("image/png") then + return true + end + end + return false +end + -- handle logout local function openidc_logout(opts, session) local session_token = session.data.enc_id_token @@ -1308,9 +1340,8 @@ local function openidc_logout(opts, session) end end - local headers = ngx.req.get_headers() - local header = get_first(headers['Accept']) - if header and header:find("image/png") then + if request_prefers_png_over_html() then + -- support for Ping Federate's proprietary logout protocol ngx.header["Cache-Control"] = "no-cache, no-store" ngx.header["Pragma"] = "no-cache" ngx.header["P3P"] = "CAO PSA OUR" diff --git a/tests/spec/logout_spec.lua b/tests/spec/logout_spec.lua index 7a0fd7d3..37cfb6c9 100644 --- a/tests/spec/logout_spec.lua +++ b/tests/spec/logout_spec.lua @@ -22,6 +22,26 @@ describe("when the configured logout uri is invoked with a non-image request", f end) end) +describe("when the configured logout uri is invoked with Firefox 128's default Accept", function() + test_support.start_server() + teardown(test_support.stop_server) + local _, _, cookie = test_support.login() + local _, status, headers = http.request({ + url = "http://127.0.0.1/default/logout", + headers = { cookie = cookie, accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8" }, + redirect = false + }) + it("the response contains a default HTML-page", function() + assert.are.equals(200, status) + assert.are.equals("text/html", headers["content-type"]) + -- TODO should there be a Cache-Control header? + end) + it("the session cookie has been revoked", function() + assert.truthy(string.match(headers["set-cookie"], + "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + end) +end) + describe("when the configured logout uri is invoked with a png request", function() -- TODO should this really take precedence over a configured end_session_endpoint? test_support.start_server({ @@ -38,7 +58,7 @@ describe("when the configured logout uri is invoked with a png request", functio headers = { cookie = cookie, accept = "image/png" }, redirect = false }) - it("the response contains a default HTML-page", function() + it("the response contains a default PNG image", function() assert.are.equals(200, status) assert.are.equals("image/png", headers["content-type"]) assert.are.equals("no-cache, no-store", headers["cache-control"]) From ab9c3860c0c9bf3234405c6b79f1c9e18e5edc7d Mon Sep 17 00:00:00 2001 From: Stefan Bodewig Date: Sun, 25 Aug 2024 15:35:35 +0200 Subject: [PATCH 68/77] XHTML should also win over PNG --- lib/resty/openidc.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 011f8923..44310ffd 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1302,7 +1302,7 @@ local function request_prefers_png_over_html() end) for _, a in ipairs(accepted) do - if a.media_range:find("text/html") then + if a.media_range:find("text/html") or a.media_range:find("application/xhtml%+xml") then return false end if a.media_range:find("image/png") then From 0fa034a68eb21f7fdccdac861ac545580af430dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Old=C5=99ich=20Jedli=C4=8Dka?= Date: Fri, 28 Apr 2023 11:54:44 +0530 Subject: [PATCH 69/77] Migrate lua-resty-session to 4.0.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Oldřich Jedlička --- lib/resty/openidc.lua | 175 ++++++++++++++++------------- lua-resty-openidc-1.7.6-3.rockspec | 2 +- tests/Dockerfile | 2 +- tests/spec/logout_spec.lua | 34 +++--- 4 files changed, 118 insertions(+), 95 deletions(-) diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index e7b8872e..4a283b5e 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -46,12 +46,12 @@ local cjson_s = require("cjson.safe") local http = require("resty.http") local r_session = require("resty.session") local string = string +local table = table local ipairs = ipairs local pairs = pairs local type = type local ngx = ngx local b64 = ngx.encode_base64 -local unb64 = ngx.decode_base64 local b64url = require("ngx.base64").encode_base64url local unb64url = require("ngx.base64").decode_base64url @@ -351,11 +351,11 @@ local function openidc_authorize(opts, session, target_url, prompt) end -- store state in the session - session.data.original_url = target_url - session.data.state = state - session.data.nonce = nonce - session.data.code_verifier = code_verifier - session.data.last_authenticated = ngx.time() + session:set("original_url", target_url) + session:set("state", state) + session:set("nonce", nonce) + session:set("code_verifier", code_verifier) + session:set("last_authenticated", ngx.time()) if opts.lifecycle and opts.lifecycle.on_created then err = opts.lifecycle.on_created(session) @@ -365,7 +365,11 @@ local function openidc_authorize(opts, session, target_url, prompt) end end - session:save() + local res + res, err = session:save() + if err then + log(WARN, "unable to save session: " .. err) + end -- redirect to the /authorization endpoint ngx.header["Cache-Control"] = "no-cache, no-store, max-age=0" @@ -1072,7 +1076,7 @@ local function openidc_load_and_validate_jwt_id_token(opts, jwt_id_token, sessio log(DEBUG, "id_token payload: ", cjson.encode(jwt_obj.payload)) -- validate the id_token contents - if openidc_validate_id_token(opts, id_token, session.data.nonce) == false then + if openidc_validate_id_token(opts, id_token, session:get("nonce")) == false then err = "id_token validation failed" log(ERROR, err) return nil, err @@ -1095,20 +1099,21 @@ local function openidc_authorization_response(opts, session) if not args.code or not args.state then err = "unhandled request to the redirect_uri: " .. ngx.var.request_uri log(ERROR, err) - return nil, err, session.data.original_url, session + return nil, err, session:get("original_url"), session end -- check that the state returned in the response against the session; prevents CSRF - if args.state ~= session.data.state then - log_err = "state from argument: " .. (args.state and args.state or "nil") .. " does not match state restored from session: " .. (session.data.state and session.data.state or "nil") + local state = session:get("state") + if args.state ~= state then + log_err = "state from argument: " .. (args.state and args.state or "nil") .. " does not match state restored from session: " .. (state and state or "nil") client_err = "state from argument does not match state restored from session" log(ERROR, log_err) - return nil, client_err, session.data.original_url, session + return nil, client_err, session:get("original_url"), session end err = ensure_config(opts) if err then - return nil, err, session.data.original_url, session + return nil, err, session:get("original_url"), session end -- check the iss if returned from the OP @@ -1116,7 +1121,7 @@ local function openidc_authorization_response(opts, session) log_err = "iss from argument: " .. args.iss .. " does not match expected issuer: " .. opts.discovery.issuer client_err = "iss from argument does not match expected issuer" log(ERROR, log_err) - return nil, client_err, session.data.original_url, session + return nil, client_err, session:get("original_url"), session end -- check the client_id if returned from the OP @@ -1124,7 +1129,7 @@ local function openidc_authorization_response(opts, session) log_err = "client_id from argument: " .. args.client_id .. " does not match expected client_id: " .. opts.client_id client_err = "client_id from argument does not match expected client_id" log(ERROR, log_err) - return nil, client_err, session.data.original_url, session + return nil, client_err, session:get("original_url"), session end -- assemble the parameters to the token endpoint @@ -1132,8 +1137,8 @@ local function openidc_authorization_response(opts, session) grant_type = "authorization_code", code = args.code, redirect_uri = openidc_get_redirect_uri(opts, session), - state = session.data.state, - code_verifier = session.data.code_verifier + state = state, + code_verifier = session:get("code_verifier") } log(DEBUG, "Authentication with OP done -> Calling OP Token Endpoint to obtain tokens") @@ -1143,22 +1148,22 @@ local function openidc_authorization_response(opts, session) local json json, err = openidc.call_token_endpoint(opts, opts.discovery.token_endpoint, body, opts.token_endpoint_auth_method) if err then - return nil, err, session.data.original_url, session + return nil, err, session:get("original_url"), session end local id_token, err = openidc_load_and_validate_jwt_id_token(opts, json.id_token, session); if err then - return nil, err, session.data.original_url, session + return nil, err, session:get("original_url"), session end -- mark this sessions as authenticated - session.data.authenticated = true - -- clear state, nonce and code_verifier to protect against potential misuse - session.data.nonce = nil - session.data.state = nil - session.data.code_verifier = nil + session:set("authenticated", true) + session:set("nonce", nil) + session:set("state", nil) + session:set("code_verifier", nil) + if store_in_session(opts, 'id_token') then - session.data.id_token = id_token + session:set("id_token", id_token) end if store_in_session(opts, 'user') then @@ -1174,21 +1179,21 @@ local function openidc_authorization_response(opts, session) err = "\"sub\" claim in id_token (\"" .. (id_token.sub or "null") .. "\") is not equal to the \"sub\" claim returned from the userinfo endpoint (\"" .. (user.sub or "null") .. "\")" log(ERROR, err) else - session.data.user = user + session:set("user", user) end end end if store_in_session(opts, 'enc_id_token') then - session.data.enc_id_token = json.id_token + session:set("enc_id_token", json.id_token) end if store_in_session(opts, 'access_token') then - session.data.access_token = json.access_token - session.data.access_token_expiration = current_time - + openidc_access_token_expires_in(opts, json.expires_in) + session:set("access_token", json.access_token) + session:set("access_token_expiration", current_time + openidc_access_token_expires_in(opts, json.expires_in)) + if json.refresh_token ~= nil then - session.data.refresh_token = json.refresh_token + session:set("refresh_token", json.refresh_token) end end @@ -1196,17 +1201,22 @@ local function openidc_authorization_response(opts, session) err = opts.lifecycle.on_authenticated(session, id_token, json) if err then log(WARN, "failed in `on_authenticated` handler: " .. err) - return nil, err, session.data.original_url, session + return nil, err, session:get("original_url"), session end end -- save the session with the obtained id_token - session:save() + local res + res, err = session:save() + if err then + log(WARN, "unable to save session: " .. err) + end -- redirect to the URL that was accessed originally - log(DEBUG, "OIDC Authorization Code Flow completed -> Redirecting to original URL (" .. session.data.original_url .. ")") - ngx.redirect(session.data.original_url) - return nil, nil, session.data.original_url, session + local original_url = session:get("original_url") + log(DEBUG, "OIDC Authorization Code Flow completed -> Redirecting to original URL (" .. original_url .. ")") + ngx.redirect(original_url) + return nil, nil, original_url, session end -- token revocation (RFC 7009) @@ -1261,8 +1271,8 @@ function openidc.revoke_tokens(opts, session) return false end - local access_token = session.data.access_token - local refresh_token = session.data.refresh_token + local access_token = session:get("access_token") + local refresh_token = session:get("refresh_token") local access_token_revoke, refresh_token_revoke if refresh_token then @@ -1282,9 +1292,9 @@ local openidc_transparent_pixel = "\137\080\078\071\013\010\026\010\000\000\000\ -- handle logout local function openidc_logout(opts, session) - local session_token = session.data.enc_id_token - local access_token = session.data.access_token - local refresh_token = session.data.refresh_token + local session_token = session:get("enc_id_token") + local access_token = session:get("access_token") + local refresh_token = session:get("refresh_token") local err if opts.lifecycle and opts.lifecycle.on_logout then @@ -1353,21 +1363,26 @@ local function openidc_access_token(opts, session, try_to_renew) local err - if session.data.access_token == nil then + local access_token = session:get("access_token") + if access_token == nil then return nil, err end + local current_time = ngx.time() - if current_time < session.data.access_token_expiration then - return session.data.access_token, err + if current_time < session:get("access_token_expiration") then + return access_token, err end + if not try_to_renew then return nil, "token expired" end - if session.data.refresh_token == nil then + + local refresh_token = session:get("refresh_token") + if refresh_token == nil then return nil, "token expired and no refresh token available" end - log(DEBUG, "refreshing expired access_token: ", session.data.access_token, " with: ", session.data.refresh_token) + log(DEBUG, "refreshing expired access_token: ", access_token, " with: ", refresh_token) -- retrieve token endpoint URL from discovery endpoint if necessary err = ensure_config(opts) @@ -1378,7 +1393,7 @@ local function openidc_access_token(opts, session, try_to_renew) -- assemble the parameters to the token endpoint local body = { grant_type = "refresh_token", - refresh_token = session.data.refresh_token, + refresh_token = refresh_token, scope = opts.scope and opts.scope or "openid email profile" } @@ -1397,30 +1412,23 @@ local function openidc_access_token(opts, session, try_to_renew) end log(DEBUG, "access_token refreshed: ", json.access_token, " updated refresh_token: ", json.refresh_token) - session.data.access_token = json.access_token - session.data.access_token_expiration = current_time + openidc_access_token_expires_in(opts, json.expires_in) + session:set("access_token", json.access_token) + session:set("access_token_expiration", current_time + openidc_access_token_expires_in(opts, json.expires_in)) if json.refresh_token then - session.data.refresh_token = json.refresh_token + session:set("refresh_token", json.refresh_token) end if json.id_token and (store_in_session(opts, 'enc_id_token') or store_in_session(opts, 'id_token')) then log(DEBUG, "id_token refreshed: ", json.id_token) if store_in_session(opts, 'enc_id_token') then - session.data.enc_id_token = json.id_token + session:set("enc_id_token", json.id_token) end if store_in_session(opts, 'id_token') then - session.data.id_token = id_token + session:set("id_token", id_token) end end - -- save the session with the new access_token and optionally the new refresh_token and id_token using a new sessionid - local regenerated - regenerated, err = session:regenerate() - if err then - log(ERROR, "failed to regenerate session: " .. err) - return nil, err - end if opts.lifecycle and opts.lifecycle.on_regenerated then err = opts.lifecycle.on_regenerated(session) if err then @@ -1429,7 +1437,15 @@ local function openidc_access_token(opts, session, try_to_renew) end end - return session.data.access_token, err + -- save the session with the new access_token and optionally the new refresh_token and id_token + local res + res, err = session:save() + if err then + log(ERROR, "failed to save session: " .. err) + return nil, err + end + + return session:get("access_token"), err end local function openidc_get_path(uri) @@ -1445,7 +1461,7 @@ local function openidc_get_redirect_uri_path(opts) end local function is_session(o) - return o ~= nil and o.start and type(o.start) == "function" + return o ~= nil and o.save and type(o.save) == "function" end -- main routine for OpenID Connect user authentication @@ -1469,6 +1485,8 @@ function openidc.authenticate(opts, target_url, unauth_action, session_or_opts) end end + local session_present = next(session:get_data()) ~= nil + target_url = target_url or ngx.var.request_uri local access_token @@ -1478,7 +1496,7 @@ function openidc.authenticate(opts, target_url, unauth_action, session_or_opts) if path == openidc_get_redirect_uri_path(opts) then log(DEBUG, "Redirect URI path (" .. path .. ") is currently navigated -> Processing authorization response coming from OP") - if not session.present then + if not session_present then err = "request to the redirect_uri path but there's no session state found" log(ERROR, err) return nil, err, target_url, session @@ -1493,7 +1511,7 @@ function openidc.authenticate(opts, target_url, unauth_action, session_or_opts) err = ensure_config(opts) if err then - return nil, err, session.data.original_url, session + return nil, err, session:get("original_url"), session end openidc_logout(opts, session) @@ -1502,7 +1520,9 @@ function openidc.authenticate(opts, target_url, unauth_action, session_or_opts) local token_expired = false local try_to_renew = opts.renew_access_token_on_expiry == nil or opts.renew_access_token_on_expiry - if session.present and session.data.authenticated + local authenticated = session:get("authenticated") + + if session_present and authenticated and store_in_session(opts, 'access_token') then -- refresh access_token if necessary @@ -1516,10 +1536,12 @@ function openidc.authenticate(opts, target_url, unauth_action, session_or_opts) end end + local id_token = session:get("id_token") + log(DEBUG, - "session.present=", session.present, - ", session.data.id_token=", session.data.id_token ~= nil, - ", session.data.authenticated=", session.data.authenticated, + "session_present=", session_present, + ", session.data.id_token=", id_token ~= nil, + ", session.data.authenticated=", authenticated, ", opts.force_reauthorize=", opts.force_reauthorize, ", opts.renew_access_token_on_expiry=", opts.renew_access_token_on_expiry, ", try_to_renew=", try_to_renew, @@ -1527,13 +1549,13 @@ function openidc.authenticate(opts, target_url, unauth_action, session_or_opts) -- if we are not authenticated then redirect to the OP for authentication -- the presence of the id_token is check for backwards compatibility - if not session.present - or not (session.data.id_token or session.data.authenticated) + if not session_present + or not (id_token or authenticated) or opts.force_reauthorize or (try_to_renew and token_expired) then if unauth_action == "pass" then if token_expired then - session.data.authenticated = false + session:set("authenticated", false) return nil, 'token refresh failed', target_url, session end return nil, err, target_url, session @@ -1544,7 +1566,7 @@ function openidc.authenticate(opts, target_url, unauth_action, session_or_opts) err = ensure_config(opts) if err then - return nil, err, session.data.original_url, session + return nil, err, session:get("original_url"), session end log(DEBUG, "Authentication is required - Redirecting to OP Authorization endpoint") @@ -1554,10 +1576,11 @@ function openidc.authenticate(opts, target_url, unauth_action, session_or_opts) -- silently reauthenticate if necessary (mainly used for session refresh/getting updated id_token data) if opts.refresh_session_interval ~= nil then - if session.data.last_authenticated == nil or (session.data.last_authenticated + opts.refresh_session_interval) < ngx.time() then + local last_authenticated = session:get("last_authenticated") + if last_authenticated == nil or (last_authenticated + opts.refresh_session_interval) < ngx.time() then err = ensure_config(opts) if err then - return nil, err, session.data.original_url, session + return nil, err, session:get("original_url"), session end log(DEBUG, "Silent authentication is required - Redirecting to OP Authorization endpoint") @@ -1568,15 +1591,15 @@ function openidc.authenticate(opts, target_url, unauth_action, session_or_opts) if store_in_session(opts, 'id_token') then -- log id_token contents - log(DEBUG, "id_token=", cjson.encode(session.data.id_token)) + log(DEBUG, "id_token=", cjson.encode(id_token)) end -- return the id_token to the caller Lua script for access control purposes return { - id_token = session.data.id_token, + id_token = id_token, access_token = access_token, - user = session.data.user + user = session:get("user") }, err, target_url, diff --git a/lua-resty-openidc-1.7.6-3.rockspec b/lua-resty-openidc-1.7.6-3.rockspec index 1114c621..ad4819d5 100644 --- a/lua-resty-openidc-1.7.6-3.rockspec +++ b/lua-resty-openidc-1.7.6-3.rockspec @@ -24,7 +24,7 @@ description = { dependencies = { "lua >= 5.1", "lua-resty-http >= 0.08", - "lua-resty-session >= 2.8, <= 3.10", + "lua-resty-session >= 4.0.3", "lua-resty-jwt >= 0.2.0" } build = { diff --git a/tests/Dockerfile b/tests/Dockerfile index cc3345d1..a287dc19 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -1,7 +1,7 @@ FROM openresty/openresty:focal # install dependencies -RUN ["luarocks", "install", "lua-resty-session", "3.10"] +RUN ["luarocks", "install", "lua-resty-session", "4.0.3"] RUN ["luarocks", "install", "lua-resty-http"] RUN ["luarocks", "install", "lua-resty-jwt"] diff --git a/tests/spec/logout_spec.lua b/tests/spec/logout_spec.lua index 7a0fd7d3..cc2cf2d0 100644 --- a/tests/spec/logout_spec.lua +++ b/tests/spec/logout_spec.lua @@ -18,7 +18,7 @@ describe("when the configured logout uri is invoked with a non-image request", f end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) @@ -45,7 +45,7 @@ describe("when the configured logout uri is invoked with a png request", functio end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) @@ -76,7 +76,7 @@ describe("when logout is invoked and a callback with hint has been configured", end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) @@ -107,7 +107,7 @@ describe("when logout is invoked and a callback with hint has been configured - end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) @@ -141,7 +141,7 @@ describe("when logout is invoked and a callback with hint has been configured bu end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) @@ -171,7 +171,7 @@ describe("when logout is invoked and a callback without hint has been configured end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) @@ -200,7 +200,7 @@ describe("when logout is invoked and discovery contains end_session_endpoint and end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) @@ -232,7 +232,7 @@ describe("when logout is invoked and discovery contains end_session_endpoint and end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) @@ -257,7 +257,7 @@ describe("when logout is invoked and discovery contains ping_end_session_endpoin end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) @@ -294,7 +294,7 @@ describe("when logout is invoked and a callback with hint and a post_logout_uri end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) @@ -330,7 +330,7 @@ describe("when logout is invoked and discovery contains end_session_endpoint and end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) @@ -361,7 +361,7 @@ describe("when logout is invoked and discovery contains ping_end_session_endpoin end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) @@ -390,7 +390,7 @@ describe("when revoke_tokens_on_logout is enabled and a valid revocation endpoin it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) it("authorization credentials have not been passed on as post parameters to the revocation endpoint", function() @@ -436,7 +436,7 @@ describe("when revoke_tokens_on_logout is enabled and a valid revocation endpoin it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) it("authorization header has not been passed on to the revocation endpoint", function() @@ -480,7 +480,7 @@ describe("when revoke_tokens_on_logout is enabled and an invalid revocation endp it("the session cookie still has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) it("error messages concerning unseccussful revocation have been logged", function() @@ -512,7 +512,7 @@ describe("when revoke_tokens_on_logout is enabled but no revocation endpoint is it("the session cookie still has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) it("debug messages concerning unseccussful revocation have been logged", function() @@ -544,7 +544,7 @@ describe("when revoke_tokens_on_logout is not defined and a revocation_endpoint it("the session cookie still has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) it("no messages concerning revocation have been logged", function() From baf2643b70afbbad66cdba4adb74362e6171237f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Old=C5=99ich=20Jedli=C4=8Dka?= Date: Thu, 28 Dec 2023 23:37:54 +0100 Subject: [PATCH 70/77] Allow changing of query parameters in on_created call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Oldřich Jedlička --- README.md | 4 +++- lib/resty/openidc.lua | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0179b40e..cc066bb3 100644 --- a/README.md +++ b/README.md @@ -206,7 +206,9 @@ h2JHukolz9xf6qN61QMLSd83+kwoBr2drp6xg3eGDLIkQCQLrkY= -- } -- -- where `handle_created`, `handle_authenticated`, `handle_regenerated` and `handle_logout` are callables - -- accepting a single argument `session` + -- accepting argument `session`. `handle_created` accepts also second argument `params` which is a table + -- containing the query parameters of the authorization request used to redirect the user to the OpenID + -- Connect provider endpoint. -- -- -- `on_created` hook is invoked *after* a session has been created in -- `openidc_authorize` immediately prior to saving the session diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 4a283b5e..28031e33 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -358,7 +358,7 @@ local function openidc_authorize(opts, session, target_url, prompt) session:set("last_authenticated", ngx.time()) if opts.lifecycle and opts.lifecycle.on_created then - err = opts.lifecycle.on_created(session) + err = opts.lifecycle.on_created(session, params) if err then log(WARN, "failed in `on_created` handler: " .. err) return err From 20aa0333d7c15fea60c616fb94548a08c9e99f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Old=C5=99ich=20Jedli=C4=8Dka?= Date: Thu, 28 Dec 2023 23:29:38 +0100 Subject: [PATCH 71/77] Fix logout with no session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug in lua-resty-session does not permit calling session:destroy() on freshly started session with unset "audience" feature, so check for empty session before trying to destroy it. Signed-off-by: Oldřich Jedlička --- lib/resty/openidc.lua | 18 ++++++++++++------ tests/spec/logout_spec.lua | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 28031e33..84272e07 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -91,6 +91,14 @@ local function store_in_session(opts, feature) return opts.session_contents[feature] end +local function is_session(o) + return o ~= nil and o.save and type(o.save) == "function" +end + +local function is_session_present(session) + return session ~= nil and next(session:get_data()) ~= nil +end + -- set value in server-wide cache if available local function openidc_cache_set(type, key, value, exp) local dict = ngx.shared[type] @@ -1305,7 +1313,9 @@ local function openidc_logout(opts, session) end end - session:destroy() + if is_session_present(session) then + session:destroy() + end if opts.revoke_tokens_on_logout then log(DEBUG, "revoke_tokens_on_logout is enabled. " .. @@ -1460,10 +1470,6 @@ local function openidc_get_redirect_uri_path(opts) return opts.redirect_uri and openidc_get_path(opts.redirect_uri) or opts.redirect_uri_path end -local function is_session(o) - return o ~= nil and o.save and type(o.save) == "function" -end - -- main routine for OpenID Connect user authentication function openidc.authenticate(opts, target_url, unauth_action, session_or_opts) @@ -1485,7 +1491,7 @@ function openidc.authenticate(opts, target_url, unauth_action, session_or_opts) end end - local session_present = next(session:get_data()) ~= nil + local session_present = is_session_present(session) target_url = target_url or ngx.var.request_uri diff --git a/tests/spec/logout_spec.lua b/tests/spec/logout_spec.lua index cc2cf2d0..cabc335f 100644 --- a/tests/spec/logout_spec.lua +++ b/tests/spec/logout_spec.lua @@ -552,3 +552,20 @@ describe("when revoke_tokens_on_logout is not defined and a revocation_endpoint assert.is_not.error_log_contains("revoke") end) end) + +describe("when the configured logout uri is invoked with no active session", function() + test_support.start_server() + teardown(test_support.stop_server) + local _, status, headers = http.request({ + url = "http://127.0.0.1/default/logout", + redirect = false + }) + it("the response contains a default HTML-page", function() + assert.are.equals(200, status) + assert.are.equals("text/html", headers["content-type"]) + -- TODO should there be a Cache-Control header? + end) + it("the session cookie has been revoked", function() + assert.is.Nil(headers["set-cookie"]) + end) +end) From 742236fc233458af27ff6e0bcf2e047ab089d789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Old=C5=99ich=20Jedli=C4=8Dka?= Date: Thu, 28 Dec 2023 16:06:02 +0100 Subject: [PATCH 72/77] Fix usage of RFC 5737 address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address range 192.0.2.0/24 is reserved for documentation according to RFC 5737. The recommendation is to reject routing of this address range on routers, but as this is not mandatory, it might happen that the address is really routed. The tests on Docker on Windows fail because of this it, the fail reason is different to the expected one. Fix this by configuring Nginx to listen on 127.0.0.1:80 (and not 0.0.0.0:80) and connecting to 127.1.2.3 instead of 192.0.2.1. Signed-off-by: Oldřich Jedlička --- tests/spec/access_token_access_spec.lua | 4 ++-- tests/spec/bearer_token_verification_spec.lua | 4 ++-- tests/spec/introspection_spec.lua | 4 ++-- tests/spec/redirect_to_op_spec.lua | 4 ++-- tests/spec/test_support.lua | 2 +- tests/spec/token_request_spec.lua | 4 ++-- tests/spec/userinfo_spec.lua | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/spec/access_token_access_spec.lua b/tests/spec/access_token_access_spec.lua index 87128e31..bd2347b4 100644 --- a/tests/spec/access_token_access_spec.lua +++ b/tests/spec/access_token_access_spec.lua @@ -202,7 +202,7 @@ describe("when token endpoint is not reachable", function() access_token_opts = { timeout = 40000, discovery = { - token_endpoint = "http://192.0.2.1/" + token_endpoint = "http://127.1.2.3/" } }, token_response_expires_in = 0 @@ -219,7 +219,7 @@ describe("when token endpoint is not reachable", function() assert.are.equals(401, status) end) it("an error has been logged", function() - assert.error_log_contains("access_token error: accessing token endpoint.*%(http://192.0.2.1/%) failed") + assert.error_log_contains("access_token error: accessing token endpoint.*%(http://127.1.2.3/%) failed") end) end) diff --git a/tests/spec/bearer_token_verification_spec.lua b/tests/spec/bearer_token_verification_spec.lua index 7834acaf..6d1c67d3 100644 --- a/tests/spec/bearer_token_verification_spec.lua +++ b/tests/spec/bearer_token_verification_spec.lua @@ -464,7 +464,7 @@ describe("when jwks endpoint is not reachable", function() verify_opts = { timeout = 40000, discovery = { - jwks_uri = "http://192.0.2.1/" + jwks_uri = "http://127.1.2.3/" } }, }) @@ -478,7 +478,7 @@ describe("when jwks endpoint is not reachable", function() assert.are.equals(401, status) end) it("an error has been logged", function() - assert.error_log_contains("Invalid token: accessing jwks url.*%(http://192.0.2.1/%) failed") + assert.error_log_contains("Invalid token: accessing jwks url.*%(http://127.1.2.3/%) failed") end) end) diff --git a/tests/spec/introspection_spec.lua b/tests/spec/introspection_spec.lua index 3b5437b7..a22a3b89 100644 --- a/tests/spec/introspection_spec.lua +++ b/tests/spec/introspection_spec.lua @@ -396,7 +396,7 @@ describe("when introspection endpoint is not reachable", function() test_support.start_server({ introspection_opts = { timeout = 40000, - introspection_endpoint = "http://192.0.2.1/" + introspection_endpoint = "http://127.1.2.3/" }, }) teardown(test_support.stop_server) @@ -409,7 +409,7 @@ describe("when introspection endpoint is not reachable", function() assert.are.equals(401, status) end) it("an error has been logged", function() - assert.error_log_contains("Introspection error:.*accessing introspection endpoint %(http://192.0.2.1/%) failed") + assert.error_log_contains("Introspection error:.*accessing introspection endpoint %(http://127.1.2.3/%) failed") end) end) diff --git a/tests/spec/redirect_to_op_spec.lua b/tests/spec/redirect_to_op_spec.lua index efadd723..05257826 100644 --- a/tests/spec/redirect_to_op_spec.lua +++ b/tests/spec/redirect_to_op_spec.lua @@ -136,7 +136,7 @@ describe("when discovery endpoint is not reachable", function() test_support.start_server({ oidc_opts = { timeout = 40000, - discovery = "http://192.0.2.1/" + discovery = "http://127.1.2.3/" }, }) teardown(test_support.stop_server) @@ -148,7 +148,7 @@ describe("when discovery endpoint is not reachable", function() assert.are.equals(401, status) end) it("an error has been logged", function() - assert.error_log_contains("authenticate failed: accessing discovery url.*%(http://192.0.2.1/%) failed") + assert.error_log_contains("authenticate failed: accessing discovery url.*%(http://127.1.2.3/%) failed") end) end) diff --git a/tests/spec/test_support.lua b/tests/spec/test_support.lua index cca73e24..688a3a91 100644 --- a/tests/spec/test_support.lua +++ b/tests/spec/test_support.lua @@ -160,7 +160,7 @@ http { server { log_subrequest on; - listen 80; + listen 127.0.0.1:80; #listen 443 ssl; #ssl_certificate certificate-chain.crt; #ssl_certificate_key private.key; diff --git a/tests/spec/token_request_spec.lua b/tests/spec/token_request_spec.lua index a7dfd50e..08b49fbc 100644 --- a/tests/spec/token_request_spec.lua +++ b/tests/spec/token_request_spec.lua @@ -153,7 +153,7 @@ describe("if token endpoint is not reachable", function() oidc_opts = { timeout = 40000, discovery = { - token_endpoint = "http://192.0.2.1/" + token_endpoint = "http://127.1.2.3/" } }, }) @@ -163,7 +163,7 @@ describe("if token endpoint is not reachable", function() assert.are.equals(401, status) end) it("an error has been logged", function() - assert.error_log_contains("authenticate failed:.*accessing token endpoint %(http://192.0.2.1/%) failed") + assert.error_log_contains("authenticate failed:.*accessing token endpoint %(http://127.1.2.3/%) failed") end) end) diff --git a/tests/spec/userinfo_spec.lua b/tests/spec/userinfo_spec.lua index 856a2378..78f79c06 100644 --- a/tests/spec/userinfo_spec.lua +++ b/tests/spec/userinfo_spec.lua @@ -75,7 +75,7 @@ describe("when userinfo endpoint is not reachable", function() oidc_opts = { timeout = 40000, discovery = { - userinfo_endpoint = "http://192.0.2.1/" + userinfo_endpoint = "http://127.1.2.3/" } }, }) @@ -85,7 +85,7 @@ describe("when userinfo endpoint is not reachable", function() assert.are.equals(302, status) end) it("an error has been logged", function() - assert.error_log_contains(".*error calling userinfo endpoint: accessing %(http://192.0.2.1/%) failed") + assert.error_log_contains(".*error calling userinfo endpoint: accessing %(http://127.1.2.3/%) failed") end) end) From d0dc1fd6953715cf5774ca637a4467792785d935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Old=C5=99ich=20Jedli=C4=8Dka?= Date: Sat, 30 Dec 2023 00:09:36 +0100 Subject: [PATCH 73/77] Fix Base64Url decoding of JWT in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mime module expects padded Base64 value, so add missing padding. Signed-off-by: Oldřich Jedlička --- tests/spec/token_request_spec.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/spec/token_request_spec.lua b/tests/spec/token_request_spec.lua index 08b49fbc..c1839aec 100644 --- a/tests/spec/token_request_spec.lua +++ b/tests/spec/token_request_spec.lua @@ -267,7 +267,8 @@ local function extract_jwt_from_error_log() local enc_hdr, enc_payload, enc_sign = string.match(encoded_jwt, '^(.+)%.(.+)%.(.*)$') local base64_url_decode = function(s) local mime = require "mime" - return mime.unb64(s:gsub('-','+'):gsub('_','/')) + local padding = (4 - #s % 4) % 4 + return mime.unb64(s:gsub('-','+'):gsub('_','/') .. string.rep("=", padding)) end local dkjson = require "dkjson" return { From 9875456c76eb034079210f79937a10c6b97aa4fd Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Mon, 9 Sep 2024 12:11:50 +0200 Subject: [PATCH 74/77] fix missing entry about userinfo response as JWT in the ChangeLog Signed-off-by: Hans Zandbelt --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog b/ChangeLog index 1daa7276..2924d703 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,9 @@ - don't return a zero-pixel image in logout for Firefox 128 and later see #521 +03/11/2024 +- handle the userinfo response as JWT; closes ##345; thanks @NatePlumm + 03/10/2023 - when looking for a bearer token an exception occured if the Authorization header didn't contain any space character; From cebfa359d960da21968de093bab0a2a527e55bf2 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Mon, 9 Sep 2024 12:46:11 +0200 Subject: [PATCH 75/77] merge support for lua-resty-session 4.x; see #489 closes #464 #480 #503; thanks @oldium @balajiv113 Signed-off-by: Hans Zandbelt --- AUTHORS | 5 +++++ ChangeLog | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/AUTHORS b/AUTHORS index 2c39bbc6..1ab9e9de 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,6 +2,7 @@ The primary authors of lua-resty-openidc are: Hans Zandbelt Stefan Bodewig + Oldřich Jedlička Thanks to the following people for contributing to lua-resty-openidc by reporting bugs, providing fixes, suggesting useful features or other: @@ -37,4 +38,8 @@ reporting bugs, providing fixes, suggesting useful features or other: Thorsten Fleischmann Tilmann Hars Junlong Li + Nate + Balaji Vijayakumar + + diff --git a/ChangeLog b/ChangeLog index 2924d703..b43a2a7d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +09/09/2024 +- merge support for lua-resty-session 4.x; see #489; closes #464 #480 #503; thanks @oldium @balajiv113 +- add @oldium to the primary AUTHORS + 08/25/2024 - don't return a zero-pixel image in logout for Firefox 128 and later see #521 From 7548b258277a7208ba2c285796f7b1fef0c9759c Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Mon, 9 Sep 2024 12:56:40 +0200 Subject: [PATCH 76/77] update logout_spec.lua to match lua-resty-openidc 4.x set-cookie header Signed-off-by: Hans Zandbelt --- tests/spec/logout_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/spec/logout_spec.lua b/tests/spec/logout_spec.lua index b911cd01..6c7eaad3 100644 --- a/tests/spec/logout_spec.lua +++ b/tests/spec/logout_spec.lua @@ -38,7 +38,7 @@ describe("when the configured logout uri is invoked with Firefox 128's default A end) it("the session cookie has been revoked", function() assert.truthy(string.match(headers["set-cookie"], - "session=; Expires=Thu, 01 Jan 1970 00:00:01 GMT.*")) + "session=; Path=/; SameSite=Lax; HttpOnly; Expires=Thu, 01 Jan 1970 00:00:01 GMT; .*")) end) end) From 4702769443c2c39be3014bd23242e1abec02dee1 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Fri, 13 Sep 2024 17:39:40 +0200 Subject: [PATCH 77/77] release 1.8.0; fix cross-tenant requests; closes #526 Signed-off-by: Hans Zandbelt --- .github/issue_template.md | 2 +- ChangeLog | 4 ++++ lib/resty/openidc.lua | 2 +- ...idc-1.7.6-3.rockspec => lua-resty-openidc-1.8.0-1.rockspec | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) rename lua-resty-openidc-1.7.6-3.rockspec => lua-resty-openidc-1.8.0-1.rockspec (97%) diff --git a/.github/issue_template.md b/.github/issue_template.md index b692773a..09a74299 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -7,7 +7,7 @@ A new issue about a bug should be verified with a minimized example. ###### Environment -- lua-resty-openidc version (e.g. 1.7.6) +- lua-resty-openidc version (e.g. 1.8.0) - OpenID Connect provider (e.g. Keycloak, Azure AD) ###### Expected behaviour diff --git a/ChangeLog b/ChangeLog index b43a2a7d..bfbd6c43 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +09/13/204 +- cross-tenant requests are fixed with lua-resty session 4.0.x; closes #526 +- release 1.8.0 + 09/09/2024 - merge support for lua-resty-session 4.x; see #489; closes #464 #480 #503; thanks @oldium @balajiv113 - add @oldium to the primary AUTHORS diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index 1c0eed1d..3ac7f20e 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -78,7 +78,7 @@ local supported_token_auth_methods = { } local openidc = { - _VERSION = "1.7.6" + _VERSION = "1.8.0" } openidc.__index = openidc diff --git a/lua-resty-openidc-1.7.6-3.rockspec b/lua-resty-openidc-1.8.0-1.rockspec similarity index 97% rename from lua-resty-openidc-1.7.6-3.rockspec rename to lua-resty-openidc-1.8.0-1.rockspec index ad4819d5..b6fc3c9b 100644 --- a/lua-resty-openidc-1.7.6-3.rockspec +++ b/lua-resty-openidc-1.8.0-1.rockspec @@ -1,8 +1,8 @@ package = "lua-resty-openidc" -version = "1.7.6-3" +version = "1.8.0-1" source = { url = "git+https://github.com/zmartzone/lua-resty-openidc", - tag = "v1.7.6", + tag = "v1.8.0", dir = "lua-resty-openidc" } description = {