From 6223239e9484dfeda35ccdafc93dbd787c9b0798 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 28 Nov 2023 18:04:29 +0100 Subject: [PATCH] implement proper expiration/prolong mechanism for cookies --- README.md | 1 + access.lua | 31 +++++++++++++++++-------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index dcfbce9..35c05a0 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ Only the `portal_domain` SSOwat configuration parameters is required, but it is - `cookie_secret_file`: Where the secret used for signing and encrypting cookie is stored. It should only be readable by root. - `cookie_name`: The name of the cookie used for authentication. Its content is expected to be a JWT signed with the cookie secret and should contain a key `user` and `password` (which is needed for Basic HTTP Auth). Because JWT is only encoded and signed (not encrypted), the `password` is expected to be encrypted using the cookie secret. +- `session_folder`: A path to a folder where files exists for any valid valid session id. SSOwat will check for the last modification date to confirm that the session is not expired. - `domain_portal_urls`: Location of the portal to use for login and browsing apps, to redirect to when access to some route is denied - `redirected_urls`: Array of URLs and/or URIs to redirect and their redirect URI/URL (**example**: `{ "/": "example.org/subpath" }`). diff --git a/access.lua b/access.lua index 614313a..3e03e42 100644 --- a/access.lua +++ b/access.lua @@ -11,6 +11,7 @@ ngx.header["X-SSO-WAT"] = "You've just been SSOed" local jwt = require("vendor.luajwtjitsi.luajwtjitsi") local cipher = require('openssl.cipher') local rex = require("rex_pcre2") +local lfs = require("lfs") -- ########################################################################### -- 0. Misc helpers because Lua has no sugar ... @@ -33,18 +34,18 @@ function cached_jwt_verify(data, secret) decoded, err = jwt.verify(data, "HS256", cookie_secret) if not decoded then logger:error(err) - return nil, nil, err + return nil, nil, nil, err end -- As explained in set_basic_auth_header(), user and hashed password do not contain ':' - -- And cache cannot contain tables, so we use "user:password" format - cached = decoded["user"]..":"..decoded["pwd"] + -- And cache cannot contain tables, so we use "id:user:password" format + cached = decoded['id']..":"..decoded["user"]..":"..decoded["pwd"] cache:set(data, cached, 120) logger:debug("Result saved in cache") - return decoded["user"], decoded["pwd"], err + return decoded['id'], decoded["user"], decoded["pwd"], err else logger:debug("Result found in cache") - user, pwd = res:match("([^:]+):(.*)") - return user, pwd, nil + session_id, user, pwd = res:match("([^:]+):([^:]+):(.*)") + return session_id, user, pwd, nil end end @@ -109,18 +110,20 @@ function check_authentication() return false, nil, nil end - user, pwd, err = cached_jwt_verify(cookie, cookie_secret) - - -- FIXME : maybe also check that the cookie was delivered for the requested domain (or a parent?) - - -- FIXME : we might want also a way to identify expired/invalidated cookies, - -- e.g. a user that got deleted after being logged in, or a user that logged out ... + session_id, user, pwd, err = cached_jwt_verify(cookie, cookie_secret) if err ~= nil then return false, nil, nil - else - return true, user, pwd end + + local session_file = conf["session_folder"] .. '/' .. session_id + local session_file_attrs = lfs.attributes(session_file, {"modification"}) + if session_file_attrs == nil or math.abs(["modification"] - os.time()) > 3 * 24 * 3600 then + -- session expired + return false, nil, nil + end + + return true, user, pwd end -- ###########################################################################