diff --git a/ChangeLog b/ChangeLog index e442ec6..b3cae37 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,9 @@ endpoint if this has not been done before in order to detect the URI of the userinfo endpoint +03/27/2020 +- added a function that implements OpenID Connect Front Channel Logout + 02/06/2020 - ability to disable keepalive from lua-resty-http By disabling keepalive we disable the native connection pool, diff --git a/README.md b/README.md index 5a5e83c..a6674b8 100644 --- a/README.md +++ b/README.md @@ -311,6 +311,43 @@ local res, err, target, session = require("resty.openidc").authenticate(opts) session:close() ``` +## Front-Channel Logout + +The `front_channel_logout` function can be used as a content handler +to implement [OpenID Connect Front-Channel +Logout](https://openid.net/specs/openid-connect-frontchannel-1_0.html). + +```nginx + ... + location /fc-logout { + content_by_lua_block { + local opts = { + -- session_required = true, + -- If this parameter is not set or set to true, requests to + -- the front channel logout URI must include the iss and sid + -- parameters. + -- This also requires id_tokens to be stored inside of the session. + -- Default is true + + -- opts.iss = '...', + -- if session_required is false the iss parameter can be + -- compared to a hard-coded iss value. + -- Default is nil + + -- downstream_logout = { 'other-uri', 'yet-another-uri' } + -- can contain a table of URIs to be notified via new iframes + -- included inside of the response + -- Default is no downstream logout URIs at all + } + require("resty.openidc").front_channel_logout(opts) + } + } +``` + +The `front_channel_logout` accepts as an optional second argument a +table that is passed to `session.open` in order to read the existing +session. + ## 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. diff --git a/lib/resty/openidc.lua b/lib/resty/openidc.lua index ddfa804..85c493d 100644 --- a/lib/resty/openidc.lua +++ b/lib/resty/openidc.lua @@ -1741,4 +1741,62 @@ function openidc.set_logging(new_log, new_levels) WARN = new_levels.WARN and new_levels.WARN or ngx.WARN end +function openidc.front_channel_logout(opts, session_opts) + local session, is_existing = r_session.open(session_opts) + local logout_success = true + log(DEBUG, 'Front-Channel Logout invoked') + if not is_existing then + log(WARN, 'no session present, nothing to do') + logout_success = false + else + if not session.started then + session:start() + end + local args = ngx.req.get_uri_args() + if not (args.sid and args.iss) then + log(DEBUG, 'Front-Channel Logout invoked without sid or iss') + if opts.session_required == nil or opts.session_required then + log(ERROR, 'deny logout as the required session information is missing') + logout_success = false + end + else + local id_token = session.data.id_token + if (opts.session_required == nil or opts.session_required) and not id_token then + log(ERROR, 'as id_token is not stored in session and session is required') + logout_success = false + end + if id_token and id_token.sid and id_token.sid ~= args.sid then + log(ERROR, 'Front-Channel Logout sid argument is different from sid stored in id_token') + logout_success = false + end + if id_token and id_token.iss and id_token.iss ~= args.iss then + log(ERROR, 'Front-Channel Logout iss argument is different from iss stored in id_token') + logout_success = false + end + if not id_token and opts and opts.iss and opts.iss ~= args.iss then + log(ERROR, 'Front-Channel Logout iss argument is different from iss stored in opts') + logout_success = false + end + end + end + ngx.header['Cache-Control'] = 'no-cache, no-store' + ngx.header['Pragma'] = 'no-cache' + ngx.header.content_type = 'text/html' + if logout_success then + log(DEBUG, 'Performing Front-Channel Logout') + session:destroy() + end + ngx.print('
') + if logout_success and opts.downstream_logout then + local downstream = type(opts.downstream_logout) == 'table' and opts.downstream_logout + or { opts.downstream_logout } + local _, d + for _, d in pairs(downstream) do + log(DEBUG, 'also notify ', d) + ngx.print('